/ Check-in [9ccfcb76]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix the sqlite3_unlock_notify() interface so that when the callback is NULL it simply cancels any outstanding callbacks. (CVS 6467)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:9ccfcb760745df28b04e746355b1b6dec49a93de
User & Date: drh 2009-04-07 22:06:57
Context
2009-04-08
11:49
Add a comment to printf.c - no changes to code. (CVS 6468) check-in: ee5a4a0e user: drh tags: trunk
2009-04-07
22:06
Fix the sqlite3_unlock_notify() interface so that when the callback is NULL it simply cancels any outstanding callbacks. (CVS 6467) check-in: 9ccfcb76 user: drh tags: trunk
22:05
Remove two unused tests from the integrity_check pragma logic. (CVS 6466) check-in: 22999d31 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/notify.c.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
124
125
126
127
128
129
130


















131
132
133
134
135
136
137
138
139
140
141





142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
...
179
180
181
182
183
184
185

186
187
188
189
190
191
192
193
...
200
201
202
203
204
205
206



207
208
209
210
211
212
213
214
215
216
217
218
**    May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file contains the implementation of the sqlite3_unlock_notify()
** API method and its associated functionality.
**
** $Id: notify.c,v 1.3 2009/04/07 11:21:29 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "btreeInt.h"

/* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY

................................................................................
  assertMutexHeld();
  checkListProperties(0);
  sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
}

/*
** Register an unlock-notify callback.


















*/
int sqlite3_unlock_notify(
  sqlite3 *db,
  void (*xNotify)(void **, int),
  void *pArg
){
  int rc = SQLITE_OK;

  sqlite3_mutex_enter(db->mutex);
  enterMutex();






  if( 0==db->pBlockingConnection ){
    /* The blocking transaction has been concluded. Or there never was a 
    ** blocking transaction. In either case, invoke the notify callback
    ** immediately. 
    */
    xNotify(&pArg, 1);
  }else{
    sqlite3 *p;

    for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection);
    if( p ){
      rc = SQLITE_LOCKED;              /* Deadlock detected. */
    }else{
      db->pUnlockConnection = db->pBlockingConnection;
      db->xUnlockNotify = xNotify;
      db->pUnlockArg = pArg;
      removeFromBlockedList(db);
................................................................................
    addToBlockedList(db);
  }
  db->pBlockingConnection = pBlocker;
  leaveMutex();
}

