1: /*
2: ** 2009 March 3
3: **
4: ** The author disclaims copyright to this source code. In place of
5: ** a legal notice, here is a blessing:
6: **
7: ** May you do good and not evil.
8: ** May you find forgiveness for yourself and forgive others.
9: ** May you share freely, never taking more than you give.
10: **
11: *************************************************************************
12: **
13: ** This file contains the implementation of the sqlite3_unlock_notify()
14: ** API method and its associated functionality.
15: */
16: #include "sqliteInt.h"
17: #include "btreeInt.h"
18:
19: /* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */
20: #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
21:
22: /*
23: ** Public interfaces:
24: **
25: ** sqlite3ConnectionBlocked()
26: ** sqlite3ConnectionUnlocked()
27: ** sqlite3ConnectionClosed()
28: ** sqlite3_unlock_notify()
29: */
30:
31: #define assertMutexHeld() \
32: assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) )
33:
34: /*
35: ** Head of a linked list of all sqlite3 objects created by this process
36: ** for which either sqlite3.pBlockingConnection or sqlite3.pUnlockConnection
37: ** is not NULL. This variable may only accessed while the STATIC_MASTER
38: ** mutex is held.
39: */
40: static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0;
41:
42: #ifndef NDEBUG
43: /*
44: ** This function is a complex assert() that verifies the following
45: ** properties of the blocked connections list:
46: **
47: ** 1) Each entry in the list has a non-NULL value for either
48: ** pUnlockConnection or pBlockingConnection, or both.
49: **
50: ** 2) All entries in the list that share a common value for
51: ** xUnlockNotify are grouped together.
52: **
53: ** 3) If the argument db is not NULL, then none of the entries in the
54: ** blocked connections list have pUnlockConnection or pBlockingConnection
55: ** set to db. This is used when closing connection db.
56: */
57: static void checkListProperties(sqlite3 *db){
58: sqlite3 *p;
59: for(p=sqlite3BlockedList; p; p=p->pNextBlocked){
60: int seen = 0;
61: sqlite3 *p2;
62:
63: /* Verify property (1) */
64: assert( p->pUnlockConnection || p->pBlockingConnection );
65:
66: /* Verify property (2) */
67: for(p2=sqlite3BlockedList; p2!=p; p2=p2->pNextBlocked){
68: if( p2->xUnlockNotify==p->xUnlockNotify ) seen = 1;
69: assert( p2->xUnlockNotify==p->xUnlockNotify || !seen );
70: assert( db==0 || p->pUnlockConnection!=db );
71: assert( db==0 || p->pBlockingConnection!=db );
72: }
73: }
74: }
75: #else
76: # define checkListProperties(x)
77: #endif
78:
79: /*
80: ** Remove connection db from the blocked connections list. If connection
81: ** db is not currently a part of the list, this function is a no-op.
82: */
83: static void removeFromBlockedList(sqlite3 *db){
84: sqlite3 **pp;
85: assertMutexHeld();
86: for(pp=&sqlite3BlockedList; *pp; pp = &(*pp)->pNextBlocked){
87: if( *pp==db ){
88: *pp = (*pp)->pNextBlocked;
89: break;
90: }
91: }
92: }
93:
94: /*
95: ** Add connection db to the blocked connections list. It is assumed
96: ** that it is not already a part of the list.
97: */
98: static void addToBlockedList(sqlite3 *db){
99: sqlite3 **pp;
100: assertMutexHeld();
101: for(
102: pp=&sqlite3BlockedList;
103: *pp && (*pp)->xUnlockNotify!=db->xUnlockNotify;
104: pp=&(*pp)->pNextBlocked
105: );
106: db->pNextBlocked = *pp;
107: *pp = db;
108: }
109:
110: /*
111: ** Obtain the STATIC_MASTER mutex.
112: */
113: static void enterMutex(void){
114: sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
115: checkListProperties(0);
116: }
117:
118: /*
119: ** Release the STATIC_MASTER mutex.
120: */
121: static void leaveMutex(void){
122: assertMutexHeld();
123: checkListProperties(0);
124: sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
125: }
126:
127: /*
128: ** Register an unlock-notify callback.
129: **
130: ** This is called after connection "db" has attempted some operation
131: ** but has received an SQLITE_LOCKED error because another connection
132: ** (call it pOther) in the same process was busy using the same shared
133: ** cache. pOther is found by looking at db->pBlockingConnection.
134: **
135: ** If there is no blocking connection, the callback is invoked immediately,
136: ** before this routine returns.
137: **
138: ** If pOther is already blocked on db, then report SQLITE_LOCKED, to indicate
139: ** a deadlock.
140: **
141: ** Otherwise, make arrangements to invoke xNotify when pOther drops
142: ** its locks.
143: **
144: ** Each call to this routine overrides any prior callbacks registered
145: ** on the same "db". If xNotify==0 then any prior callbacks are immediately
146: ** cancelled.
147: */
148: int sqlite3_unlock_notify(
149: sqlite3 *db,
150: void (*xNotify)(void **, int),
151: void *pArg
152: ){
153: int rc = SQLITE_OK;
154:
155: sqlite3_mutex_enter(db->mutex);
156: enterMutex();
157:
158: if( xNotify==0 ){
159: removeFromBlockedList(db);
160: db->pBlockingConnection = 0;
161: db->pUnlockConnection = 0;
162: db->xUnlockNotify = 0;
163: db->pUnlockArg = 0;
164: }else if( 0==db->pBlockingConnection ){
165: /* The blocking transaction has been concluded. Or there never was a
166: ** blocking transaction. In either case, invoke the notify callback
167: ** immediately.
168: */
169: xNotify(&pArg, 1);
170: }else{
171: sqlite3 *p;
172:
173: for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection){}
174: if( p ){
175: rc = SQLITE_LOCKED; /* Deadlock detected. */
176: }else{
177: db->pUnlockConnection = db->pBlockingConnection;
178: db->xUnlockNotify = xNotify;
179: db->pUnlockArg = pArg;
180: removeFromBlockedList(db);
181: addToBlockedList(db);
182: }
183: }
184:
185: leaveMutex();
186: assert( !db->mallocFailed );
187: sqlite3Error(db, rc, (rc?"database is deadlocked":0));
188: sqlite3_mutex_leave(db->mutex);
189: return rc;
190: }
191:
192: /*
193: ** This function is called while stepping or preparing a statement
194: ** associated with connection db. The operation will return SQLITE_LOCKED
195: ** to the user because it requires a lock that will not be available
196: ** until connection pBlocker concludes its current transaction.
197: */
198: void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){
199: enterMutex();
200: if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){
201: addToBlockedList(db);
202: }
203: db->pBlockingConnection = pBlocker;
204: leaveMutex();
205: }
206:
207: /*
208: ** This function is called when
209: ** the transaction opened by database db has just finished. Locks held
210: ** by database connection db have been released.
211: **
212: ** This function loops through each entry in the blocked connections
213: ** list and does the following:
214: **
215: ** 1) If the sqlite3.pBlockingConnection member of a list entry is
216: ** set to db, then set pBlockingConnection=0.
217: **
218: ** 2) If the sqlite3.pUnlockConnection member of a list entry is
219: ** set to db, then invoke the configured unlock-notify callback and
220: ** set pUnlockConnection=0.
221: **
222: ** 3) If the two steps above mean that pBlockingConnection==0 and
223: ** pUnlockConnection==0, remove the entry from the blocked connections
224: ** list.
225: */
226: void sqlite3ConnectionUnlocked(sqlite3 *db){
227: void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */
228: int nArg = 0; /* Number of entries in aArg[] */
229: sqlite3 **pp; /* Iterator variable */
230: void **aArg; /* Arguments to the unlock callback */
231: void **aDyn = 0; /* Dynamically allocated space for aArg[] */
232: void *aStatic[16]; /* Starter space for aArg[]. No malloc required */
233:
234: aArg = aStatic;
235: enterMutex(); /* Enter STATIC_MASTER mutex */
236:
237: /* This loop runs once for each entry in the blocked-connections list. */
238: for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){
239: sqlite3 *p = *pp;
240:
241: /* Step 1. */
242: if( p->pBlockingConnection==db ){
243: p->pBlockingConnection = 0;
244: }
245:
246: /* Step 2. */
247: if( p->pUnlockConnection==db ){
248: assert( p->xUnlockNotify );
249: if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){
250: xUnlockNotify(aArg, nArg);
251: nArg = 0;
252: }
253:
254: sqlite3BeginBenignMalloc();
255: assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) );
256: assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn );
257: if( (!aDyn && nArg==(int)ArraySize(aStatic))
258: || (aDyn && nArg==(int)(sqlite3MallocSize(aDyn)/sizeof(void*)))
259: ){
260: /* The aArg[] array needs to grow. */
261: void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2);
262: if( pNew ){
263: memcpy(pNew, aArg, nArg*sizeof(void *));
264: sqlite3_free(aDyn);
265: aDyn = aArg = pNew;
266: }else{
267: /* This occurs when the array of context pointers that need to
268: ** be passed to the unlock-notify callback is larger than the
269: ** aStatic[] array allocated on the stack and the attempt to
270: ** allocate a larger array from the heap has failed.
271: **
272: ** This is a difficult situation to handle. Returning an error
273: ** code to the caller is insufficient, as even if an error code
274: ** is returned the transaction on connection db will still be
275: ** closed and the unlock-notify callbacks on blocked connections
276: ** will go unissued. This might cause the application to wait
277: ** indefinitely for an unlock-notify callback that will never
278: ** arrive.
279: **
280: ** Instead, invoke the unlock-notify callback with the context
281: ** array already accumulated. We can then clear the array and
282: ** begin accumulating any further context pointers without
283: ** requiring any dynamic allocation. This is sub-optimal because
284: ** it means that instead of one callback with a large array of
285: ** context pointers the application will receive two or more
286: ** callbacks with smaller arrays of context pointers, which will
287: ** reduce the applications ability to prioritize multiple
288: ** connections. But it is the best that can be done under the
289: ** circumstances.
290: */
291: xUnlockNotify(aArg, nArg);
292: nArg = 0;
293: }
294: }
295: sqlite3EndBenignMalloc();
296:
297: aArg[nArg++] = p->pUnlockArg;
298: xUnlockNotify = p->xUnlockNotify;
299: p->pUnlockConnection = 0;
300: p->xUnlockNotify = 0;
301: p->pUnlockArg = 0;
302: }
303:
304: /* Step 3. */
305: if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){
306: /* Remove connection p from the blocked connections list. */
307: *pp = p->pNextBlocked;
308: p->pNextBlocked = 0;
309: }else{
310: pp = &p->pNextBlocked;
311: }
312: }
313:
314: if( nArg!=0 ){
315: xUnlockNotify(aArg, nArg);
316: }
317: sqlite3_free(aDyn);
318: leaveMutex(); /* Leave STATIC_MASTER mutex */
319: }
320:
321: /*
322: ** This is called when the database connection passed as an argument is
323: ** being closed. The connection is removed from the blocked list.
324: */
325: void sqlite3ConnectionClosed(sqlite3 *db){
326: sqlite3ConnectionUnlocked(db);
327: enterMutex();
328: removeFromBlockedList(db);
329: checkListProperties(db);
330: leaveMutex();
331: }
332: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>