/* Get the time to sleep till the next pending event. Must be called locked. */ static MprTicks getIdleTicks(MprEventService *es, MprTicks timeout) { MprDispatcher *readyQ, *waitQ, *dp; MprEvent *event; MprTicks delay; waitQ = es->waitQ; readyQ = es->readyQ; if (readyQ->next != readyQ) { delay = 0; } else if (mprIsStopping()) { delay = 10; } else { /* Examine all the dispatchers on the waitQ */ delay = es->delay ? es->delay : MPR_MAX_TIMEOUT; for (dp = waitQ->next; dp != waitQ; dp = dp->next) { event = dp->eventQ->next; if (event != dp->eventQ) { delay = min(delay, (event->due - es->now)); if (delay <= 0) { break; } } } delay = min(delay, timeout); es->delay = 0; } return delay < 0 ? 0 : delay; }
/* static function run(timeout: Number = -1, oneEvent: Boolean = false): Boolean */ static EjsObj *app_run(Ejs *ejs, EjsObj *unused, int argc, EjsObj **argv) { MprTicks mark, remaining; int64 dispatcherMark; int rc, oneEvent, timeout; timeout = (argc > 0) ? ejsGetInt(ejs, argv[0]) : MAXINT; oneEvent = (argc > 1) ? ejsGetInt(ejs, argv[1]) : 0; if (ejs->hosted) { return ESV(true); } if (timeout < 0) { timeout = MAXINT; } mark = mprGetTicks(); remaining = timeout; dispatcherMark = mprGetEventMark(ejs->dispatcher); do { rc = mprWaitForEvent(ejs->dispatcher, remaining, dispatcherMark); remaining = mprGetRemainingTicks(mark, timeout); dispatcherMark = mprGetEventMark(ejs->dispatcher); } while (!ejs->exception && !oneEvent && !ejs->exiting && remaining > 0 && !mprIsStopping()); return (rc == 0) ? ESV(true) : ESV(false); }
/* function run(): Void */ static EjsVoid *hs_run(Ejs *ejs, EjsHttpServer *sp, int argc, EjsObj **argv) { if (!sp->hosted) { while (!ejs->exiting && !mprIsStopping()) { mprWaitForEvent(ejs->dispatcher, MAXINT); } } return 0; }
/* function run(): Void */ static EjsVoid *hs_run(Ejs *ejs, EjsHttpServer *sp, int argc, EjsObj **argv) { int64 dispatcherMark; if (!sp->hosted) { dispatcherMark = mprGetEventMark(ejs->dispatcher); while (!ejs->exiting && !mprIsStopping()) { mprWaitForEvent(ejs->dispatcher, MPR_MAX_TIMEOUT, dispatcherMark); dispatcherMark = mprGetEventMark(ejs->dispatcher); } } return 0; }
static int unloadEsp(MprModule *mp) { HttpStage *stage; if (esp->inUse) { return MPR_ERR_BUSY; } if (mprIsStopping()) { return 0; } if ((stage = httpLookupStage(mp->name)) != 0) { stage->flags |= HTTP_STAGE_UNLOADED; } return 0; }
/* Interpret from the console or from a literal command */ static int interpretCommands(EcCompiler *cp, cchar *cmd) { Ejs *ejs; EjsString *result; char *tmpArgv[1]; int err; ejs = cp->ejs; cp->interactive = 1; if (ecOpenConsoleStream(cp, (cmd) ? commandGets: consoleGets, cmd) < 0) { mprError("Cannot open input"); return EJS_ERR; } tmpArgv[0] = EC_INPUT_STREAM; while (!cp->stream->eof && !mprIsStopping()) { err = 0; cp->uid = 0; ejs->result = ESV(undefined); if (ecCompile(cp, 1, tmpArgv) < 0) { mprRawLog(0, "%s", cp->errorMsg); ejs->result = ESV(undefined); err++; } if (!err && cp->errorCount == 0) { if (ejsRunProgram(ejs, NULL, NULL) < 0) { ejsReportError(ejs, "Error in script"); } } if (!ejs->exception && ejs->result != ESV(undefined)) { if (ejsIs(ejs, ejs->result, Date) /* MOB || ejsIsType(ejs, ejs->result) */) { if ((result = (EjsString*) ejsToString(ejs, ejs->result)) != 0) { mprPrintf("%@\n", result); } } else if (ejs->result != ESV(null)) { if ((result = (EjsString*) ejsSerialize(ejs, ejs->result, EJS_JSON_SHOW_PRETTY)) != 0) { mprPrintf("%@\n", result); } } } ecResetInput(cp); cp->errorCount = 0; cp->fatalError = 0; } ecCloseStream(cp); return 0; }
/* Pause the application. This services events while asleep. static function sleep(delay: Number = -1): void MOB - sleep currently throws if an exception is generated in an event callback (worker). It should not. */ static EjsObj *app_sleep(Ejs *ejs, EjsObj *unused, int argc, EjsObj **argv) { MprTicks mark, remaining; int timeout; timeout = (argc > 0) ? ejsGetInt(ejs, argv[0]) : MAXINT; if (timeout < 0) { timeout = MAXINT; } mark = mprGetTicks(); remaining = timeout; do { mprWaitForEvent(ejs->dispatcher, (int) remaining); remaining = mprGetRemainingTicks(mark, timeout); } while (!ejs->exiting && remaining > 0 && !mprIsStopping()); return 0; }
/* Sample main event loop. This demonstrates how to integrate Mpr with your applications event loop using select() */ void eventLoop() { MSG msg; /* If single threaded or if you desire control over the event loop, you should code an event loop similar to that below: */ while (!mprIsStopping()) { /* Socket events will be serviced in the msgProc */ if (GetMessage(&msg, NULL, 0, 0) == 0) { /* WM_QUIT received */ break; } TranslateMessage(&msg); DispatchMessage(&msg); } }
/* Schedule events. This routine will service events until the timeout expires or if MPR_SERVICE_NO_BLOCK is specified in flags, until there are no more events to service. This routine will also return when the MPR is stopping. This will service all enabled non-running dispatcher queues and pending I/O events. An app should dedicate only one thread to be an event service thread. @param timeout Time in milliseconds to wait. Set to zero for no wait. Set to -1 to wait forever. @param flags Set to MPR_SERVICE_NO_BLOCK for non-blocking. @returns Number of events serviced. */ PUBLIC int mprServiceEvents(MprTicks timeout, int flags) { MprEventService *es; MprDispatcher *dp; MprTicks expires, delay; int beginEventCount, eventCount; if (MPR->eventing) { mprLog("warn mpr event", 0, "mprServiceEvents called reentrantly"); return 0; } mprAtomicBarrier(); if (mprIsDestroying()) { return 0; } MPR->eventing = 1; es = MPR->eventService; beginEventCount = eventCount = es->eventCount; es->now = mprGetTicks(); expires = timeout < 0 ? MPR_MAX_TIMEOUT : (es->now + timeout); if (expires < 0) { expires = MPR_MAX_TIMEOUT; } mprSetWindowsThread(0); while (es->now <= expires) { eventCount = es->eventCount; mprServiceSignals(); while ((dp = getNextReadyDispatcher(es)) != NULL) { assert(!isRunning(dp)); queueDispatcher(es->runQ, dp); if (dp->flags & MPR_DISPATCHER_IMMEDIATE) { dispatchEventsWorker(dp); } else { if (mprStartWorker((MprWorkerProc) dispatchEventsWorker, dp) < 0) { /* Should not get here */ queueDispatcher(es->pendingQ, dp); break; } } } if (flags & MPR_SERVICE_NO_BLOCK) { expires = 0; /* But still service I/O events below */ } if (es->eventCount == eventCount) { lock(es); delay = getIdleTicks(es, expires - es->now); es->willAwake = es->now + delay; es->waiting = 1; unlock(es); /* Service IO events */ mprWaitForIO(MPR->waitService, delay); } es->now = mprGetTicks(); if (flags & MPR_SERVICE_NO_BLOCK) { break; } if (mprIsStopping()) { break; } } MPR->eventing = 0; mprSignalCond(MPR->cond); return abs(es->eventCount - beginEventCount); }
static void runService() { MprTime mark; char **av, *env[3], **argv; int err, i, status, ac, next; app->servicePid = 0; atexit(cleanup); mprLog(1, "%s: Watching over %s", app->appName, app->serviceProgram); if (access(app->serviceProgram, X_OK) < 0) { mprError("start: can't access %s, errno %d", app->serviceProgram, mprGetOsError()); return; } if (writePid(getpid()) < 0) { return; } mark = mprGetTime(); while (!mprIsStopping()) { if (mprGetElapsedTime(mark) > (3600 * 1000)) { mark = mprGetTime(); app->restartCount = 0; app->restartWarned = 0; } if (app->servicePid == 0) { if (app->restartCount >= app->retries) { if (! app->restartWarned) { mprError("Too many restarts for %s, %d in ths last hour", app->serviceProgram, app->restartCount); mprError("Suspending restarts for one hour"); app->restartWarned++; } mprSleep(60 * 1000); continue; } /* Create the child */ app->servicePid = vfork(); if (app->servicePid < 0) { mprError("Can't fork new process to run %s", app->serviceProgram); continue; } else if (app->servicePid == 0) { /* Child */ umask(022); setsid(); mprLog(1, "%s: Change dir to %s", app->appName, app->serviceHome); if (chdir(app->serviceHome) < 0) {} for (i = 3; i < 128; i++) { close(i); } if (app->serviceArgs && *app->serviceArgs) { ac = mprMakeArgv(app->serviceArgs, &av, 0); } else { ac = 0; } argv = mprAlloc(sizeof(char*) * (6 + ac)); env[0] = sjoin("LD_LIBRARY_PATH=", app->serviceHome, NULL); env[1] = sjoin("PATH=", getenv("PATH"), NULL); env[2] = 0; next = 0; argv[next++] = app->serviceProgram; if (app->logSpec) { argv[next++] = "--log"; argv[next++] = (char*) app->logSpec; } for (i = 0; i < ac; i++) { argv[next++] = av[i]; } argv[next++] = 0; mprLog(1, "%s: Running %s", app->appName, app->serviceProgram); for (i = 1; argv[i]; i++) { mprLog(1, "%s: argv[%d] = %s", app->appName, i, argv[i]); } execve(app->serviceProgram, argv, (char**) &env); /* Should not get here */ err = errno; mprError("%s: Can't exec %s, err %d, cwd %s", app->appName, app->serviceProgram, err, app->serviceHome); exit(MPR_ERR_CANT_INITIALIZE); } /* Parent */ mprLog(1, "%s: create child %s at pid %d", app->appName, app->serviceProgram, app->servicePid); app->restartCount++; waitpid(app->servicePid, &status, 0); mprLog(1, "%s: %s has exited with status %d", app->appName, app->serviceProgram, WEXITSTATUS(status)); if (!mprIsStopping()) { mprLog(1, "%s: Restarting %s (%d/%d)...", app->appName, app->serviceProgram, app->restartCount, app->retries); } app->servicePid = 0; } } }
MAIN(appweb, int argc, char **argv, char **envp) { Mpr *mpr; cchar *ipAddrPort, *argp, *jail; char *ip, *logSpec; int argind, port, status, verbose; ipAddrPort = 0; ip = 0; jail = 0; port = -1; verbose = 0; logSpec = 0; argv[0] = BLD_APPWEB_PATH; if ((mpr = mprCreate(argc, argv, MPR_USER_EVENTS_THREAD)) == NULL) { exit(1); } mprSetAppName(BLD_PRODUCT, BLD_NAME, BLD_VERSION); if ((app = mprAllocObj(App, manageApp)) == NULL) { exit(2); } mprAddRoot(app); mprAddStandardSignals(); #if BLD_FEATURE_ROMFS extern MprRomInode romFiles[]; mprSetRomFileSystem(romFiles); #endif app->mpr = mpr; app->workers = -1; app->configFile = BLD_CONFIG_FILE; app->home = BLD_SERVER_ROOT; app->documents = app->home; argc = mpr->argc; argv = mpr->argv; for (argind = 1; argind < argc; argind++) { argp = argv[argind]; if (*argp != '-') { break; } if (smatch(argp, "--config") || smatch(argp, "--conf")) { if (argind >= argc) { usageError(); } app->configFile = sclone(argv[++argind]); #if BLD_UNIX_LIKE } else if (smatch(argp, "--chroot")) { if (argind >= argc) { usageError(); } jail = mprGetAbsPath(argv[++argind]); #endif } else if (smatch(argp, "--debugger") || smatch(argp, "-D")) { mprSetDebugMode(1); } else if (smatch(argp, "--exe")) { if (argind >= argc) { usageError(); } mpr->argv[0] = mprGetAbsPath(argv[++argind]); mprSetAppPath(mpr->argv[0]); mprSetModuleSearchPath(NULL); } else if (smatch(argp, "--home")) { if (argind >= argc) { usageError(); } app->home = mprGetAbsPath(argv[++argind]); #if UNUSED && KEEP if (chdir(app->home) < 0) { mprError("%s: Can't change directory to %s", mprGetAppName(), app->home); exit(4); } #endif } else if (smatch(argp, "--log") || smatch(argp, "-l")) { if (argind >= argc) { usageError(); } logSpec = argv[++argind]; } else if (smatch(argp, "--name") || smatch(argp, "-n")) { if (argind >= argc) { usageError(); } mprSetAppName(argv[++argind], 0, 0); } else if (smatch(argp, "--threads")) { if (argind >= argc) { usageError(); } app->workers = atoi(argv[++argind]); } else if (smatch(argp, "--verbose") || smatch(argp, "-v")) { verbose++; } else if (smatch(argp, "--version") || smatch(argp, "-V")) { mprPrintf("%s %s-%s\n", mprGetAppTitle(), BLD_VERSION, BLD_NUMBER); exit(0); } else { mprError("Unknown switch \"%s\"", argp); usageError(); exit(5); } } if (logSpec) { mprStartLogging(logSpec, 1); mprSetCmdlineLogging(1); } else if (verbose) { mprStartLogging(sfmt("stderr:%d", verbose + 1), 1); mprSetCmdlineLogging(1); } if (mprStart() < 0) { mprUserError("Can't start MPR for %s", mprGetAppName()); mprDestroy(MPR_EXIT_DEFAULT); return MPR_ERR_CANT_INITIALIZE; } if (checkEnvironment(argv[0]) < 0) { exit(6); } if (argc > argind) { if (argc > (argind + 2)) { usageError(); } ipAddrPort = argv[argind++]; if (argc > argind) { app->documents = sclone(argv[argind++]); } mprParseSocketAddress(ipAddrPort, &ip, &port, HTTP_DEFAULT_PORT); } else if (findConfigFile() < 0) { exit(7); } if (jail && changeRoot(jail) < 0) { exit(8); } if (initialize(ip, port) < 0) { return MPR_ERR_CANT_INITIALIZE; } if (maStartAppweb(app->appweb) < 0) { mprUserError("Can't start HTTP service, exiting."); exit(9); } /* Service I/O events until instructed to exit */ while (!mprIsStopping()) { mprServiceEvents(-1, 0); } status = mprGetExitStatus(); mprLog(1, "Stopping Appweb ..."); maStopAppweb(app->appweb); mprDestroy(MPR_EXIT_DEFAULT); return status; }
/* The http timer does maintenance activities and will fire per second while there are active requests. This routine will also be called by httpTerminate with event == 0 to signify a shutdown. NOTE: Because we lock the http here, connections cannot be deleted while we are modifying the list. */ static void httpTimer(Http *http, MprEvent *event) { HttpConn *conn; HttpStage *stage; HttpLimits *limits; MprModule *module; int next, active, abort; updateCurrentDate(); /* Check for any inactive connections or expired requests (inactivityTimeout and requestTimeout) OPT - could check for expired connections every 10 seconds. */ lock(http->connections); for (active = 0, next = 0; (conn = mprGetNextItem(http->connections, &next)) != 0; active++) { limits = conn->limits; if (!conn->timeoutEvent) { abort = mprIsStopping(); if (httpServerConn(conn) && (HTTP_STATE_CONNECTED < conn->state && conn->state < HTTP_STATE_PARSED) && (http->now - conn->started) > limits->requestParseTimeout) { conn->timeout = HTTP_PARSE_TIMEOUT; abort = 1; } else if ((http->now - conn->lastActivity) > limits->inactivityTimeout) { conn->timeout = HTTP_INACTIVITY_TIMEOUT; abort = 1; } else if ((http->now - conn->started) > limits->requestTimeout) { conn->timeout = HTTP_REQUEST_TIMEOUT; abort = 1; } else if (!event) { /* Called directly from httpStop to stop connections */ if (MPR->exitTimeout > 0) { if (conn->state == HTTP_STATE_COMPLETE || (HTTP_STATE_CONNECTED < conn->state && conn->state < HTTP_STATE_PARSED)) { abort = 1; } } else { abort = 1; } } if (abort && !mprGetDebugMode()) { httpScheduleConnTimeout(conn); } } } /* Check for unloadable modules OPT - could check for modules every minute */ if (mprGetListLength(http->connections) == 0) { for (next = 0; (module = mprGetNextItem(MPR->moduleService->modules, &next)) != 0; ) { if (module->timeout) { if (module->lastActivity + module->timeout < http->now) { mprLog("info http", 2, "Unloading inactive module %s", module->name); if ((stage = httpLookupStage(module->name)) != 0) { if (mprUnloadModule(module) < 0) { active++; } else { stage->flags |= HTTP_STAGE_UNLOADED; } } else { mprUnloadModule(module); } } else { active++; } } } } httpPruneMonitors(); if (active == 0 || mprIsStopping()) { if (event) { mprRemoveEvent(event); } http->timer = 0; /* Going to sleep now, so schedule a GC to free as much as possible. */ mprGC(MPR_GC_FORCE | MPR_GC_NO_BLOCK); } else { mprGC(MPR_GC_NO_BLOCK); } unlock(http->connections); }
MAIN(appweb, int argc, char **argv, char **envp) { Mpr *mpr; cchar *argp, *jail; char *logSpec; int argind, status, verbose; jail = 0; verbose = 0; logSpec = 0; if ((mpr = mprCreate(argc, argv, MPR_USER_EVENTS_THREAD)) == NULL) { exit(1); } mprSetAppName(BIT_PRODUCT, BIT_TITLE, BIT_VERSION); /* Allocate the top level application object. ManageApp is the GC manager function and is called by the GC to mark references in the app object. */ if ((app = mprAllocObj(AppwebApp, manageApp)) == NULL) { exit(2); } mprAddRoot(app); mprAddStandardSignals(); app->mpr = mpr; app->configFile = sclone("appweb.conf"); app->home = mprGetCurrentPath(); app->documents = app->home; argc = mpr->argc; argv = (char**) mpr->argv; for (argind = 1; argind < argc; argind++) { argp = argv[argind]; if (*argp != '-') { break; } if (smatch(argp, "--config") || smatch(argp, "--conf")) { if (argind >= argc) { usageError(); } app->configFile = sclone(argv[++argind]); #if BIT_UNIX_LIKE } else if (smatch(argp, "--chroot")) { if (argind >= argc) { usageError(); } jail = mprGetAbsPath(argv[++argind]); #endif } else if (smatch(argp, "--debugger") || smatch(argp, "-D")) { mprSetDebugMode(1); } else if (smatch(argp, "--exe")) { if (argind >= argc) { usageError(); } mpr->argv[0] = mprGetAbsPath(argv[++argind]); mprSetAppPath(mpr->argv[0]); mprSetModuleSearchPath(NULL); } else if (smatch(argp, "--home")) { if (argind >= argc) { usageError(); } app->home = mprGetAbsPath(argv[++argind]); if (chdir(app->home) < 0) { mprError("%s: Cannot change directory to %s", mprGetAppName(), app->home); exit(4); } } else if (smatch(argp, "--log") || smatch(argp, "-l")) { if (argind >= argc) { usageError(); } logSpec = argv[++argind]; } else if (smatch(argp, "--name") || smatch(argp, "-n")) { if (argind >= argc) { usageError(); } mprSetAppName(argv[++argind], 0, 0); } else if (smatch(argp, "--verbose") || smatch(argp, "-v")) { verbose++; } else if (smatch(argp, "--version") || smatch(argp, "-V")) { mprPrintf("%s-%s\n", BIT_VERSION, BIT_BUILD_NUMBER); exit(0); } else { if (!smatch(argp, "?")) { mprError("Unknown switch \"%s\"", argp); } usageError(); exit(5); } } if (logSpec) { mprStartLogging(logSpec, 1); mprSetCmdlineLogging(1); } else if (verbose) { mprStartLogging(sfmt("stderr:%d", verbose + 1), 1); mprSetCmdlineLogging(1); } /* Start the multithreaded portable runtime (MPR) */ if (mprStart() < 0) { mprError("Cannot start MPR for %s", mprGetAppName()); mprDestroy(MPR_EXIT_DEFAULT); return MPR_ERR_CANT_INITIALIZE; } if (checkEnvironment(argv[0]) < 0) { exit(6); } if (findAppwebConf() < 0) { exit(7); } if (jail && changeRoot(jail) < 0) { exit(8); } /* Open the sockets to listen on */ if (createEndpoints(argc - argind, &argv[argind]) < 0) { return MPR_ERR_CANT_INITIALIZE; } /* Start HTTP services */ if (maStartAppweb(app->appweb) < 0) { mprError("Cannot start HTTP service, exiting."); exit(9); } /* Service I/O events until instructed to exit */ while (!mprIsStopping()) { mprServiceEvents(-1, 0); } status = mprGetExitStatus(); mprLog(1, "Stopping Appweb ..."); maStopAppweb(app->appweb); mprDestroy(MPR_EXIT_DEFAULT); return status; }