| Index: third_party/sqlite/src/notify.c
|
| ===================================================================
|
| --- third_party/sqlite/src/notify.c (revision 56608)
|
| +++ third_party/sqlite/src/notify.c (working copy)
|
| @@ -1,333 +0,0 @@
|
| -/*
|
| -** 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.
|
| -**
|
| -** $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
|
| -
|
| -/*
|
| -** 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->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)(sqlite3DbMallocSize(db, 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
|
|
|