Index: third_party/sqlite/sqlite-src-3070603/src/notify.c |
diff --git a/third_party/sqlite/sqlite-src-3070603/src/notify.c b/third_party/sqlite/sqlite-src-3070603/src/notify.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fcab5bfaf01908d3de93fa88b5eae9e96ca3c8e8 |
--- /dev/null |
+++ b/third_party/sqlite/sqlite-src-3070603/src/notify.c |
@@ -0,0 +1,332 @@ |
+/* |
+** 2009 March 3 |
+** |
+** The author disclaims copyright to this source code. In place of |
+** a legal notice, here is a blessing: |
+** |
+** 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. |
+** |
+************************************************************************* |
+** |
+** This file contains the implementation of the sqlite3_unlock_notify() |
+** API method and its associated functionality. |
+*/ |
+#include "sqliteInt.h" |
+#include "btreeInt.h" |
+ |
+/* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */ |
+#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY |
+ |
+/* |
+** Public interfaces: |
+** |
+** sqlite3ConnectionBlocked() |
+** sqlite3ConnectionUnlocked() |
+** sqlite3ConnectionClosed() |
+** sqlite3_unlock_notify() |
+*/ |
+ |
+#define assertMutexHeld() \ |
+ assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) ) |
+ |
+/* |
+** Head of a linked list of all sqlite3 objects created by this process |
+** for which either sqlite3.pBlockingConnection or sqlite3.pUnlockConnection |
+** is not NULL. This variable may only accessed while the STATIC_MASTER |
+** mutex is held. |
+*/ |
+static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0; |
+ |
+#ifndef NDEBUG |
+/* |
+** This function is a complex assert() that verifies the following |
+** properties of the blocked connections list: |
+** |
+** 1) Each entry in the list has a non-NULL value for either |
+** pUnlockConnection or pBlockingConnection, or both. |
+** |
+** 2) All entries in the list that share a common value for |
+** xUnlockNotify are grouped together. |
+** |
+** 3) If the argument db is not NULL, then none of the entries in the |
+** blocked connections list have pUnlockConnection or pBlockingConnection |
+** set to db. This is used when closing connection db. |
+*/ |
+static void checkListProperties(sqlite3 *db){ |
+ sqlite3 *p; |
+ for(p=sqlite3BlockedList; p; p=p->pNextBlocked){ |
+ int seen = 0; |
+ sqlite3 *p2; |
+ |
+ /* Verify property (1) */ |
+ assert( p->pUnlockConnection || p->pBlockingConnection ); |
+ |
+ /* Verify property (2) */ |
+ for(p2=sqlite3BlockedList; p2!=p; p2=p2->pNextBlocked){ |
+ if( p2->xUnlockNotify==p->xUnlockNotify ) seen = 1; |
+ assert( p2->xUnlockNotify==p->xUnlockNotify || !seen ); |
+ assert( db==0 || p->pUnlockConnection!=db ); |
+ assert( db==0 || p->pBlockingConnection!=db ); |
+ } |
+ } |
+} |
+#else |
+# define checkListProperties(x) |
+#endif |
+ |
+/* |
+** Remove connection db from the blocked connections list. If connection |
+** db is not currently a part of the list, this function is a no-op. |
+*/ |
+static void removeFromBlockedList(sqlite3 *db){ |
+ sqlite3 **pp; |
+ assertMutexHeld(); |
+ for(pp=&sqlite3BlockedList; *pp; pp = &(*pp)->pNextBlocked){ |
+ if( *pp==db ){ |
+ *pp = (*pp)->pNextBlocked; |
+ break; |
+ } |
+ } |
+} |
+ |
+/* |
+** Add connection db to the blocked connections list. It is assumed |
+** that it is not already a part of the list. |
+*/ |
+static void addToBlockedList(sqlite3 *db){ |
+ sqlite3 **pp; |
+ assertMutexHeld(); |
+ for( |
+ pp=&sqlite3BlockedList; |
+ *pp && (*pp)->xUnlockNotify!=db->xUnlockNotify; |
+ pp=&(*pp)->pNextBlocked |
+ ); |
+ db->pNextBlocked = *pp; |
+ *pp = db; |
+} |
+ |
+/* |
+** Obtain the STATIC_MASTER mutex. |
+*/ |
+static void enterMutex(void){ |
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); |
+ checkListProperties(0); |
+} |
+ |
+/* |
+** Release the STATIC_MASTER mutex. |
+*/ |
+static void leaveMutex(void){ |
+ 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->pBlockingConnection = 0; |
+ 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); |
+ } |
+ } |
+ |
+ leaveMutex(); |
+ assert( !db->mallocFailed ); |
+ sqlite3Error(db, rc, (rc?"database is deadlocked":0)); |
+ sqlite3_mutex_leave(db->mutex); |
+ return rc; |
+} |
+ |
+/* |
+** This function is called while stepping or preparing a statement |
+** associated with connection db. The operation will return SQLITE_LOCKED |
+** to the user because it requires a lock that will not be available |
+** until connection pBlocker concludes its current transaction. |
+*/ |
+void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){ |
+ enterMutex(); |
+ if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){ |
+ 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. |
+** |
+** 2) If the sqlite3.pUnlockConnection member of a list entry is |
+** set to db, then invoke the configured unlock-notify callback and |
+** set pUnlockConnection=0. |
+** |
+** 3) If the two steps above mean that pBlockingConnection==0 and |
+** 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. */ |
+ if( p->pBlockingConnection==db ){ |
+ p->pBlockingConnection = 0; |
+ } |
+ |
+ /* Step 2. */ |
+ if( p->pUnlockConnection==db ){ |
+ assert( p->xUnlockNotify ); |
+ if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){ |
+ xUnlockNotify(aArg, nArg); |
+ nArg = 0; |
+ } |
+ |
+ sqlite3BeginBenignMalloc(); |
+ assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) ); |
+ assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn ); |
+ if( (!aDyn && nArg==(int)ArraySize(aStatic)) |
+ || (aDyn && nArg==(int)(sqlite3MallocSize(aDyn)/sizeof(void*))) |
+ ){ |
+ /* The aArg[] array needs to grow. */ |
+ void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2); |
+ if( pNew ){ |
+ memcpy(pNew, aArg, nArg*sizeof(void *)); |
+ sqlite3_free(aDyn); |
+ aDyn = aArg = pNew; |
+ }else{ |
+ /* This occurs when the array of context pointers that need to |
+ ** be passed to the unlock-notify callback is larger than the |
+ ** aStatic[] array allocated on the stack and the attempt to |
+ ** allocate a larger array from the heap has failed. |
+ ** |
+ ** This is a difficult situation to handle. Returning an error |
+ ** code to the caller is insufficient, as even if an error code |
+ ** is returned the transaction on connection db will still be |
+ ** closed and the unlock-notify callbacks on blocked connections |
+ ** will go unissued. This might cause the application to wait |
+ ** indefinitely for an unlock-notify callback that will never |
+ ** arrive. |
+ ** |
+ ** Instead, invoke the unlock-notify callback with the context |
+ ** array already accumulated. We can then clear the array and |
+ ** begin accumulating any further context pointers without |
+ ** requiring any dynamic allocation. This is sub-optimal because |
+ ** it means that instead of one callback with a large array of |
+ ** context pointers the application will receive two or more |
+ ** callbacks with smaller arrays of context pointers, which will |
+ ** reduce the applications ability to prioritize multiple |
+ ** connections. But it is the best that can be done under the |
+ ** circumstances. |
+ */ |
+ xUnlockNotify(aArg, nArg); |
+ nArg = 0; |
+ } |
+ } |
+ sqlite3EndBenignMalloc(); |
+ |
+ aArg[nArg++] = p->pUnlockArg; |
+ xUnlockNotify = p->xUnlockNotify; |
+ p->pUnlockConnection = 0; |
+ p->xUnlockNotify = 0; |
+ p->pUnlockArg = 0; |
+ } |
+ |
+ /* Step 3. */ |
+ if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){ |
+ /* Remove connection p from the blocked connections list. */ |
+ *pp = p->pNextBlocked; |
+ p->pNextBlocked = 0; |
+ }else{ |
+ pp = &p->pNextBlocked; |
+ } |
+ } |
+ |
+ if( nArg!=0 ){ |
+ xUnlockNotify(aArg, nArg); |
+ } |
+ sqlite3_free(aDyn); |
+ leaveMutex(); /* Leave STATIC_MASTER mutex */ |
+} |
+ |
+/* |
+** This is called when the database connection passed as an argument is |
+** being closed. The connection is removed from the blocked list. |
+*/ |
+void sqlite3ConnectionClosed(sqlite3 *db){ |
+ sqlite3ConnectionUnlocked(db); |
+ enterMutex(); |
+ removeFromBlockedList(db); |
+ checkListProperties(db); |
+ leaveMutex(); |
+} |
+#endif |