Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(396)

Side by Side Diff: third_party/sqlite/src/notify.c

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

Powered by Google App Engine
This is Rietveld 408576698