/** * Puts work item to the global pool or deallocates it. Also deallocates * the events associated with the work item. NOTE: this code is designed * to be efficient, not compact */ STATIC void WKQ_ReleaseWorkItem(WorkQueueModule * module, WorkItem * w) { Bool locked = False; ASSERT(module->initcount > 0); ASSERT(!w->submitQ.queue); ASSERT(!w->itemsQ.queue); /* deallocate waiters */ while (w->waiters) { Waiter * waiter = w->waiters; Waiter * next = waiter->next; if (module->nwait < module->maxwait) { if (locked) { WKQ_WaiterToPool(module, waiter); waiter = NULL; } else { locked = MUTEX_Lock(&module->mutex); if (module->nwait < module->maxwait) { WKQ_WaiterToPool(module, waiter); waiter = NULL; } } } if (waiter) { EVENT_Destroy(&waiter->event); MEM_Free(waiter); } w->waiters = next; } if (QUEUE_Size(&module->itempool) < module->maxitems) { if (locked) { w->flags = WKI_DETACHED; QUEUE_InsertTail(&module->itempool, &w->itemsQ); } else { locked = MUTEX_Lock(&module->mutex); if (QUEUE_Size(&module->itempool) < module->maxitems) { w->flags = WKI_DETACHED; QUEUE_InsertTail(&module->itempool, &w->itemsQ); } else { MEM_Free(w); } } } else { MEM_Free(w); } if (locked) { MUTEX_Unlock(&module->mutex); } }
/** * Submits asynchronous XRPC call. Doesn't wait until the call completes. * This resolves the deadlock between Ecmt Gateway and emulator.exe */ STATIC void GWENG_SubmitAsyncCall(MidpSession* midp, XRpcString method, XRpcContainer* params) { if (params) { AsyncXRpcEntry* a = MEM_New(AsyncXRpcEntry); if (a) { memset(a, 0, sizeof(*a)); a->method = method; a->params = params; MUTEX_Lock(&midp->xrpcMutex); QUEUE_InsertTail(&midp->xrpcQueue, &a->entry); MUTEX_Unlock(&midp->xrpcMutex); if (!WKI_Submit(midp->xrpcWorkItem)) { /* * The work item is busy processing pending calls. It * could be that GWENG_AsyncXRpc callback has already * exited the loop but hasn't returned yet. In that case, * this asynchronous call would remain in the queue until * we submit the next one. That's not good. Try to "kick" * it with another work item. */ WKQ_InvokeLater(midp->xrpcWorkThread, GWENG_AsyncXRpc, midp); } } else { XRPC_FreeContainer(params); } } }
/** * Gets a RWLockWaiter structure for a shared waiter from the cache or * allocates a new one. Must be called under synchronization */ STATIC RWLockWaiter * RWLOCK_GetShareWaiter(RWLock * lock) { RWLockWaiter * w = RWLOCK_GetWaiter(lock); if (w) { w->event = &lock->shareEvent; QUEUE_InsertTail(&lock->shareWaiters, &w->entry); } return w; }
/** * Puts RWLockWaiter structure into the cache or deallocates it. This is * enough for a shared waiter but not for exclusive waiter (see function * below). Must be used under synchronization. */ STATIC void RWLOCK_ReleaseWaiter(RWLock * lock, RWLockWaiter * w) { QUEUE_RemoveEntry(&w->entry); if (QUEUE_Size(&lock->waiterCache) < MAX_WAITER_CACHE_SIZE) { w->index = 0; w->event = NULL; QUEUE_InsertTail(&lock->waiterCache, &w->entry); } else { MEM_Free(w); } }
/** * 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; }
/** * Gets a RWLockWaiter structure for an exclusive waiter from the cache or * allocates a new one. Adds the waiter to the tail of the queue. * Must be called under synchronization */ STATIC RWLockWaiter * RWLOCK_GetExclusiveWaiter(RWLock * lock) { RWLockWaiter * w = RWLOCK_GetWaiter(lock); if (w) { if (lock->flags & RWLOCK_FLAG_EXCLUSIVE_EVENT_IN_USE) { ASSERT(w->index); w->event = RWLOCK_GetExclusiveEvent(lock); if (!w->event) { RWLOCK_ReleaseWaiter(lock, w); return NULL; } } else { lock->flags |= RWLOCK_FLAG_EXCLUSIVE_EVENT_IN_USE; w->event = &lock->exclusiveEvent; } QUEUE_InsertTail(&lock->exclusiveWaiters, &w->entry); return w; } return NULL; }
/** * Submits a work item to the specified work queue. Re-submitting the same * work before it has been executed just moves it to the tail of the work * queue. It does NOT schedule it to run twice. */ Bool WKI_Submit(WorkItem * w) { WorkQueue * q = WKI_GetQueue(w); ASSERT(q); ASSERT(!(w->flags & WKI_DETACHED)); if (q) { if (MUTEX_Lock(&q->mutex)) { if (q->flags & WKQ_ACTIVE) { w->flags &= ~(WKI_DONE | WKI_CANCELED); QUEUE_RemoveEntry(&w->submitQ); QUEUE_InsertTail(&q->submit, &w->submitQ); EVENT_Set(&q->event); MUTEX_Unlock(&q->mutex); return True; } MUTEX_Unlock(&q->mutex); /* fall through and return False */ } } return False; }
/** * 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; }