/** * Destroys the contents of the EcmtGatewayPort structure but doesn't * deallocate the structure itself. */ void GWENG_PortDestroy(EcmtGatewayPort* port) { ASSERT(port->ref < 2); QUEUE_RemoveEntry(&port->entry); MEM_Free(port->name); MEM_Free(port->comment); }
STATIC Bool QUEUE_ItrRemove(Iterator * itr) { Bool ok = False; QueueIterator * qi = CAST(itr,QueueIterator,itr); if (qi->entry) { ok = QUEUE_RemoveEntry(qi->entry); qi->entry = NULL; } return ok; }
/** * 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); } }
/** * Switches the work item into the "detached" state. A detached work * item is not waitable, the caller does not own the work item anymore. */ void WKI_Detach(WorkItem * w) { WorkQueue * q = WKI_GetQueue(w); ASSERT(!(w->flags & WKI_DETACHED)); MUTEX_Lock(&q->mutex); w->flags |= WKI_DETACHED; if (!w->submitQ.queue && !(w->flags & WKI_CALL)) { QUEUE_RemoveEntry(&w->itemsQ); WKQ_ReleaseWorkItem(&WKQ, w); } MUTEX_Unlock(&q->mutex); }
/** * Cancels the work item. Returns True if work item has been removed from * the queue before being called, False in any other case. Unblocks the * waiters. */ Bool WKI_Cancel(WorkItem * w) { Bool canceled = False; WorkQueue * q = WKI_GetQueue(w); ASSERT(!(w->flags & WKI_DETACHED)); if (MUTEX_Lock(&q->mutex)) { if (QUEUE_RemoveEntry(&w->submitQ)) { canceled = True; w->flags |= WKI_CANCELED; WKI_Signal(w); } MUTEX_Unlock(&q->mutex); } return canceled; }
/** * 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); } }
/** * 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; }
/** * 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); } }