static void removeWorker(EjsWorker *worker) { Ejs *ejs; assert(!worker->inside); assert(worker); ejs = worker->ejs; if (ejs) { lock(ejs); if (ejs->workers) { mprRemoveItem(ejs->workers, worker); } if (ejs->joining) { mprSignalDispatcher(ejs->dispatcher); } /* Accelerate GC */ if (worker->pair) { worker->pair->ejs = 0; worker->pair->pair = 0; worker->pair = 0; } worker->ejs = 0; unlock(ejs); } }
PUBLIC void mprSignalCompletion(MprDispatcher *dispatcher) { if (dispatcher == 0) { dispatcher = MPR->dispatcher; } dispatcher->flags |= MPR_DISPATCHER_COMPLETE; mprSignalDispatcher(dispatcher); }
/* Schedule a dispatcher to run but don't disturb an already running dispatcher. If the event queue is empty, the dispatcher is moved to the idleQ. If there is a past-due event, it is moved to the readyQ. If there is a future event pending, it is put on the waitQ. */ PUBLIC void mprScheduleDispatcher(MprDispatcher *dispatcher) { MprEventService *es; MprEvent *event; int mustWakeWaitService, mustWakeCond; assert(dispatcher); if (dispatcher->flags & MPR_DISPATCHER_DESTROYED) { return; } if ((es = dispatcher->service) == 0) { return; } lock(es); mustWakeWaitService = es->waiting; if (isRunning(dispatcher)) { mustWakeCond = dispatcher->flags & MPR_DISPATCHER_WAITING; } else if (isEmpty(dispatcher)) { queueDispatcher(es->idleQ, dispatcher); mustWakeCond = dispatcher->flags & MPR_DISPATCHER_WAITING; } else { event = dispatcher->eventQ->next; mustWakeWaitService = mustWakeCond = 0; if (event->due > es->now) { queueDispatcher(es->waitQ, dispatcher); if (event->due < es->willAwake) { mustWakeWaitService = 1; mustWakeCond = dispatcher->flags & MPR_DISPATCHER_WAITING; } } else { queueDispatcher(es->readyQ, dispatcher); mustWakeWaitService = es->waiting; mustWakeCond = dispatcher->flags & MPR_DISPATCHER_WAITING; } } unlock(es); if (mustWakeCond) { mprSignalDispatcher(dispatcher); } if (mustWakeWaitService) { mprWakeEventService(); } }
/* function terminate(): Void */ static EjsObj *workerTerminate(Ejs *ejs, EjsWorker *worker, int argc, EjsObj **argv) { if (worker->state == EJS_WORKER_BEGIN) { ejsThrowStateError(ejs, "Worker has not yet started"); return 0; } if (worker->state >= EJS_WORKER_COMPLETE) { return 0; } /* Switch to the inside worker if called from outside */ assert(worker->pair && worker->pair->ejs); ejs = (!worker->inside) ? worker->pair->ejs : ejs; ejs->exiting = 1; mprSignalDispatcher(ejs->dispatcher); return 0; }
/* Process a message sent from postMessage. This may run inside the worker or outside in the parent depending on the direction of the message. But it ALWAYS runs in the appropriate thread for the interpreter. */ static int doMessage(Message *msg, MprEvent *mprEvent) { Ejs *ejs; EjsObj *event, *frame; EjsWorker *worker; EjsFunction *callback; EjsObj *argv[1]; worker = msg->worker; worker->gotMessage = 1; ejs = worker->ejs; assert(!ejs->exception); event = 0; ejsBlockGC(ejs); callback = ejsGetProperty(ejs, worker, msg->callbackSlot); switch (msg->callbackSlot) { case ES_Worker_onerror: event = ejsCreateObj(ejs, ESV(ErrorEvent), 0); break; case ES_Worker_onclose: case ES_Worker_onmessage: event = ejsCreateObj(ejs, ESV(Event), 0); break; default: assert(msg->callbackSlot == 0); return 0; } worker->event = event; if (msg->data) { ejsSetProperty(ejs, event, ES_Event_data, ejsCreateStringFromAsc(ejs, msg->data)); } if (msg->message) { ejsSetProperty(ejs, event, ES_ErrorEvent_message, msg->message); } if (msg->stack) { ejsSetProperty(ejs, event, ES_ErrorEvent_stack, msg->stack); if ((frame = ejsGetProperty(ejs, msg->stack, 0)) != 0 && !ejsIs(ejs, frame, Void)) { ejsSetProperty(ejs, event, ES_ErrorEvent_filename, ejsGetPropertyByName(ejs, frame, EN("filename"))); ejsSetProperty(ejs, event, ES_ErrorEvent_lineno, ejsGetPropertyByName(ejs, frame, EN("lineno"))); } } assert(!ejs->exception); if (callback == 0 || ejsIs(ejs, callback, Null)) { if (msg->callbackSlot == ES_Worker_onmessage) { mprTrace(6, "Discard message as no onmessage handler defined for worker"); } else if (msg->callbackSlot == ES_Worker_onerror) { if (ejsIs(ejs, msg->message, String)) { ejsThrowError(ejs, "Exception in Worker: %@", ejsToString(ejs, msg->message)); } else { ejsThrowError(ejs, "Exception in Worker: %s", ejsGetErrorMsg(worker->pair->ejs, 1)); } } else { /* Ignore onclose message */ } } else if (!ejsIsFunction(ejs, callback)) { ejsThrowTypeError(ejs, "Worker callback %s is not a function", msg->callback); } else { assert(!ejs->exception); argv[0] = event; ejsRunFunction(ejs, callback, worker, 1, argv); } if (msg->callbackSlot == ES_Worker_onclose) { assert(!worker->inside); worker->state = EJS_WORKER_COMPLETE; mprTrace(5, "Worker.doMessage: complete"); /* Worker and insider interpreter are now eligible for garbage collection */ removeWorker(worker); } mprSignalDispatcher(ejs->dispatcher); worker->event = 0; return 0; }