/*

** The transaction opened by database db has just finished. Locks held 
** by database connection db have been released.
**
** This function loops through each entry in the blocked connections
** list and does the following:
**
**   1) If the sqlite3.pBlockingConnection member of a list entry is
**      set to db, then set pBlockingConnection=0.
................................................................................
**      pUnlockConnection==0, remove the entry from the blocked connections
**      list.
*/
void sqlite3ConnectionUnlocked(sqlite3 *db){
  void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */
  int nArg = 0;                            /* Number of entries in aArg[] */
  sqlite3 **pp;                            /* Iterator variable */




  void *aStatic[16];
  void **aArg = aStatic;
  void **aDyn = 0;

  enterMutex();         /* Enter STATIC_MASTER mutex */

  /* This loop runs once for each entry in the blocked-connections list. */
  for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){
    sqlite3 *p = *pp;

    /* Step 1. */







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











>
>
>
>
>
|








|







 







>
|







 







>
>
>

<
|
<
<







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
...
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
...
224
225
226
227
228
229
230
231
232
233
234

235


236
237
238
239
240
241
242
**    May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file contains the implementation of the sqlite3_unlock_notify()
** API method and its associated functionality.
**
** $Id: notify.c,v 1.4 2009/04/07 22:06:57 drh Exp $
*/
#include "sqliteInt.h"
#include "btreeInt.h"

/* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY

................................................................................
  assertMutexHeld();
  checkListProperties(0);
  sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
}

/*
** Register an unlock-notify callback.
**
** This is called after connection "db" has attempted some operation
** but has received an SQLITE_LOCKED error because another connection
** (call it pOther) in the same process was busy using the same shared
** cache.  pOther is found by looking at db->pBlockingConnection.
**
** If there is no blocking connection, the callback is invoked immediately,
** before this routine returns.
**
** If pOther is already blocked on db, then report SQLITE_LOCKED, to indicate
** a deadlock.
**
** Otherwise, make arrangements to invoke xNotify when pOther drops
** its locks.
**
** Each call to this routine overrides any prior callbacks registered
** on the same "db".  If xNotify==0 then any prior callbacks are immediately
** cancelled.
*/
int sqlite3_unlock_notify(
  sqlite3 *db,
  void (*xNotify)(void **, int),
  void *pArg
){
  int rc = SQLITE_OK;

  sqlite3_mutex_enter(db->mutex);
  enterMutex();

  if( xNotify==0 ){
    removeFromBlockedList(db);
    db->pUnlockConnection = 0;
    db->xUnlockNotify = 0;
    db->pUnlockArg = 0;
  }else if( 0==db->pBlockingConnection ){
    /* The blocking transaction has been concluded. Or there never was a 
    ** blocking transaction. In either case, invoke the notify callback
    ** immediately. 
    */
    xNotify(&pArg, 1);
  }else{
    sqlite3 *p;

    for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection){}
    if( p ){
      rc = SQLITE_LOCKED;              /* Deadlock detected. */
    }else{
      db->pUnlockConnection = db->pBlockingConnection;
      db->xUnlockNotify = xNotify;
      db->pUnlockArg = pArg;
      removeFromBlockedList(db);
................................................................................
    addToBlockedList(db);
  }
  db->pBlockingConnection = pBlocker;
  leaveMutex();
}

/*
** This function is called when
** the transaction opened by database db has just finished. Locks held 
** by database connection db have been released.
**
** This function loops through each entry in the blocked connections
** list and does the following:
**
**   1) If the sqlite3.pBlockingConnection member of a list entry is
**      set to db, then set pBlockingConnection=0.
................................................................................
**      pUnlockConnection==0, remove the entry from the blocked connections
**      list.
*/
void sqlite3ConnectionUnlocked(sqlite3 *db){
  void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */
  int nArg = 0;                            /* Number of entries in aArg[] */
  sqlite3 **pp;                            /* Iterator variable */
  void **aArg;               /* Arguments to the unlock callback */
  void **aDyn = 0;           /* Dynamically allocated space for aArg[] */
  void *aStatic[16];         /* Starter space for aArg[].  No malloc required */


  aArg = aStatic;


  enterMutex();         /* Enter STATIC_MASTER mutex */

  /* This loop runs once for each entry in the blocked-connections list. */
  for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){
    sqlite3 *p = *pp;

    /* Step 1. */

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
832
833
834
835
836
837
838







839
840
841
842
843
844
845
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.852 2009/04/07 14:14:22 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Include the configuration header output by 'configure' if we're using the
** autoconf-based build
................................................................................
  int nSavepoint;               /* Number of non-transaction savepoints */
  int nStatement;               /* Number of nested statement-transactions  */
  u8 isTransactionSavepoint;    /* True if the outermost savepoint is a TS */

#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
  /* The following variables are all protected by the STATIC_MASTER 
  ** mutex, not by sqlite3.mutex. They are used by code in notify.c. 







  */
  sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */
  sqlite3 *pUnlockConnection;           /* Connection to watch for unlock */
  void *pUnlockArg;                     /* Argument to xUnlockNotify */
  void (*xUnlockNotify)(void **, int);  /* Unlock notify callback */
  sqlite3 *pNextBlocked;        /* Next in list of all blocked connections */
#endif







|







 







>
>
>
>
>
>
>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.853 2009/04/07 22:06:57 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Include the configuration header output by 'configure' if we're using the
** autoconf-based build
................................................................................
  int nSavepoint;               /* Number of non-transaction savepoints */
  int nStatement;               /* Number of nested statement-transactions  */
  u8 isTransactionSavepoint;    /* True if the outermost savepoint is a TS */

#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
  /* The following variables are all protected by the STATIC_MASTER 
  ** mutex, not by sqlite3.mutex. They are used by code in notify.c. 
  **
  ** When X.pUnlockConnection==Y, that means that X is waiting for Y to
  ** unlock so that it can proceed.
  **
  ** When X.pBlockingConnection==Y, that means that something that X tried
  ** tried to do recently failed with an SQLITE_LOCKED error due to locks
  ** held by Y.
  */
  sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */
  sqlite3 *pUnlockConnection;           /* Connection to watch for unlock */
  void *pUnlockArg;                     /* Argument to xUnlockNotify */
  void (*xUnlockNotify)(void **, int);  /* Unlock notify callback */
  sqlite3 *pNextBlocked;        /* Next in list of all blocked connections */
#endif

Changes to test/notify1.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
50
51
52
53
54
55
56
































57
58
59
60
61
62
63
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the sqlite3_unlock_notify() API.
#
# $Id: notify1.test,v 1.2 2009/03/25 15:43:09 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable !unlock_notify||!shared_cache {
  finish_test
  return
................................................................................
  set zScript
} {}
do_test notify1-1.5 {
  execsql { COMMIT }
  eval $zScript
  execsql { SELECT * FROM t1 }
} {1 2 3 4}

































#-------------------------------------------------------------------------
# The following tests, notify1-2.*, test that deadlock is detected 
# correctly.
# 
do_test notify1-2.1 {
  execsql { 







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the sqlite3_unlock_notify() API.
#
# $Id: notify1.test,v 1.3 2009/04/07 22:06:57 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable !unlock_notify||!shared_cache {
  finish_test
  return
................................................................................
  set zScript
} {}
do_test notify1-1.5 {
  execsql { COMMIT }
  eval $zScript
  execsql { SELECT * FROM t1 }
} {1 2 3 4}

#-------------------------------------------------------------------------
# Verify that invoking the "unlock_notify" method with no arguments
# (which is the equivalent of invoking sqlite3_unlock_notify() with
# a NULL xNotify argument) cancels a pending notify callback.
#
do_test notify1-1.11 {
  execsql { DROP TABLE t1; CREATE TABLE t1(a, b) }
} {}
do_test notify1-1.12 {
  execsql {
    BEGIN;
    INSERT INTO t1 VALUES(1, 2);
  }
  catchsql { INSERT INTO t1 VALUES(3, 4) } db2
} {1 {database table is locked}}
do_test notify1-1.13 {
  set zScript ""
  db2 unlock_notify {
    set zScript "db2 eval { INSERT INTO t1 VALUES(3, 4) }"
  }
  execsql { SELECT * FROM t1 }
} {1 2}
do_test notify1-1.14 {
  set zScript
} {}
do_test notify1-1.15 {
  db2 unlock_notify
  execsql { COMMIT }
  eval $zScript
  execsql { SELECT * FROM t1 }
} {1 2}

#-------------------------------------------------------------------------
# The following tests, notify1-2.*, test that deadlock is detected 
# correctly.
# 
do_test notify1-2.1 {
  execsql {