/** * "connect" method handler */ STATIC XRpcElement* GWENG_MidpConnect(void* ctx, const XRpcContainer* param) { /* decode parameters */ const XRpcIntElement* sidParam = XRPC_GetIntElementByName(param, ECMTGW_SEI_CONNECT_SID_PARAM); const XRpcIntElement* cidParam = XRPC_GetIntElementByName(param, ECMTGW_SEI_CONNECT_CID_PARAM); const XRpcShortElement* portParam = XRPC_GetShortElementByName(param, ECMTGW_SEI_CONNECT_PORT_PARAM); if (sidParam && cidParam && portParam) { MidpSession* midp; EcmtGateway* gw = ctx; I32u cid = XRPC_GetInt(cidParam); Port port = XRPC_GetShort(portParam); MidpSessionKey key; key.xrpcSid = XRPC_GetInt(sidParam); key.xrpcSession = XRPC_GetCurrentSession(gw->xrpc); TRACE4("GW: MidpConnect(%08x.%08x.%u, port %hu)\n", key.xrpcSession, key.xrpcSid, cid, port); MUTEX_Lock(&gw->mutex); midp = HASH_Get(&gw->midpSessionMap,&key); if (midp) { MidpConnection* conn = MEM_New(MidpConnection); if (conn) { memset(conn, 0, sizeof(*conn)); conn->connId = cid; if (HASH_Put(&midp->connMap, (HashKey)cid, conn)) { char pkt[ECMT_MIDP_DEBUG_CONNECT_SIZE]; GWENG_MidpFillHeader(pkt,midp->sid,ECMT_MIDP_DEBUG_OPCODE_CONNECT); *((I32u*)(pkt+ECMT_MIDP_DEBUG_CONNECT_CID_OFFSET)) = htonl(cid); *((I16u*)(pkt+ECMT_MIDP_DEBUG_CONNECT_PORT_OFFSET)) = htons(port); GWENG_QueueAdd(gw->handsetQueue, KECMT_MIDP_DEBUG_PLUGIN_UID, pkt, ECMT_MIDP_DEBUG_CONNECT_SIZE); MUTEX_Unlock(&gw->mutex); return NULL; } MEM_Free(conn); } GWENG_MidpResetConn(gw, midp, cid, False, True); } else { TRACE3("GW: unexpected MIDP connect (%08x.%08x.%u)\n", key.xrpcSession, key.xrpcSid, cid); } MUTEX_Unlock(&gw->mutex); } return NULL; }
/** * Stop the worker thread */ void WKQ_Stop(WorkQueue * q, Bool canWait) { int nthreads = 0; if (MUTEX_Lock(&q->mutex)) { if (q->flags & WKQ_ACTIVE) { q->flags &= ~WKQ_ACTIVE; EVENT_Set(&q->event); } if (canWait && q->nthreads) { ASSERT(!(q->flags & WKQ_DEAD)); nthreads = q->nthreads; q->nthreads = 0; } MUTEX_Unlock(&q->mutex); } if (canWait) { if (nthreads > 0) { int i, flags = WKQ_DEAD; for (i=0; i<nthreads; i++) { if (THREAD_IsSelf(q->threads[i])) { /* We can handle this, but it's a bit awkward and * usually unnecessary. Let's issue a warning in * the debug build, just in case */ TRACE("WARNING! WKQ_Stop called on its own thread\n"); /* Settings WKQ_KILLME flag will cause WKQ_Thread to * kill the associated WorkQueue. Obviously, we have * to set this flag after all other threads have * terminated ot they all will try to do the same */ flags |= WKQ_KILLME; /* it's a bit weird that a thread is detaching itself * but it seems to work */ THREAD_Detach(q->threads[i]); } else { THREAD_Join(q->threads[i]); } } ASSERT(!(q->flags & WKQ_DEAD)); MUTEX_Lock(&q->mutex); q->flags |= flags; EVENT_Set(&q->stopEvent); MUTEX_Unlock(&q->mutex); } else { WKQ_Wait(q); } } }
/** * ECMT message filter */ Bool GWENG_MidpFilter(EcmtReader* reader, int uid, const void* data, int datalen) { if (uid == KECMT_MIDP_DEBUG_GATEWAY_UID) { ASSERT(datalen >= ECMT_MIDP_DEBUG_HEADER_SIZE); if (datalen >= ECMT_MIDP_DEBUG_HEADER_SIZE) { MidpSession* midp; EcmtGateway* gw = reader->gw; I32u sid; I8u opcode = GWENG_MidpParseHeader(data,&sid); MUTEX_Lock(&gw->mutex); midp = HASH_Get(&gw->ecmtSessionMap,(HashKey)sid); if (midp) { GWENG_MidpHandleEcmtPacket(gw, midp, data, datalen); } else { TRACE1("GW: invalid SID 0x%08x\n",sid); } MUTEX_Unlock(&gw->mutex); } return False; } else { return True; } }
/** * "session finished" callback */ STATIC void GWENG_MidpSessionClose(void * context, XRpcSession * session) { Iterator* i; EcmtGateway* gw = context; XRpcSession* xrpcSession = XRPC_GetCurrentSession(gw->xrpc); MUTEX_Lock(&gw->mutex); i = HASH_Values(&gw->ecmtSessionMap); if (i) { while (ITR_HasNext(i)) { MidpSession* midp = ITR_Next(i); if (midp->key.xrpcSession == xrpcSession) { char p[ECMT_MIDP_DEBUG_CLOSE_SIZE]; TRACE1("GW: terminating session 0x%08lx\n",midp->sid); GWENG_MidpFillHeader(p,midp->sid,ECMT_MIDP_DEBUG_OPCODE_CLOSE); GWENG_QueueAdd(gw->handsetQueue,KECMT_MIDP_DEBUG_PLUGIN_UID,p, ECMT_MIDP_DEBUG_CLOSE_SIZE); ITR_Remove(i); GWENG_MidpFree(gw, midp); } } ITR_Delete(i); } MUTEX_Unlock(&gw->mutex); }
/** * "open" method handler */ STATIC XRpcElement* GWENG_MidpOpen(void* ctx, const XRpcContainer* param) { /* decode parameters */ const XRpcIntElement* sidParam = XRPC_GetIntElementByName(param, ECMTGW_SEI_SEND_SID_PARAM); if (sidParam) { EcmtGateway* gw = ctx; MidpSession* midp; MUTEX_Lock(&gw->mutex); TRACE2("GW: MidpOpen(%08x.%08x)\n", XRPC_GetCurrentSession(gw->xrpc), XRPC_GetInt(sidParam)); midp = GWENG_MidpCreateSession(gw,XRPC_GetInt(sidParam)); if (midp) { char p[ECMT_MIDP_DEBUG_OPEN_SIZE]; GWENG_MidpFillHeader(p, midp->sid, ECMT_MIDP_DEBUG_OPCODE_OPEN); GWENG_QueueAdd(gw->handsetQueue,KECMT_MIDP_DEBUG_PLUGIN_UID,p, ECMT_MIDP_DEBUG_OPEN_SIZE); } MUTEX_Unlock(&gw->mutex); } else { TRACE("GW: open without cid!\n"); } return NULL; }
/** * Returns a wait context from the pool, or allocates a new one */ STATIC Waiter * WKQ_GetWaiter(WorkQueueModule * module) { Waiter * waiter = NULL; ASSERT(module->initcount > 0); if (module->waitpool) { MUTEX_Lock(&module->mutex); if (module->waitpool) { waiter = module->waitpool; module->waitpool = waiter->next; waiter->next = NULL; module->nwait--; ASSERT(module->nwait >= 0); ASSERT(module->nwait || !module->waitpool); } MUTEX_Unlock(&module->mutex); } if (!waiter) { waiter = MEM_New(Waiter); if (waiter) { if (!EVENT_Init(&waiter->event)) { MEM_Free(waiter); waiter = NULL; } } } if (waiter) { EVENT_Reset(&waiter->event); } return waiter; }
/** * 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); } } }
/** * Returns the next pseudorandom, uniformly distributed 32-bit integer * value from this random number generator's sequence. */ I32s RANDOM_NextI32(Random * r) { Bool unlock = BoolValue(r->syn && MUTEX_Lock(&r->mutex)); I32s next = NEXT_RANDOM(r,32); if (unlock) MUTEX_Unlock(&r->mutex); return next; }
/** * 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); }
/** * Releases the critical section */ void CS_Unlock(CritSect * cs) { ASSERT(MUTEX_IsLocked(&cs->mutex)); if (MUTEX_IsLocked(&cs->mutex)) { ASSERT(cs->count > 0); if ((--cs->count) == 0) { MUTEX_Unlock(&cs->mutex); } } }
/** * Returns the seed that can be used to restart the current pseudorandom * number sequence from its current point. */ Seed RANDOM_GetState(const Random * r) { Seed state = (Seed)0; if (r->rng.rng_state) { Bool unlock = BoolValue(r->syn && MUTEX_Lock((Mutex*)(&r->mutex))); state = (*(r->rng.rng_state))(r->ctx); if (unlock) MUTEX_Unlock((Mutex*)(&r->mutex)); } return state; }
/** * "send" method handler */ STATIC XRpcElement* GWENG_MidpSend(void* ctx, const XRpcContainer* param) { /* decode parameters */ const XRpcIntElement* sidParam = XRPC_GetIntElementByName(param, ECMTGW_SEI_SEND_SID_PARAM); const XRpcIntElement* cidParam = XRPC_GetIntElementByName(param, ECMTGW_SEI_SEND_CID_PARAM); const XRpcBinaryElement* dataParam = XRPC_GetBinaryElementByName(param, ECMTGW_SEI_SEND_DATA_PARAM); if (sidParam && cidParam && dataParam) { MidpSession* midp; EcmtGateway* gw = ctx; size_t size = XRPC_GetBinaryDataSize(dataParam); I32u cid = XRPC_GetInt(cidParam); const XRpcByte* data = XRPC_GetBinaryData(dataParam); MidpSessionKey key; key.xrpcSid = XRPC_GetInt(sidParam); key.xrpcSession = XRPC_GetCurrentSession(gw->xrpc); TRACE4("GW: MidpSend(%08x.%08x.%u, %d bytes)\n", key.xrpcSession, key.xrpcSid, cid, size); DEBUG_ONLY(PRINT_Dump(DEBUG_Trace,data,size,0)); MUTEX_Lock(&gw->mutex); midp = HASH_Get(&gw->midpSessionMap,&key); if (midp) { MidpConnection* conn = HASH_Get(&midp->connMap,(HashKey)cid); if (conn) { char h[ECMT_MIDP_DEBUG_SEND_DATA_OFFSET]; I32u seq = conn->outCount++; GWENG_MidpFillHeader(h,midp->sid,ECMT_MIDP_DEBUG_OPCODE_SEND); *((I32u*)(h+ECMT_MIDP_DEBUG_SEND_CID_OFFSET)) = htonl(cid); *((I32u*)(h+ECMT_MIDP_DEBUG_SEND_SEQ_OFFSET)) = htonl(seq); GWENG_QueueAdd2(gw->handsetQueue, KECMT_MIDP_DEBUG_PLUGIN_UID, h, ECMT_MIDP_DEBUG_SEND_DATA_OFFSET, data, size); } else { TRACE1("GW: invalid conn id %u\n",cid); GWENG_MidpResetConn(gw, midp, cid, False, True); } } else { TRACE2("GW: unexpected MIDP send (%08x.%08x)\n", key.xrpcSession, key.xrpcSid); } MUTEX_Unlock(&gw->mutex); } return NULL; }
/** * 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); } }
/** * Returns a pseudorandom, uniformly distributed 32-bit integer value * between 0 (inclusive) and the specified value (exclusive), drawn * from this random number generator's sequence. */ I32s RANDOM_NextInt32(Random * r, int n) { Bool unlock = BoolValue(r->syn && MUTEX_Lock(&r->mutex)); I32s bits, val; ASSERT(n>0); if ((n & -n) == n) { /* i.e., n is a power of 2 */ I64s next = (I64s)NEXT_RANDOM(r,31); if (unlock) MUTEX_Unlock(&r->mutex); return (I32s)((n * next) >> 31); }
/** * Sets the seed of this random number generator. */ Seed RANDOM_SetSeed(Random * r, Seed s) { Bool unlock = BoolValue(r->syn && MUTEX_Lock(&r->mutex)); r->seed = s; (*(r->rng.rng_seed))(r->ctx, s); #ifndef __KERNEL__ r->haveNextGaussian = False; #endif /* __KERNEL__ */ if (unlock) MUTEX_Unlock(&r->mutex); return s; }
/** * 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); }
/** * 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; }
/** * Converts exclusive lock to non-exclusive without releasing it. The caller * must own the lock exclusively. */ Bool RWLOCK_DropWrite(RWLock * lock) { Bool success = False; RWEntry * entry; MUTEX_Lock(&lock->mutex); entry = RWLOCK_FindEntry(lock); /* current thread must have the lock */ ASSERT(entry); if (entry) { /* and it must be the write lock */ ASSERT(entry->write > 0); if (entry->write > 0) { QEntry * e; RWLockWaiter * shareWaiter = NULL; RWLockWaiter * exclusiveWaiter = NULL; /* convert write lock to read lock */ entry->read += entry->write; entry->write = 0; /* lock is no longer owned exclusively */ ASSERT(lock->flags & RWLOCK_FLAG_EXCLUSIVE_LOCK); lock->flags &= ~RWLOCK_FLAG_EXCLUSIVE_LOCK; /* * wake up shared waiters only unless the exclusive * waiter is first in the line */ e = QUEUE_First(&lock->shareWaiters); if (e) shareWaiter = QCAST(e,RWLockWaiter,entry); e = QUEUE_First(&lock->exclusiveWaiters); if (e) exclusiveWaiter = QCAST(e,RWLockWaiter,entry); if (shareWaiter && (!exclusiveWaiter || shareWaiter->index < exclusiveWaiter->index)) { EVENT_Set(shareWaiter->event); } /* success */ success = True; } } MUTEX_Unlock(&lock->mutex); return success; }
/** * 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; }
/** * Finds port by name. Optionally, adds a reference to the returned port. * The search is case insensitive. */ EcmtGatewayPort* GWENG_PortByName(EcmtGatewayTransport* t, Str name, Bool ref) { QEntry* e; EcmtGatewayPort* port = NULL; MUTEX_Lock(&t->mutex); for (e = QUEUE_First(&t->ports); e; e = QUEUE_Next(e)) { if (!StrCaseCmp(name, QCAST(e,EcmtGatewayPort,entry)->name)) { port = QCAST(e,EcmtGatewayPort,entry); if (ref) GWENG_PortAddRef(port); break; } } MUTEX_Unlock(&t->mutex); return port; }
/** * Puts event back to the pool or deallocates it. */ STATIC void WKQ_ReleaseWaiter(WorkQueueModule * module, Waiter * waiter) { ASSERT(module->initcount > 0); if (module->nwait < module->maxwait) { MUTEX_Lock(&module->mutex); if (module->nwait < module->maxwait) { WKQ_WaiterToPool(module, waiter); waiter = NULL; } MUTEX_Unlock(&module->mutex); } if (waiter) { EVENT_Destroy(&waiter->event); MEM_Free(waiter); } }
/** * Attaches a waiter to the work item if the work item is waitable. * The caller is responsible for deallocating this waiter */ STATIC Waiter * WKI_AttachWaiter(WorkItem * w) { Waiter * waiter = NULL; WorkQueue * q = WKI_GetQueue(w); ASSERT(!(w->flags & WKI_DETACHED)); /* quick check without synchronization */ if (w->submitQ.queue || (w->flags & WKI_CALL)) { /* We avoid calling WKQ_GetWaiter under mutex because in NT kernel * mode environment this results in KeInitializeEvent being called * at IRQL DISPATCH_LEVEL. According to the Windows DDK documentation, * the callers of KeInitializeEvent must be running at IRQL * PASSIVE_LEVEL. I personally think it's a mistake in the * documentation, because KeInitializeEvent does not do anything * that would require the current thread to wait or to access * pageable memory, unless the event is allocated from paged pool * or the KeInitializeEvent code itself resides in a pageable code * segment (which does not seem to be the case). Anyway, I decided * to play it safe and follow the documentation. */ waiter = WKQ_GetWaiter(&WKQ); if (waiter) { Bool waitable = False; MUTEX_Lock(&q->mutex); /* the same check, this time under synchronization */ if (w->submitQ.queue || (w->flags & WKI_CALL)) { waiter->next = w->waiters; w->waiters = waiter; waitable = True; } MUTEX_Unlock(&q->mutex); if (!waitable) { /* Something must have changed while we were allocating the * waiter. Return it to the pool. In real life, this almost * never happens. */ WKQ_ReleaseWaiter(&WKQ, waiter); waiter = NULL; } } } return waiter; }
/** * 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); } }
/** * Sets the idle timeout function for the work queue. If the cb parameter * is NULL, idle timeouts are disabled and the other parameters (ms, param) * are ignored. The timeout must be positive. * * NOTE: currently, the old callback may still be invoked (btu no more than * once) after this function has returned. */ void WKQ_SetIdle(WorkQueue * q, long ms, IdleProc cb, void * param) { if (MUTEX_Lock(&q->mutex)) { if (cb) { ASSERT(ms > 0); q->idleTimeout = MAX(ms,1); q->idleProc = cb; q->idleParam = param; } else { q->idleTimeout = 0; q->idleProc = NULL; q->idleParam = NULL; } /* notify the worker thread */ EVENT_Set(&q->event); MUTEX_Unlock(&q->mutex); } }
/** * "close" method handler */ STATIC XRpcElement* GWENG_MidpClose(void* ctx, const XRpcContainer* param) { /* decode parameters */ const XRpcIntElement* sidParam = XRPC_GetIntElementByName(param, ECMTGW_SEI_SEND_SID_PARAM); if (sidParam) { EcmtGateway* gw = ctx; MidpSession* midp; MidpSessionKey key; key.xrpcSid = XRPC_GetInt(sidParam); key.xrpcSession = XRPC_GetCurrentSession(gw->xrpc); MUTEX_Lock(&gw->mutex); TRACE2("GW: MidpClose(%08x.%08x)\n", key.xrpcSession, key.xrpcSid); midp = HASH_Get(&gw->midpSessionMap,&key); if (midp) { char p[ECMT_MIDP_DEBUG_CLOSE_SIZE]; GWENG_MidpFillHeader(p, midp->sid, ECMT_MIDP_DEBUG_OPCODE_CLOSE); GWENG_QueueAdd(gw->handsetQueue,KECMT_MIDP_DEBUG_PLUGIN_UID,p, ECMT_MIDP_DEBUG_CLOSE_SIZE); TRACE1("GW: closing session 0x%08lx (via XRPC)\n",midp->sid); GWENG_MidpFree(gw, midp); } else { TRACE("GW: XRPC close for non-existent session\n"); } MUTEX_Unlock(&gw->mutex); } else { TRACE("GW: close without cid!\n"); } return NULL; }
/** * "reset" method handler */ STATIC XRpcElement* GWENG_MidpReset(void* ctx, const XRpcContainer* param) { /* decode parameters */ const XRpcIntElement* sidParam = XRPC_GetIntElementByName(param, ECMTGW_SEI_CONNECT_SID_PARAM); const XRpcIntElement* cidParam = XRPC_GetIntElementByName(param, ECMTGW_SEI_CONNECT_CID_PARAM); if (sidParam && cidParam) { MidpSession* midp; EcmtGateway* gw = ctx; I32u cid = XRPC_GetInt(cidParam); MidpSessionKey key; key.xrpcSid = XRPC_GetInt(sidParam); key.xrpcSession = XRPC_GetCurrentSession(gw->xrpc); TRACE3("GW: MidpReset(%08x.%08x.%u)\n", key.xrpcSession, key.xrpcSid, cid); MUTEX_Lock(&gw->mutex); midp = HASH_Get(&gw->midpSessionMap,&key); if (midp) { GWENG_MidpResetConn(gw, midp, cid, True, False); } else { TRACE3("GW: unexpected MIDP reset (%08x.%08x.%u)\n", key.xrpcSession, key.xrpcSid, cid); } MUTEX_Unlock(&gw->mutex); } 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); } }
/** * Locks resource for non-exclusive use, waits if necessary. Returns True * if lock has been successfully acquired, otherwise False. */ Bool RWLOCK_TimeReadLock(RWLock * lock, long ms) { Bool ok = True; Bool success = False; RWLockWaiter * waiter = NULL; Time deadline = 0; /* * this flag is False if we have found that current thread is NOT * an owner of the resource, so that we don't scan the lock entries * more than once. */ Bool maybeOwner = True; RWEntry * entry = NULL; /* calculate the deadline if it's a wait with timeout */ if (ms > 0) { deadline = TIME_Now() + ms; } MUTEX_Lock(&lock->mutex); while (ok) { Time now = 0; /* * if this thread already owns this resource either exclusively * or shared, we are all set. All we need is to increment entry * count. NOTE that we don't touch the "exclusive" flag, meaning * that if resource has been acquired exclusively, it remains * this way. */ if (maybeOwner) { entry = RWLOCK_FindEntry(lock); if (entry) { success = True; if (lock->flags & RWLOCK_FLAG_EXCLUSIVE_LOCK) { ASSERT(entry->write > 0); entry->write++; } else { ASSERT(entry->write == 0); entry->read++; } break; } else { maybeOwner = False; /* don't scan entry table again */ } } /* if resource is not owned and no one is waiting, we can have it */ if (lock->locks <= 0 && QUEUE_Size(&lock->shareWaiters) == 0 && QUEUE_Size(&lock->exclusiveWaiters) == 0) { /* * note that it's quite possible that resource is not owned * but the wait queue is not empty. this can happen for example * if this thread just released the resource and waiters didn't * yet have the chance to run. in such case, this thread should * be place into the queue to avoid starving the waiters */ entry = RWLOCK_GetEntry(lock); if (entry) { success = True; lock->flags &= ~RWLOCK_FLAG_EXCLUSIVE_LOCK; entry->read++; } break; } /* * if resource is owned in shared mode, there's a good chance that * we can have it immediately. Some restrictions apply (see below) */ if (!(lock->flags & RWLOCK_FLAG_EXCLUSIVE_LOCK)) { /* * normally we allow this thread to access the resource * in readonly mode even if there's an exclusive waiter. * However, if we always did that, the exclusive waiter * might end up waiting forever if new readonly waters * keep coming. To prevent this from happening, we count * the number of times an exclusive waiter has been bypassed * by a lucky late-coming reader. If this number exceeds * the limit, everyone has to stay in the line. */ if (QUEUE_Size(&lock->exclusiveWaiters) == 0 || lock->bypassCount < RWLOCK_MAX_BYPASS_COUNT) { entry = RWLOCK_GetEntry(lock); if (entry) { if (QUEUE_Size(&lock->exclusiveWaiters) > 0) { lock->bypassCount++; } ASSERT(entry->write == 0); success = True; entry->read++; } break; } } /* * resource cannot be acquired immediately for exclusive access. * If we cannot wait (any longer), break the loop. */ if (ms == 0) { break; } else if (ms > 0) { /* check for timeout */ now = TIME_Now(); if (now >= deadline) { break; } } /* * release the mutex and wait for event to be signalled, then * start it all over again. */ lock->contentions++; if (!waiter) { waiter = RWLOCK_GetShareWaiter(lock); if (!waiter) break; } EVENT_Reset(&lock->shareEvent); MUTEX_Unlock(&lock->mutex); /* wait */ if (ms > 0) { long tmo = (long)(deadline - now); if (EVENT_TimeWait(waiter->event, tmo) == WAIT_STATE_ERROR) { ok = False; } } else { ok = BoolValue(EVENT_Wait(waiter->event) == WAIT_STATE_OK); } MUTEX_Lock(&lock->mutex); } if (success) lock->locks++; if (waiter) RWLOCK_ReleaseWaiter(lock, waiter); MUTEX_Unlock(&lock->mutex); return success; }
/** * Release n recursively acquired locks. */ void RWLOCK_UnlockMany(RWLock * lock, int n) { if (n > 0) { RWEntry * entry; MUTEX_Lock(&lock->mutex); entry = RWLOCK_FindEntry(lock); /* * if we cannot find the entry, it means that current thread * does not own the lock. It's a programming error. */ ASSERT(entry); if (entry) { lock->locks--; /* first release write locks */ if (entry->write > 0) { if (entry->write >= n) { entry->write -= n; n = 0; } else { n -= entry->write; entry->write = 0; } } /* then read locks */ if (n > 0) { entry->read -= n; } /* * ASSERT that current thread does not release more locks than * it has acquired */ ASSERT(lock->locks >= 0); ASSERT(entry->read >= 0); ASSERT(entry->write >= 0); /* * no more work to do unless calling thread has released the * resource (i.e. usage count came down to zero) */ if ((entry->read + entry->write) <= 0) { int i; int inUse; QEntry * e; RWLockWaiter * shareWaiter = NULL; RWLockWaiter * exclusiveWaiter = NULL; entry->id = 0; lock->entriesActive--; ASSERT(lock->entriesActive >= 0); /* * update lock->entriesInUse * NOTE that RWLOCK_FindStaticEntry() may access it without * synchronization. */ i = lock->entriesInUse - 1; inUse = 0; while (i >= 0) { RWEntry * lockEntry = GET_ENTRY(lock,i); if (lockEntry->id) { inUse = i + 1; break; } i--; } lock->entriesInUse = inUse; /* * if resource was acquired exclusively, it must be free now */ if (lock->flags & RWLOCK_FLAG_EXCLUSIVE_LOCK) { ASSERT(!lock->locks); lock->flags &= ~RWLOCK_FLAG_EXCLUSIVE_LOCK; } /* * release the waiters in the order they have arrived */ e = QUEUE_First(&lock->shareWaiters); if (e) shareWaiter = QCAST(e,RWLockWaiter,entry); e = QUEUE_First(&lock->exclusiveWaiters); if (e) exclusiveWaiter = QCAST(e,RWLockWaiter,entry); if (exclusiveWaiter && (!shareWaiter || exclusiveWaiter->index < shareWaiter->index)) { EVENT_Set(exclusiveWaiter->event); } else if (shareWaiter) { /* this should unblock all shared waiters */ EVENT_Set(shareWaiter->event); } } else if (lock->flags & RWLOCK_FLAG_EXCLUSIVE_LOCK) { /* * if the owner of the lock has released all its WRITE locks * but still have some READ locks, switch to shared mode. */ if (!entry->write) { QEntry * e; RWLockWaiter * shareWaiter = NULL; RWLockWaiter * exclusiveWaiter = NULL; ASSERT(entry->read > 0); lock->flags &= ~RWLOCK_FLAG_EXCLUSIVE_LOCK; /* * wake up shared waiters only unless the exclusive * waiter is first in the line */ e = QUEUE_First(&lock->shareWaiters); if (e) shareWaiter = QCAST(e,RWLockWaiter,entry); e = QUEUE_First(&lock->exclusiveWaiters); if (e) exclusiveWaiter = QCAST(e,RWLockWaiter,entry); if (shareWaiter && (!exclusiveWaiter || shareWaiter->index < exclusiveWaiter->index)) { EVENT_Set(shareWaiter->event); } } } } MUTEX_Unlock(&lock->mutex); } }