/** * Deallocates the system resources used by the lock. */ void RWLOCK_Destroy(RWLock * lock) { QEntry * e; ASSERT(!lock->locks); ASSERT(!lock->entriesInUse); ASSERT(!lock->entriesActive); ASSERT(!lock->shareWaiters.size); ASSERT(!lock->exclusiveWaiters.size); MUTEX_Destroy(&lock->mutex); EVENT_Destroy(&lock->shareEvent); EVENT_Destroy(&lock->exclusiveEvent); /* free dynamically allocated entries */ if (lock->moreEntries) { MEM_Free(lock->moreEntries); lock->moreEntries = NULL; } /* free waiter cache */ while ((e = QUEUE_RemoveHead(&lock->waiterCache)) != NULL) { RWLockWaiter * w = QCAST(e,RWLockWaiter,entry); MEM_Free(w); } /* free dynamically allocated events */ while (lock->eventsInCache > 0) { lock->eventsInCache--; ASSERT(lock->eventCache[lock->eventsInCache]); EVENT_Delete(lock->eventCache[lock->eventsInCache]); lock->eventCache[lock->eventsInCache] = NULL; } lock->numEntries = 0; lock->locks = -1; /* to cause ASSERT if destroyed twice */ }
/** * Moves all the entries from the source queue to the tail of the destination * queue. DOES NOT remove the entries that are already in the destination * queue. Returns number of moved entries. */ int QUEUE_Move(Queue * dest, Queue * src) { int n = 0; if (src && src != dest && QUEUE_Size(src) > 0) { QEntry * e; while ((e = QUEUE_RemoveHead(src)) != NULL) { QUEUE_InsertTail(dest, e); n++; } ASSERT(QUEUE_Size(src) == 0); } return n; }
/** * Actually submits the XRPC calls from the work thread. */ STATIC void GWENG_AsyncXRpc(WorkItem * w, void* arg) { QEntry* e; MidpSession* session = arg; MUTEX_Lock(&session->xrpcMutex); while ((e = QUEUE_RemoveHead(&session->xrpcQueue)) != NULL) { AsyncXRpcEntry* a = QCAST(e,AsyncXRpcEntry,entry); MUTEX_Unlock(&session->xrpcMutex); XRPC_Notify(XRPC_GetClient(session->key.xrpcSession), ECMTGW_SEI_PROTOCOL, a->method, XRPC_ContainerToElement(a->params)); XRPC_FreeContainer(a->params); MEM_Free(a); MUTEX_Lock(&session->xrpcMutex); } MUTEX_Unlock(&session->xrpcMutex); }
/** * Cancels all pending work items in the work queue. */ void WKQ_Cancel(WorkQueue * q) { if (MUTEX_Lock(&q->mutex)) { QEntry * e; while ((e = QUEUE_RemoveHead(&q->submit)) != NULL) { WorkItem * w = QCAST(e,WorkItem,submitQ); w->flags |= WKI_CANCELED; if (w->flags & WKI_DETACHED) { ASSERT(!w->waiters); QUEUE_RemoveEntry(&w->itemsQ); WKQ_ReleaseWorkItem(&WKQ, w); } else { WKI_Signal(w); } } MUTEX_Unlock(&q->mutex); } }
/** * Deallocates the MIDP session context. */ STATIC void GWENG_MidpFree(EcmtGateway* gw, MidpSession* midp) { if (midp) { QEntry* e; VERIFY(HASH_Remove(&gw->midpSessionMap, &midp->key)); HASH_Remove(&gw->ecmtSessionMap, (HashKey)midp->sid); HASH_Destroy(&midp->connMap); WKI_Cancel(midp->xrpcWorkItem); WKI_Detach(midp->xrpcWorkItem); WKQ_Delete(midp->xrpcWorkThread); MUTEX_Destroy(&midp->xrpcMutex); while ((e = QUEUE_RemoveHead(&midp->xrpcQueue)) != NULL) { AsyncXRpcEntry* a = QCAST(e,AsyncXRpcEntry,entry); XRPC_FreeContainer(a->params); MEM_Free(a); } MEM_Free(midp); } }
void WKQ_Shutdown() { if ((--WKQ.initcount) == 0) { while (WKQ.waitpool) { Waiter * next = WKQ.waitpool->next; EVENT_Destroy(&WKQ.waitpool->event); MEM_Free(WKQ.waitpool); WKQ.waitpool = next; WKQ.nwait--; } ASSERT(WKQ.nwait == 0); while (!QUEUE_IsEmpty(&WKQ.itempool)) { QEntry * e = QUEUE_RemoveHead(&WKQ.itempool); WorkItem * w = QCAST(e,WorkItem,itemsQ); MEM_Free(w); } MUTEX_Destroy(&WKQ.mutex); THREAD_Shutdown(); } }
/** * Returns work item from the pool, allocates a new one if needed. */ STATIC WorkItem * WKQ_GetWorkItem(WorkQueueModule * mod, WorkQueue * q, WorkProc cb, WorkProc2 cb2, void * p1, void * p2) { WorkItem * w = NULL; ASSERT(mod->initcount > 0); /* can't use QUEUE_IsEmpty without synchronization */ if (!mod->itempool.size) { MUTEX_Lock(&mod->mutex); if (!QUEUE_IsEmpty(&mod->itempool)) { w = QCAST(QUEUE_RemoveHead(&mod->itempool),WorkItem,itemsQ); w->flags = 0; } MUTEX_Unlock(&mod->mutex); } if (!w) { w = MEM_New(WorkItem); if (w) { memset(w, 0, sizeof(*w)); } } if (w) { if (MUTEX_Lock(&q->mutex)) { w->proc = cb; w->proc2 = cb2; w->param = p1; w->param2 = p2; QUEUE_InsertTail(&q->items,&w->itemsQ); MUTEX_Unlock(&q->mutex); return w; } MEM_Free(w); } return NULL; }
/** * The worker thread */ STATIC void WKQ_Thread(void * par) { WorkQueue * q = (WorkQueue *)par; TRACE("WKQ: starting\n"); /* start the loop */ MUTEX_Lock(&q->mutex); q->lastActivity = TIME_Now(); while ((q->flags & WKQ_ACTIVE) || !QUEUE_IsEmpty(&q->submit)) { QEntry * e; while ((e = QUEUE_RemoveHead(&q->submit)) != NULL) { WorkItem * w = QCAST(e,WorkItem,submitQ); ASSERT(!(w->flags & (WKI_DONE|WKI_CANCELED))); /* * NULL callback may be used by dummy work items whose purpose * is to wait until all pending work items have been processed */ if (w->proc) { /* update flags */ w->flags |= WKI_CALL; /* invoke the handler */ MUTEX_Unlock(&q->mutex); w->proc(w, w->param); MUTEX_Lock(&q->mutex); q->lastActivity = TIME_Now(); if (w->flags & WKI_DETACHED) { /* put the work item to the pool or deallocate it */ ASSERT(!w->waiters); QUEUE_RemoveEntry(&w->itemsQ); WKQ_ReleaseWorkItem(&WKQ, w); } else { /* * update flags. Note that we released the mutex when * were invoking the callback. Therefore, this work * item could be re-submitted to the queue. Or it could * be re-submitted and then canceled. In such cases we * don't need to set the WKI_DONE flag. */ w->flags &= ~WKI_CALL; if (!(w->flags & WKI_CANCELED) && !w->submitQ.queue) { w->flags |= WKI_DONE; } /* signal the events associated with the work item */ WKI_Signal(w); } } else { /* it's a dummy work item. Just release the waiters */ WKI_Signal(w); } } /* wait for a signal */ if (q->flags & WKQ_ACTIVE) { EVENT_Reset(&q->event); if (q->idleProc) { /* we have an idle timeout */ IdleProc idle = q->idleProc; void * param = q->idleParam; Time now = TIME_Now(); Time deadline = q->lastActivity + q->idleTimeout; if (deadline > now) { MUTEX_Unlock(&q->mutex); switch (EVENT_TimeWait(&q->event,(long)(deadline-now))) { case WAIT_STATE_OK: /* don't invoke idle callback */ MUTEX_Lock(&q->mutex); break; case WAIT_STATE_TIMEOUT: /* invoke idle callback */ MUTEX_Lock(&q->mutex); now = TIME_Now(); deadline = q->lastActivity + q->idleTimeout; if (deadline <= now) { MUTEX_Unlock(&q->mutex); q->lastActivity = now; idle(q, param); MUTEX_Lock(&q->mutex); } break; default: case WAIT_STATE_ERROR: /* terminate the thread on error */ MUTEX_Lock(&q->mutex); q->flags &= ~WKQ_ACTIVE; break; } } else { q->lastActivity = now; MUTEX_Unlock(&q->mutex); idle(q, param); MUTEX_Lock(&q->mutex); } } else { /* wait forever */ MUTEX_Unlock(&q->mutex); EVENT_Wait(&q->event); MUTEX_Lock(&q->mutex); } } } /* cleanup */ MUTEX_Unlock(&q->mutex); TRACE("WKQ: done\n"); if (q->flags & WKQ_KILLME) { TRACE1("WKQ: killing WorkQueue %p\n",q); WKQ_Free(q); } }