void SocketService::attach(SocketPort *port) { enterMutex(); #ifdef USE_POLL port->ufd = 0; #endif if(last) last->next = port; port->prev = last; last = port; FD_SET(port->so, &connect); if(port->so >= hiwater) hiwater = port->so + 1; port->service = this; ++count; if(!first) first = port; // start thread if necessary if (count == 1) { if (!isRunning()) { leaveMutex(); start(); return; } } leaveMutex(); update(); }
size_t Buffer::peek(void *buf) { size_t rc; enterMutex(); if(!_used) { leaveMutex(); return 0; } rc = onPeek(buf); leaveMutex(); return rc; }
/* ** 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(); }
static jint receiveBytes(SharedMemoryConnection *connection, void *bytes, jint length) { Stream *stream = &connection->incoming; SharedStream *shared = stream->shared; jint fragmentStart; jint fragmentLength; jint index = 0; jint maxLength; CHECK_ERROR(enterMutex(stream, connection->shutdown)); while (index < length) { CHECK_ERROR(waitForData(connection, stream)); SHMEM_ASSERT(!EMPTY(stream)); fragmentStart = shared->readOffset; if (fragmentStart < shared->writeOffset) { maxLength = shared->writeOffset - fragmentStart; } else { maxLength = SHARED_BUFFER_SIZE - fragmentStart; } fragmentLength = MIN(maxLength, length - index); memcpy((jbyte *)bytes + index, shared->buffer + fragmentStart, fragmentLength); shared->readOffset = ADD_OFFSET(fragmentStart, fragmentLength); index += fragmentLength; shared->isFull = JNI_FALSE; STREAM_INVARIANT(stream); CHECK_ERROR(signalSpace(stream)); } CHECK_ERROR(leaveMutex(stream)); return SYS_OK; }
void ThreadQueue::run(void) { data_t *prev; started = true; for(;;) { Semaphore::wait(); if(!started) sleep((timeout_t)~0); startQueue(); while(first) { runQueue(first->data); enterMutex(); prev = first; first = first->next; delete[] prev; if(!first) last = NULL; leaveMutex(); if(first) Semaphore::wait(); // demark semaphore } stopQueue(); } }
/* ** 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(); }
void MapTable::addFree(MapObject *obj) { obj->detach(); enterMutex(); obj->nextObject = map[range]; map[range] = obj; leaveMutex(); }
void MapTable::cleanup(void) { enterMutex(); if(map) delete[] map; map = NULL; leaveMutex(); }
size_t Buffer::wait(void *buf, timeout_t timeout) { size_t rc = 0; enterMutex(); while(!_used) { if(!Conditional::wait(timeout, true)) { leaveMutex(); return Buffer::timeout; } } rc = (ssize_t)onWait(buf); --_used; Conditional::signal(false); leaveMutex(); return rc; }
void *SharedMemPager::alloc(size_t size) { void *mem; enterMutex(); mem = MemPager::alloc(size); leaveMutex(); return mem; }
void *MapTable::getFree(void) { enterMutex(); MapObject *obj = map[range]; if(obj) map[range] = obj->nextObject; leaveMutex(); return obj; }
void MapTable::addObject(MapObject &obj) { unsigned idx = getIndex(obj.idObject); if(obj.table == this || !map) return; obj.detach(); enterMutex(); obj.nextObject = map[idx]; map[idx] = &obj; leaveMutex(); }
/* enter the stream's mutex and (optionally) check for a closed stream */ static jint enterMutex(Stream *stream, sys_event_t event) { jint ret = sysIPMutexEnter(stream->mutex, event); if (ret != SYS_OK) { return ret; } if (IS_STATE_CLOSED(stream->state)) { (void)leaveMutex(stream); return SYS_ERR; } return SYS_OK; }
static jint closeStream(Stream *stream, jboolean linger) { /* * Lock stream during close - ignore shutdown event as we are * closing down and shutdown should be signalled. */ CHECK_ERROR(enterMutex(stream, NULL)); /* mark the stream as closed */ stream->state = STATE_CLOSED; /* wake up waitForData() if it is in sysEventWait() */ sysEventSignal(stream->hasData); sysEventClose(stream->hasData); /* wake up waitForSpace() if it is in sysEventWait() */ sysEventSignal(stream->hasSpace); sysEventClose(stream->hasSpace); /* * If linger requested then give the stream a few seconds to * drain before closing it. */ if (linger) { int attempts = 10; while (!EMPTY(stream) && attempts>0) { CHECK_ERROR(leaveMutex(stream)); sysSleep(200); CHECK_ERROR(enterMutex(stream, NULL)); attempts--; } } CHECK_ERROR(leaveMutex(stream)); sysIPMutexClose(stream->mutex); return SYS_OK; }
/* * Enter/exit with stream mutex held. * On error, does not hold the stream mutex. */ static jint waitForData(SharedMemoryConnection *connection, Stream *stream) { jint error = SYS_OK; /* Assumes mutex is held on call */ while ((error == SYS_OK) && EMPTY(stream)) { CHECK_ERROR(leaveMutex(stream)); error = sysEventWait(connection->otherProcess, stream->hasData); if (error == SYS_OK) { CHECK_ERROR(enterMutex(stream, connection->shutdown)); } } return error; }
size_t Buffer::post(void *buf, timeout_t timeout) { size_t rc; if(!timeout) timeout = INFINITE; if(Thread::waitThread(sem_tail, timeout) != WAIT_OBJECT_0) return Buffer::timeout; enterMutex(); rc = onPost(buf); ++_used; leaveMutex(); ::ReleaseSemaphore(sem_head, 1, (LPLONG)NULL); return rc; }
void *MapTable::getObject(const char *id) { if(!map) return NULL; enterMutex(); MapObject *obj = map[getIndex(id)]; leaveMutex(); while(obj) { if(!stricmp(obj->idObject, id)) break; obj = obj->nextObject; } return (void *)obj; }
/* * Enter/exit with stream mutex held. * On error, does not hold the stream mutex. */ static jint waitForSpace(SharedMemoryConnection *connection, Stream *stream) { jint error = SYS_OK; /* Assumes mutex is held on call */ while ((error == SYS_OK) && FULL(stream)) { CHECK_ERROR(leaveMutex(stream)); error = sysEventWait(connection->otherProcess, stream->hasSpace, 0); if (error == SYS_OK) { CHECK_ERROR(enterMutex(stream, connection->shutdown)); } else { setLastError(error); } } return error; }
/* enter the stream's mutex and (optionally) check for a closed stream */ static jint enterMutex(Stream *stream, sys_event_t event) { jint ret = sysIPMutexEnter(stream->mutex, event); if (ret != SYS_OK) { if (IS_STATE_CLOSED(stream->state)) { setLastErrorMsg("stream closed"); } return ret; } if (IS_STATE_CLOSED(stream->state)) { setLastErrorMsg("stream closed"); (void)leaveMutex(stream); return SYS_ERR; } return SYS_OK; }
/* ** 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 ); sqlite3ErrorWithMsg(db, rc, (rc?"database is deadlocked":0)); sqlite3_mutex_leave(db->mutex); return rc; }
void ThreadQueue::post(const void *dp, unsigned len) { data_t *data = (data_t *)new char[sizeof(data_t) + len]; memcpy(data->data, dp, len); data->len = len; data->next = NULL; enterMutex(); if(!first) first = data; if(last) last->next = data; last = data; if(!started) { start(); started = true; } leaveMutex(); Semaphore::post(); }
static jint closeStream(Stream *stream) { /* * Lock stream during close - ignore shutdown event as we are * closing down and shutdown should be signalled. */ CHECK_ERROR(enterMutex(stream, NULL)); /* mark the stream as closed */ stream->state = STATE_CLOSED; /* wake up waitForData() if it is in sysEventWait() */ sysEventSignal(stream->hasData); sysEventClose(stream->hasData); /* wake up waitForSpace() if it is in sysEventWait() */ sysEventSignal(stream->hasSpace); sysEventClose(stream->hasSpace); CHECK_ERROR(leaveMutex(stream)); sysIPMutexClose(stream->mutex); return SYS_OK; }
static jint sendBytes(SharedMemoryConnection *connection, const void *bytes, jint length) { Stream *stream = &connection->outgoing; SharedStream *shared = stream->shared; jint fragmentStart; jint fragmentLength; jint index = 0; jint maxLength; clearLastError(); CHECK_ERROR(enterMutex(stream, connection->shutdown)); while (index < length) { CHECK_ERROR(waitForSpace(connection, stream)); SHMEM_ASSERT(!FULL(stream)); fragmentStart = shared->writeOffset; if (fragmentStart < shared->readOffset) { maxLength = shared->readOffset - fragmentStart; } else { maxLength = SHARED_BUFFER_SIZE - fragmentStart; } fragmentLength = MIN(maxLength, length - index); memcpy(shared->buffer + fragmentStart, (jbyte *)bytes + index, fragmentLength); shared->writeOffset = ADD_OFFSET(fragmentStart, fragmentLength); index += fragmentLength; shared->isFull = (shared->readOffset == shared->writeOffset); STREAM_INVARIANT(stream); CHECK_ERROR(signalData(stream)); } CHECK_ERROR(leaveMutex(stream)); return SYS_OK; }
jint shmemBase_receiveByte(SharedMemoryConnection *connection, jbyte *data) { Stream *stream = &connection->incoming; SharedStream *shared = stream->shared; int offset; CHECK_ERROR(enterMutex(stream, connection->shutdown)); CHECK_ERROR(waitForData(connection, stream)); SHMEM_ASSERT(!EMPTY(stream)); offset = shared->readOffset; *data = shared->buffer[offset]; shared->readOffset = ADD_OFFSET(offset, 1); shared->isFull = JNI_FALSE; STREAM_INVARIANT(stream); CHECK_ERROR(leaveMutex(stream)); CHECK_ERROR(signalSpace(stream)); return SYS_OK; }
jint shmemBase_sendByte(SharedMemoryConnection *connection, jbyte data) { Stream *stream = &connection->outgoing; SharedStream *shared = stream->shared; int offset; CHECK_ERROR(enterMutex(stream, connection->shutdown)); CHECK_ERROR(waitForSpace(connection, stream)); SHMEM_ASSERT(!FULL(stream)); offset = shared->writeOffset; shared->buffer[offset] = data; shared->writeOffset = ADD_OFFSET(offset, 1); shared->isFull = (shared->readOffset == shared->writeOffset); STREAM_INVARIANT(stream); CHECK_ERROR(leaveMutex(stream)); CHECK_ERROR(signalData(stream)); return SYS_OK; }
void SocketService::detach(SocketPort *port) { enterMutex(); #if !defined(USE_POLL) FD_CLR(port->so, &connect); #endif if(port->prev) { port->prev->next = port->next; } else { first = port->next; } if(port->next) { port->next->prev = port->prev; } else { last = port->prev; } port->service = NULL; --count; leaveMutex(); update(); }
void SocketService::run(void) { timeout_t timer, expires; SocketPort *port; unsigned char buf; #ifndef WIN32 #ifdef USE_POLL Poller mfd; pollfd * p_ufd; int lastcount = 0; // initialize ufd in all attached ports : // probably don't need this but it can't hurt. enterMutex(); port = first; while(port) { port->ufd = 0; port = port->next; } leaveMutex(); #else struct timeval timeout, *tvp; fd_set inp, out, err; FD_ZERO(&inp); FD_ZERO(&out); FD_ZERO(&err); int so; #endif #else // WIN32 int numHandle = 0; HANDLE hv[MAXIMUM_WAIT_OBJECTS]; #endif #ifdef WIN32 // FIXME: needed ? ResetEvent(sync->GetSync()); #endif setCancel(cancelDeferred); for(;;) { timer = TIMEOUT_INF; #ifndef WIN32 while(1 == ::read(iosync[0], (char *)&buf, 1)) { #else for(;;) { int f = sync->getFlag(); if (f < 0) break; buf = f; #endif if(buf) { onUpdate(buf); continue; } setCancel(cancelImmediate); sleep(TIMEOUT_INF); exit(); } #ifndef WIN32 #ifdef USE_POLL bool reallocate = false; MUTEX_START onEvent(); port = first; while(port) { onCallback(port); if ( ( p_ufd = port->ufd ) ) { if ( ( POLLHUP | POLLNVAL ) & p_ufd->revents ) { // Avoid infinite loop from disconnected sockets port->detect_disconnect = false; p_ufd->events &= ~POLLHUP; SocketPort* p = port; port = port->next; detach(p); reallocate = true; p->disconnect(); continue; } if ( ( POLLIN | POLLPRI ) & p_ufd->revents ) port->pending(); if ( POLLOUT & p_ufd->revents ) port->output(); } else { reallocate = true; } retry: expires = port->getTimer(); if(expires > 0) if(expires < timer) timer = expires; if(!expires) { port->endTimer(); port->expired(); goto retry; } port = port->next; } // // reallocate things if we saw a ServerPort without // ufd set ! if ( reallocate || ( ( count + 1 ) != lastcount ) ) { lastcount = count + 1; p_ufd = mfd.getList( count + 1 ); // Set up iosync polling p_ufd->fd = iosync[0]; p_ufd->events = POLLIN | POLLHUP; p_ufd ++; port = first; while(port) { p_ufd->fd = port->so; p_ufd->events = ( port->detect_disconnect ? POLLHUP : 0 ) | ( port->detect_output ? POLLOUT : 0 ) | ( port->detect_pending ? POLLIN : 0 ) ; port->ufd = p_ufd; p_ufd ++; port = port->next; } } MUTEX_END poll( mfd.getList(), lastcount, timer ); #else MUTEX_START onEvent(); port = first; while(port) { onCallback(port); so = port->so; if(FD_ISSET(so, &err)) { port->detect_disconnect = false; SocketPort* p = port; port = port->next; p->disconnect(); continue; } if(FD_ISSET(so, &inp)) port->pending(); if(FD_ISSET(so, &out)) port->output(); retry: expires = port->getTimer(); if(expires > 0) if(expires < timer) timer = expires; // if we expire, get new scheduling now if(!expires) { port->endTimer(); port->expired(); goto retry; } port = port->next; } FD_ZERO(&inp); FD_ZERO(&out); FD_ZERO(&err); FD_SET(iosync[0],&inp); port = first; while(port) { so = port->so; if(port->detect_pending) FD_SET(so, &inp); if(port->detect_output) FD_SET(so, &out); if(port->detect_disconnect) FD_SET(so, &err); port = port->next; } MUTEX_END if(timer == TIMEOUT_INF) tvp = NULL; else { tvp = &timeout; timeout.tv_sec = timer / 1000; timeout.tv_usec = (timer % 1000) * 1000; } select(hiwater, &inp, &out, &err, tvp); #endif #else // WIN32 MUTEX_START onEvent(); hv[0] = sync->GetSync(); numHandle = 1; port = first; while(port) { onCallback(port); long events = 0; if(port->detect_pending) events |= FD_READ; if(port->detect_output) events |= FD_WRITE; if(port->detect_disconnect) events |= FD_CLOSE; // !!! ignore some socket on overflow !!! if (events && numHandle < MAXIMUM_WAIT_OBJECTS) { WSAEventSelect(port->so,port->event,events); hv[numHandle++] = port->event; } retry: expires = port->getTimer(); if(expires > 0) if(expires < timer) timer = expires; // if we expire, get new scheduling now if(!expires) { port->endTimer(); port->expired(); goto retry; } port = port->next; } MUTEX_END // FIXME: handle thread cancellation correctly DWORD res = WaitForMultipleObjects(numHandle,hv,FALSE,timer); switch (res) { case WAIT_OBJECT_0: break; case WAIT_TIMEOUT: break; default: // FIXME: handle failures (detach SocketPort) if (res >= WAIT_OBJECT_0+1 && res <= WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS) { int curr = res - (WAIT_OBJECT_0); WSANETWORKEVENTS events; // search port MUTEX_START port = first; while(port) { if (port->event == hv[curr]) break; port = port->next; } MUTEX_END // if port not found ignore if (!port || port->event != hv[curr]) break; WSAEnumNetworkEvents(port->so,port->event,&events); if(events.lNetworkEvents & FD_CLOSE) { port->detect_disconnect = false; port->disconnect(); continue; } if(events.lNetworkEvents & FD_READ) port->pending(); if(events.lNetworkEvents & FD_WRITE) port->output(); } } #endif } }
void SocketService::run(void) { timeout_t timer, expires; SocketPort *port; unsigned char buf; #ifdef USE_POLL Poller mfd; pollfd * p_ufd; int lastcount = 0; // initialize ufd in all attached ports : // probably don't need this but it can't hurt. enterMutex(); port = first; while(port) { port->ufd = 0; port = port->next; } leaveMutex(); #else struct timeval timeout, *tvp; fd_set inp, out, err; FD_ZERO(&inp); FD_ZERO(&out); FD_ZERO(&err); int so; #endif setCancel(cancelDeferred); for(;;) { timer = TIMEOUT_INF; while(1 == ::read(iosync[0], (char *)&buf, 1)) { if(buf) { onUpdate(buf); continue; } setCancel(cancelImmediate); sleep(TIMEOUT_INF); exit(); } #ifdef USE_POLL bool reallocate = false; MUTEX_START onEvent(); port = first; while(port) { onCallback(port); if ( ( p_ufd = port->ufd ) ) { if ( ( POLLHUP | POLLNVAL ) & p_ufd->revents ) { // Avoid infinite loop from disconnected sockets port->detect_disconnect = false; p_ufd->events &= ~POLLHUP; SocketPort* p = port; port = port->next; detach(p); reallocate = true; p->disconnect(); continue; } if ( ( POLLIN | POLLPRI ) & p_ufd->revents ) port->pending(); if ( POLLOUT & p_ufd->revents ) port->output(); } else { reallocate = true; } retry: expires = port->getTimer(); if(expires > 0) if(expires < timer) timer = expires; if(!expires) { port->endTimer(); port->expired(); goto retry; } port = port->next; } // // reallocate things if we saw a ServerPort without // ufd set ! if ( reallocate || ( ( count + 1 ) != lastcount ) ) { lastcount = count + 1; p_ufd = mfd.getList( count + 1 ); // Set up iosync polling p_ufd->fd = iosync[0]; p_ufd->events = POLLIN | POLLHUP; p_ufd ++; port = first; while(port) { p_ufd->fd = port->so; p_ufd->events = ( port->detect_disconnect ? POLLHUP : 0 ) | ( port->detect_output ? POLLOUT : 0 ) | ( port->detect_pending ? POLLIN : 0 ) ; port->ufd = p_ufd; p_ufd ++; port = port->next; } } MUTEX_END poll( mfd.getList(), lastcount, timer ); #else MUTEX_START onEvent(); port = first; while(port) { onCallback(port); so = port->so; if(FD_ISSET(so, &err)) { port->detect_disconnect = false; SocketPort* p = port; port = port->next; p->disconnect(); continue; } if(FD_ISSET(so, &inp)) port->pending(); if(FD_ISSET(so, &out)) port->output(); retry: expires = port->getTimer(); if(expires > 0) if(expires < timer) timer = expires; // if we expire, get new scheduling now if(!expires) { port->endTimer(); port->expired(); goto retry; } port = port->next; } FD_ZERO(&inp); FD_ZERO(&out); FD_ZERO(&err); FD_SET(iosync[0],&inp); port = first; while(port) { so = port->so; if(port->detect_pending) FD_SET(so, &inp); if(port->detect_output) FD_SET(so, &out); if(port->detect_disconnect) FD_SET(so, &err); port = port->next; } MUTEX_END if(timer == TIMEOUT_INF) tvp = NULL; else { tvp = &timeout; timeout.tv_sec = timer / 1000; timeout.tv_usec = (timer % 1000) * 1000; } select(hiwater, &inp, &out, &err, tvp); #endif } }
void SharedMemPager::purge(void) { enterMutex(); MemPager::purge(); 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 */ }