/* Run the queue service routines until there is no more work to be done. If flags & HTTP_BLOCK, this routine may block while yielding. Return true if actual work was done. */ PUBLIC bool httpServiceQueues(HttpConn *conn, int flags) { HttpQueue *q; bool workDone; workDone = 0; while (conn->state < HTTP_STATE_COMPLETE && (q = httpGetNextQueueForService(conn->serviceq)) != NULL) { if (q->servicing) { /* Called re-entrantly */ q->flags |= HTTP_QUEUE_RESERVICE; } else { assert(q->schedulePrev == q->scheduleNext); httpServiceQueue(q); workDone = 1; } if (mprNeedYield() && (flags & HTTP_BLOCK)) { mprYield(0); } } /* Always do a yield if requested even if there are no queues to service */ if (mprNeedYield() && (flags & HTTP_BLOCK)) { mprYield(0); } return workDone; }
/* Wait for I/O on all registered file descriptors. Timeout is in milliseconds. Return the number of events detected. */ PUBLIC void mprWaitForIO(MprWaitService *ws, MprTicks timeout) { struct epoll_event events[ME_MAX_EVENTS]; int nevents; if (timeout < 0 || timeout > MAXINT) { timeout = MAXINT; } #if ME_DEBUG if (mprGetDebugMode() && timeout > 30000) { timeout = 30000; } #endif if (ws->needRecall) { mprDoWaitRecall(ws); return; } mprYield(MPR_YIELD_STICKY); if ((nevents = epoll_wait(ws->epoll, events, sizeof(events) / sizeof(struct epoll_event), timeout)) < 0) { if (errno != EINTR) { mprLog("error mpr event", 0, "epoll returned %d, errno %d", nevents, mprGetOsError()); } } mprClearWaiting(); mprResetYield(); if (nevents > 0) { serviceIO(ws, events, nevents); } ws->wakeRequested = 0; }
static int blockingFileCopy(HttpConn *conn, cchar *path) { MprFile *file; char buf[MPR_BUFSIZE]; ssize bytes, nbytes, offset; int oldMode; file = mprOpenFile(path, O_RDONLY | O_BINARY, 0); if (file == 0) { mprError("Can't open %s", path); return MPR_ERR_CANT_OPEN; } mprAddRoot(file); oldMode = mprSetSocketBlockingMode(conn->sock, 1); while ((bytes = mprReadFile(file, buf, sizeof(buf))) > 0) { offset = 0; while (bytes > 0) { if ((nbytes = httpWriteBlock(conn->writeq, &buf[offset], bytes)) < 0) { mprCloseFile(file); mprRemoveRoot(file); return MPR_ERR_CANT_WRITE; } bytes -= nbytes; offset += nbytes; mprAssert(bytes >= 0); } mprYield(0); } httpFlushQueue(conn->writeq, 1); mprSetSocketBlockingMode(conn->sock, oldMode); mprCloseFile(file); mprRemoveRoot(file); return 0; }
/* Wait for I/O on all registered descriptors. Timeout is in milliseconds. Return the number of events serviced. Should only be called by the thread that calls mprServiceEvents */ PUBLIC void mprWaitForIO(MprWaitService *ws, MprTicks timeout) { MSG msg; assert(ws->hwnd); if (timeout < 0 || timeout > MAXINT) { timeout = MAXINT; } #if BIT_DEBUG if (mprGetDebugMode() && timeout > 30000) { timeout = 30000; } #endif if (ws->needRecall) { mprDoWaitRecall(ws); return; } SetTimer(ws->hwnd, 0, (UINT) timeout, NULL); mprYield(MPR_YIELD_STICKY); if (GetMessage(&msg, NULL, 0, 0) == 0) { mprResetYield(); mprTerminate(MPR_EXIT_DEFAULT, -1); } else { mprClearWaiting(); mprResetYield(); TranslateMessage(&msg); DispatchMessage(&msg); } ws->wakeRequested = 0; }
static void big() { int i; // This will emit ~39K (under the item limit) for (i = 0; i < 500; i++) { render("Line: %05d %s", i, "aaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccccddddddd<br/>\r\n"); mprYield(0); } }
static void sml() { int i; for (i = 0; i < 1; i++) { render("Line: %05d %s", i, "aaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccccddddddd<br/>\r\n"); mprYield(0); } render("{ when: %Ld, uri: '%s', query: '%s' }\r\n", mprGetTicks(), getUri(), getQuery()); }
static void huge() { int i; // This will emit ~390K (over the item limit) for (i = 0; i < 10000; i++) { render("Line: %05d %s", i, "aaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccccddddddd<br/>\r\n"); mprYield(0); } render("{ when: %Ld, uri: '%s', query: '%s' }\r\n", mprGetTicks(), getUri(), getQuery()); }
/* Wait for an event to occur on the dispatcher and service the event. This is not called by mprServiceEvents. The dispatcher may be "started" and owned by the thread, or it may be unowned. WARNING: the event may have already happened by the time this API is invoked. WARNING: this will enable GC while sleeping. */ PUBLIC int mprWaitForEvent(MprDispatcher *dispatcher, MprTicks timeout, int64 mark) { MprEventService *es; MprTicks expires, delay; int runEvents, changed; if (dispatcher == NULL) { dispatcher = MPR->dispatcher; } if (dispatcher->flags & MPR_DISPATCHER_DESTROYED) { return 0; } if ((runEvents = (dispatcher->owner == mprGetCurrentOsThread())) != 0) { /* Called from an event on a running dispatcher */ assert(isRunning(dispatcher) || (dispatcher->flags & MPR_DISPATCHER_DESTROYED)); if (dispatchEvents(dispatcher)) { return 0; } } es = MPR->eventService; es->now = mprGetTicks(); expires = timeout < 0 ? MPR_MAX_TIMEOUT : (es->now + timeout); if (expires < 0) { expires = MPR_MAX_TIMEOUT; } delay = expires - es->now; lock(es); delay = getDispatcherIdleTicks(dispatcher, delay); dispatcher->flags |= MPR_DISPATCHER_WAITING; changed = dispatcher->mark != mark && mark != -1; unlock(es); if (changed) { return 0; } mprYield(MPR_YIELD_STICKY); mprWaitForCond(dispatcher->cond, delay); mprResetYield(); es->now = mprGetTicks(); lock(es); dispatcher->flags &= ~MPR_DISPATCHER_WAITING; unlock(es); if (runEvents) { dispatchEvents(dispatcher); assert(isRunning(dispatcher) || (dispatcher->flags & MPR_DISPATCHER_DESTROYED)); } return 0; }
/* Windows only routine to wait for I/O on the channels to the gateway and the child process. This will queue events on the dispatcher queue when I/O occurs or the process dies. NOTE: NamedPipes cannot use WaitForMultipleEvents, so we dedicate a thread to polling. WARNING: this should not be called from a dispatcher other than cmd->dispatcher. */ static void pollWinCmd(MprCmd *cmd, MprTicks timeout) { MprTicks mark, delay; MprWaitHandler *wp; int i, rc, nbytes; mark = mprGetTicks(); if (cmd->stopped) { timeout = 0; } slock(cmd); for (i = MPR_CMD_STDOUT; i < MPR_CMD_MAX_PIPE; i++) { if (cmd->files[i].handle) { wp = cmd->handlers[i]; if (wp && wp->desiredMask & MPR_READABLE) { rc = PeekNamedPipe(cmd->files[i].handle, NULL, 0, NULL, &nbytes, NULL); if (rc && nbytes > 0 || cmd->process == 0) { wp->presentMask |= MPR_READABLE; mprQueueIOEvent(wp); } } } } if (cmd->files[MPR_CMD_STDIN].handle) { wp = cmd->handlers[MPR_CMD_STDIN]; if (wp && wp->desiredMask & MPR_WRITABLE) { wp->presentMask |= MPR_WRITABLE; mprQueueIOEvent(wp); } } if (cmd->process) { delay = (cmd->eofCount == cmd->requiredEof && cmd->files[MPR_CMD_STDIN].handle == 0) ? timeout : 0; do { mprYield(MPR_YIELD_STICKY); if (WaitForSingleObject(cmd->process, (DWORD) delay) == WAIT_OBJECT_0) { mprResetYield(); reapCmd(cmd, 0); break; } else { mprResetYield(); } delay = mprGetRemainingTicks(mark, timeout); } while (cmd->eofCount == cmd->requiredEof); } sunlock(cmd); }
/* Wait for I/O on a single file descriptor. Return a mask of events found. Mask is the events of interest. timeout is in milliseconds. */ PUBLIC int mprWaitForSingleIO(int fd, int mask, MprTicks timeout) { struct epoll_event ev, events[2]; int epfd, rc, result; if (timeout < 0 || timeout > MAXINT) { timeout = MAXINT; } memset(&ev, 0, sizeof(ev)); memset(events, 0, sizeof(events)); ev.data.fd = fd; if ((epfd = epoll_create(ME_MAX_EVENTS)) < 0) { mprLog("error mpr event", 0, "Epoll_create failed, errno=%d", errno); return MPR_ERR_CANT_INITIALIZE; } ev.events = 0; if (mask & MPR_READABLE) { ev.events = EPOLLIN | EPOLLHUP; } if (mask & MPR_WRITABLE) { ev.events = EPOLLOUT | EPOLLHUP; } if (ev.events) { epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); } mprYield(MPR_YIELD_STICKY); rc = epoll_wait(epfd, events, sizeof(events) / sizeof(struct epoll_event), timeout); mprResetYield(); close(epfd); result = 0; if (rc < 0) { mprLog("error mpr event", 0, "Epoll returned %d, errno %d", rc, errno); } else if (rc > 0) { if (rc > 0) { if ((events[0].events & (EPOLLIN | EPOLLERR | EPOLLHUP)) && (mask & MPR_READABLE)) { result |= MPR_READABLE; } if ((events[0].events & (EPOLLOUT | EPOLLHUP)) && (mask & MPR_WRITABLE)) { result |= MPR_WRITABLE; } } } return result; }
/* Read input from the console (stdin) */ static int consoleGets(EcStream *stream) { char prompt[MPR_MAX_STRING], *line, *cp; int level; if (stream->flags & EC_STREAM_EOL) { return 0; } level = stream->compiler->state ? stream->compiler->state->blockNestCount : 0; fmt(prompt, sizeof(prompt), "%s-%d> ", EJS_NAME, level); mprYield(MPR_YIELD_STICKY); line = readline(prompt); mprResetYield(); if (line == NULL) { stream->eof = 1; mprPrintf("\n"); return -1; } cp = strim(line, "\r\n", MPR_TRIM_BOTH); ecSetStreamBuf(stream, cp, slen(cp)); stream->flags |= EC_STREAM_EOL; return (int) slen(cp); }
PUBLIC void mprSleep(MprTicks timeout) { mprYield(MPR_YIELD_STICKY); mprNap(timeout); mprResetYield(); }
static ssize writeBody(HttpConn *conn, MprList *files) { MprFile *file; char buf[HTTP_BUFSIZE], *path, *pair; ssize bytes, len, count, nbytes, sofar; int next, oldMode; if (app->upload) { if (httpWriteUploadData(conn, app->files, app->formData) < 0) { mprError("Can't write upload data %s", httpGetError(conn)); return MPR_ERR_CANT_WRITE; } } else { if (app->formData) { count = mprGetListLength(app->formData); for (next = 0; (pair = mprGetNextItem(app->formData, &next)) != 0; ) { len = strlen(pair); if (next < count) { len = slen(pair); if (httpWrite(conn->writeq, pair, len) != len || httpWrite(conn->writeq, "&", 1) != 1) { return MPR_ERR_CANT_WRITE; } } else { if (httpWrite(conn->writeq, pair, len) != len) { return MPR_ERR_CANT_WRITE; } } } } if (files) { mprAssert(mprGetListLength(files) == 1); for (next = 0; (path = mprGetNextItem(files, &next)) != 0; ) { if (strcmp(path, "-") == 0) { file = mprAttachFileFd(0, "stdin", O_RDONLY | O_BINARY); } else { file = mprOpenFile(path, O_RDONLY | O_BINARY, 0); } if (file == 0) { mprError("Can't open \"%s\"", path); return MPR_ERR_CANT_OPEN; } app->inFile = file; if (app->verbose) { mprPrintf("uploading: %s\n", path); } oldMode = mprSetSocketBlockingMode(conn->sock, 1); while ((bytes = mprReadFile(file, buf, sizeof(buf))) > 0) { sofar = 0; while (bytes > 0) { if ((nbytes = httpWriteBlock(conn->writeq, &buf[sofar], bytes)) < 0) { mprCloseFile(file); return MPR_ERR_CANT_WRITE; } bytes -= nbytes; sofar += nbytes; mprAssert(bytes >= 0); } mprYield(0); } httpFlushQueue(conn->writeq, 1); mprSetSocketBlockingMode(conn->sock, oldMode); mprCloseFile(file); app->inFile = 0; } } if (app->bodyData) { mprAddNullToBuf(app->bodyData); len = mprGetBufLength(app->bodyData); if (httpWriteBlock(conn->writeq, mprGetBufStart(app->bodyData), len) != len) { return MPR_ERR_CANT_WRITE; } } } return 0; }
static int compileInner(EcCompiler *cp, int argc, char **argv) { Ejs *ejs; EjsModule *mp; MprList *nodes; EjsBlock *block; EcLocation loc; cchar *ext; char *msg; int next, i, j, nextModule, lflags, rc, paused; ejs = cp->ejs; if ((nodes = mprCreateList(-1, 0)) == 0) { return EJS_ERR; } cp->nodes = nodes; /* Warn about source files mentioned multiple times. TODO OPT. This is slow. */ for (i = 0; i < argc; i++) { for (j = 0; j < argc; j++) { if (i == j) { continue; } if (mprSamePath(argv[i], argv[j])) { compileError(cp, "Loading source %s multiple times. Ignoring extra copies.", argv[i]); return EJS_ERR; } } if (cp->outputFile && mprSamePath(cp->outputFile, argv[i])) { compileError(cp, "Output file is the same as input file: %s", argv[i]); return EJS_ERR; } } /* Compile source files and load any module files */ for (i = 0; i < argc && !cp->fatalError; i++) { ext = mprGetPathExt(argv[i]); if (scasecmp(ext, "mod") == 0 || scasecmp(ext, BIT_SHOBJ) == 0) { nextModule = mprGetListLength(ejs->modules); lflags = cp->strict ? EJS_LOADER_STRICT : 0; if ((rc = ejsLoadModule(cp->ejs, ejsCreateStringFromAsc(ejs, argv[i]), -1, -1, lflags)) < 0) { msg = sfmt("Error initializing module %s\n%s", argv[i], ejsGetErrorMsg(cp->ejs, 1)); memset(&loc, 0, sizeof(EcLocation)); loc.filename = sclone(argv[i]); if (rc == MPR_ERR_CANT_INITIALIZE) { ecError(cp, "Error", &loc, msg); } else { ecError(cp, "Error", &loc, msg); } cp->nodes = NULL; return EJS_ERR; } if (cp->merge) { /* If merging, we must emit the loaded module into the output. So add to the compiled modules list. */ for (next = nextModule; (mp = mprGetNextItem(ejs->modules, &next)) != 0; ) { if (mprLookupItem(cp->modules, mp) < 0 && mprAddItem(cp->modules, mp) < 0) { compileError(cp, "Can't add module %s", mp->name); } } } mprAddItem(nodes, 0); } else { mprAssert(!MPR->marking); paused = ejsBlockGC(ejs); mprAddItem(nodes, ecParseFile(cp, argv[i])); ejsUnblockGC(ejs, paused); } mprAssert(!MPR->marking); } mprAssert(ejs->result == 0 || (MPR_GET_GEN(MPR_GET_MEM(ejs->result)) != MPR->heap->dead)); /* Allocate the eval frame stack. This is used for property lookups. We have one dummy block at the top always. */ block = ejsCreateBlock(ejs, 0); mprSetName(block, "Compiler"); ejsPushBlock(ejs, block); /* Process the internal representation and generate code */ paused = ejsBlockGC(ejs); if (!cp->parseOnly && cp->errorCount == 0) { ecResetParser(cp); if (ecAstProcess(cp) < 0) { ejsPopBlock(ejs); cp->nodes = NULL; ejsUnblockGC(ejs, paused); return EJS_ERR; } if (cp->errorCount == 0) { ecResetParser(cp); if (ecCodeGen(cp) < 0) { ejsPopBlock(ejs); cp->nodes = NULL; ejsUnblockGC(ejs, paused); return EJS_ERR; } } } ejsPopBlock(ejs); mprAssert(ejs->result == 0 || (MPR_GET_GEN(MPR_GET_MEM(ejs->result)) != MPR->heap->dead)); /* Add compiled modules to the interpreter */ for (next = 0; ((mp = (EjsModule*) mprGetNextItem(cp->modules, &next)) != 0); ) { ejsAddModule(cp->ejs, mp); } cp->nodes = NULL; ejsUnblockGC(ejs, paused); if (!paused) { mprYield(0); } mprAssert(ejs->result == 0 || (MPR_GET_GEN(MPR_GET_MEM(ejs->result)) != MPR->heap->dead)); return (cp->errorCount > 0) ? EJS_ERR: 0; }