static void JimAioDelProc(Jim_Interp *interp, void *privData) { AioFile *af = privData; JIM_NOTUSED(interp); if (!(af->OpenFlags & AIO_KEEPOPEN)) fclose(af->fp); if (!af->OpenFlags == AIO_FDOPEN) // fp = fdopen(fd) !! close(af->fd); if (af->rEvent) { // remove existing EventHandlers fprintf(stderr,"deleting ReadEvent\n"); Jim_DeleteFileHandler(interp,af->fp); Jim_DecrRefCount(interp,af->rEvent); } if (af->wEvent) { fprintf(stderr,"deleting WriteEvent\n"); Jim_DeleteFileHandler(interp,af->fp); Jim_DecrRefCount(interp,af->wEvent); } if (af->eEvent) { fprintf(stderr,"deleting ExceptionEvent\n"); Jim_DeleteFileHandler(interp,af->fp); Jim_DecrRefCount(interp,af->eEvent); } Jim_Free(af); }
static void JimAioDelProc(Jim_Interp *interp, void *privData) { AioFile *af = privData; JIM_NOTUSED(interp); if (!(af->OpenFlags & AIO_KEEPOPEN)) { fclose(af->fp); } Jim_DecrRefCount(interp, af->filename); #ifdef jim_ext_eventloop /* remove existing EventHandlers */ if (af->rEvent) { Jim_DeleteFileHandler(interp, af->fp); } if (af->wEvent) { Jim_DeleteFileHandler(interp, af->fp); } if (af->eEvent) { Jim_DeleteFileHandler(interp, af->fp); } #endif Jim_Free(af); }
static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj, int argc, Jim_Obj * const *argv) { int scriptlen = 0; if (argc == 0) { /* Return current script */ if (*scriptHandlerObj) { Jim_SetResult(interp, *scriptHandlerObj); } return JIM_OK; } if (*scriptHandlerObj) { /* Delete old handler */ Jim_DeleteFileHandler(interp, af->fp); *scriptHandlerObj = NULL; } /* Now possibly add the new script(s) */ Jim_GetString(argv[0], &scriptlen); if (scriptlen == 0) { /* Empty script, so done */ return JIM_OK; } /* A new script to add */ Jim_IncrRefCount(argv[0]); *scriptHandlerObj = argv[0]; Jim_CreateFileHandler(interp, af->fp, mask, JimAioFileEventHandler, *scriptHandlerObj, JimAioFileEventFinalizer); return JIM_OK; }
/* Calls to [aio.file] create commands that are implemented by this * C command. */ static int JimAioHandlerCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); int option; const char *options[] = { "close", "seek", "tell", "gets", "read", "puts", "flush", "eof", "ndelay", "readable", "writable", "onexception", "accept", NULL }; enum {OPT_CLOSE, OPT_SEEK, OPT_TELL, OPT_GETS, OPT_READ, OPT_PUTS, OPT_FLUSH, OPT_EOF, OPT_NDELAY, OPT_READABLE, OPT_WRITABLE, OPT_EXCEPTION, OPT_ACCEPT }; if (argc < 2) { Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?"); return JIM_ERR; } if (Jim_GetEnum(interp, argv[1], options, &option, "AIO method", JIM_ERRMSG) != JIM_OK) return JIM_ERR; /* CLOSE */ if (option == OPT_CLOSE) { if (argc != 2) { Jim_WrongNumArgs(interp, 2, argv, ""); return JIM_ERR; } Jim_DeleteCommand(interp, Jim_GetString(argv[0], NULL)); return JIM_OK; } else if (option == OPT_SEEK) { /* SEEK */ int orig = SEEK_SET; long offset; if (argc != 3 && argc != 4) { Jim_WrongNumArgs(interp, 2, argv, "offset ?origin?"); return JIM_ERR; } if (argc == 4) { if (Jim_CompareStringImmediate(interp, argv[3], "start")) orig = SEEK_SET; else if (Jim_CompareStringImmediate(interp, argv[3], "current")) orig = SEEK_CUR; else if (Jim_CompareStringImmediate(interp, argv[3], "end")) orig = SEEK_END; else { Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); Jim_AppendStrings(interp, Jim_GetResult(interp), "bad origin \"", Jim_GetString(argv[3], NULL), "\" must be: start, current, or end", NULL); return JIM_ERR; } } if (Jim_GetLong(interp, argv[2], &offset) != JIM_OK) return JIM_ERR; if (fseek(af->fp, offset, orig) == -1) { JimAioSetError(interp); return JIM_ERR; } return JIM_OK; } else if (option == OPT_TELL) { /* TELL */ long position; if (argc != 2) { Jim_WrongNumArgs(interp, 2, argv, ""); return JIM_ERR; } position = ftell(af->fp); Jim_SetResult(interp, Jim_NewIntObj(interp, position)); return JIM_OK; } else if (option == OPT_GETS) { /* GETS */ char buf[AIO_BUF_LEN]; Jim_Obj *objPtr; if (argc != 2 && argc != 3) { Jim_WrongNumArgs(interp, 2, argv, "?varName?"); return JIM_ERR; } objPtr = Jim_NewStringObj(interp, NULL, 0); while (1) { int more = 0; buf[AIO_BUF_LEN-1] = '_'; if (fgets(buf, AIO_BUF_LEN, af->fp) == NULL) break; if (buf[AIO_BUF_LEN-1] == '\0' && buf[AIO_BUF_LEN-2] != '\n') more = 1; if (more) { Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN-1); } else { /* strip "\n" */ Jim_AppendString(interp, objPtr, buf, strlen(buf)-1); } if (!more) break; } if (ferror(af->fp) && (errno != EAGAIN)) { /* I/O error */ Jim_IncrRefCount(objPtr); Jim_DecrRefCount(interp, objPtr); JimAioSetError(interp); return JIM_ERR; } /* On EOF returns -1 if varName was specified, or the empty string. */ if (feof(af->fp) && Jim_Length(objPtr) == 0) { Jim_IncrRefCount(objPtr); Jim_DecrRefCount(interp, objPtr); if (argc == 3) Jim_SetResult(interp, Jim_NewIntObj(interp, -1)); return JIM_OK; } if (argc == 3) { int totLen; Jim_GetString(objPtr, &totLen); if (Jim_SetVariable(interp, argv[2], objPtr) != JIM_OK) { Jim_IncrRefCount(objPtr); Jim_DecrRefCount(interp, objPtr); return JIM_ERR; } Jim_SetResult(interp, Jim_NewIntObj(interp, totLen)); } else { Jim_SetResult(interp, objPtr); } return JIM_OK; } else if (option == OPT_READ) { /* READ */ char buf[AIO_BUF_LEN]; Jim_Obj *objPtr; int nonewline = 0; int neededLen = -1; /* -1 is "read as much as possible" */ if (argc != 2 && argc != 3) { Jim_WrongNumArgs(interp, 2, argv, "?-nonewline? ?len?"); return JIM_ERR; } if (argc == 3 && Jim_CompareStringImmediate(interp, argv[2], "-nonewline")) { nonewline = 1; argv++; argc--; } if (argc == 3) { jim_wide wideValue; if (Jim_GetWide(interp, argv[2], &wideValue) != JIM_OK) return JIM_ERR; if (wideValue < 0) { Jim_SetResultString(interp, "invalid parameter: negative len", -1); return JIM_ERR; } neededLen = (int) wideValue; } objPtr = Jim_NewStringObj(interp, NULL, 0); while (neededLen != 0) { int retval; int readlen; if (neededLen == -1) { readlen = AIO_BUF_LEN; } else { readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen); } retval = fread(buf, 1, readlen, af->fp); if (retval > 0) { Jim_AppendString(interp, objPtr, buf, retval); if (neededLen != -1) { neededLen -= retval; } } if (retval != readlen) break; } /* Check for error conditions */ if (ferror(af->fp)) { /* I/O error */ Jim_FreeNewObj(interp, objPtr); JimAioSetError(interp); return JIM_ERR; } if (nonewline) { int len; const char *s = Jim_GetString(objPtr, &len); if (len > 0 && s[len-1] == '\n') { objPtr->length--; objPtr->bytes[objPtr->length] = '\0'; } } Jim_SetResult(interp, objPtr); return JIM_OK; } else if (option == OPT_PUTS) { /* PUTS */ int wlen; const char *wdata; if (argc != 3 && (argc != 4 || !Jim_CompareStringImmediate( interp, argv[2], "-nonewline"))) { Jim_WrongNumArgs(interp, 2, argv, "?-nonewline? string"); return JIM_ERR; } wdata = Jim_GetString(argv[2+(argc==4)], &wlen); if (fwrite(wdata, 1, wlen, af->fp) != (unsigned)wlen || (argc == 3 && fwrite("\n", 1, 1, af->fp) != 1)) { JimAioSetError(interp); return JIM_ERR; } return JIM_OK; } else if (option == OPT_FLUSH) { /* FLUSH */ if (argc != 2) { Jim_WrongNumArgs(interp, 2, argv, ""); return JIM_ERR; } if (fflush(af->fp) == EOF) { JimAioSetError(interp); return JIM_ERR; } return JIM_OK; } else if (option == OPT_EOF) { /* EOF */ if (argc != 2) { Jim_WrongNumArgs(interp, 2, argv, ""); return JIM_ERR; } Jim_SetResult(interp, Jim_NewIntObj(interp, feof(af->fp))); return JIM_OK; } else if (option == OPT_NDELAY) { #ifdef O_NDELAY int fmode = af->flags; if (argc == 3) { jim_wide wideValue; if (Jim_GetWide(interp, argv[2], &wideValue) != JIM_OK) return JIM_ERR; switch (wideValue) { case 0: fmode &= ~O_NDELAY; break ; case 1: fmode |= O_NDELAY; break ; } fcntl(af->fd,F_SETFL,fmode); af->flags = fmode; } Jim_SetResult(interp, Jim_NewIntObj(interp, (fmode & O_NONBLOCK)?1:0)); return JIM_OK; #else return JIM_ERR; #endif } else if ( (option == OPT_READABLE) || (option == OPT_WRITABLE) || (option == OPT_EXCEPTION) ) { int mask = 0; Jim_Obj **scrListObjpp = NULL; Jim_Obj *listObj; const char *dummy = NULL; int scrlen = 0; if (!(Jim_CreateFileHandler && Jim_DeleteFileHandler)) { Jim_SetResultString(interp, "Eventloop not present ( or loaded too late ) !", -1); return JIM_ERR; } switch (option) { case OPT_READABLE: mask = JIM_EVENT_READABLE; scrListObjpp = &af->rEvent; if (argc == 4) mask |= JIM_EVENT_FEOF ; break; case OPT_WRITABLE: mask = JIM_EVENT_WRITABLE; scrListObjpp = &af->wEvent; break; case OPT_EXCEPTION: mask = JIM_EVENT_EXCEPTION; scrListObjpp = &af->eEvent; break; } switch (argc) { case 4: case 3: if (*scrListObjpp) { Jim_DeleteFileHandler(interp, af->fp); //,mask); Jim_DecrRefCount(interp, *scrListObjpp); *scrListObjpp = NULL; } if ( dummy = Jim_GetString(argv[2],&scrlen),(scrlen == 0)) { break; } else { *scrListObjpp = Jim_NewListObj(interp, NULL, 0); Jim_IncrRefCount(*scrListObjpp); // fprintf(stderr,"0 %p \n",*scrListObjpp); listObj = argv[2]; if (Jim_IsShared(listObj)) listObj = Jim_DuplicateObj(interp, listObj); // Jim_IncrRefCount(listObj); // fprintf(stderr,"script:\"%s\" argp: %p objp1: %p\n", Jim_GetString(argv[2], NULL),argv[2],listObj); // fprintf(stderr,"1"); Jim_ListAppendElement(interp,*scrListObjpp,listObj); // fprintf(stderr,"2"); if (mask & JIM_EVENT_FEOF) { listObj = argv[3]; if (Jim_IsShared(listObj)) listObj = Jim_DuplicateObj(interp, listObj); // Jim_IncrRefCount(listObj); // fprintf(stderr,"script:\"%s\" argp: %p objp2: %p\n", Jim_GetString(argv[3], NULL),argv[3],listObj); // fprintf(stderr,"3"); Jim_ListAppendElement(interp,*scrListObjpp,listObj); // fprintf(stderr,"4"); } // fprintf(stderr,"event readable fd: %d, script:\"%s\" objp3: %p\n",af->fd, Jim_GetString(argv[2], NULL),argv[2]); Jim_IncrRefCount(*scrListObjpp); // fprintf(stderr,"6 %p \n",Jim_CreateFileHandler); Jim_CreateFileHandler(interp, af->fp, mask, JimAioFileEventHandler, *scrListObjpp, JimAioFileEventFinalizer); // fprintf(stderr,"7"); } break; case 2: if (*scrListObjpp) Jim_SetResult(interp,*scrListObjpp); return JIM_OK; default: Jim_WrongNumArgs(interp, 2, argv, ""); return JIM_ERR; } } else if (option == OPT_ACCEPT) { int ret; fprintf(stderr,"ACCEPT\n"); ret = JimAioAcceptHelper(interp,af); fprintf(stderr,"ret %d\n",ret); return (ret); } return JIM_OK; }
/* Process every pending time event, then every pending file event * (that may be registered by time event callbacks just processed). * The behaviour depends upon the setting of flags: * * If flags is 0, the function does nothing and returns. * if flags has JIM_ALL_EVENTS set, all event types are processed. * if flags has JIM_FILE_EVENTS set, file events are processed. * if flags has JIM_TIME_EVENTS set, time events are processed. * if flags has JIM_DONT_WAIT set, the function returns as soon as all * the events that are possible to process without waiting are processed. * * Returns the number of events processed or -1 if * there are no matching handlers, or -2 on error. */ int Jim_ProcessEvents(Jim_Interp *interp, int flags) { jim_wide sleep_us = -1; int processed = 0; Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); Jim_FileEvent *fe = eventLoop->fileEventHead; Jim_TimeEvent *te; jim_wide maxId; if ((flags & JIM_FILE_EVENTS) == 0 || fe == NULL) { /* No file events */ if ((flags & JIM_TIME_EVENTS) == 0 || eventLoop->timeEventHead == NULL) { /* No time events */ return -1; } } /* Note that we want call select() even if there are no * file events to process as long as we want to process time * events, in order to sleep until the next time event is ready * to fire. */ if (flags & JIM_DONT_WAIT) { /* Wait no time */ sleep_us = 0; } else if (flags & JIM_TIME_EVENTS) { /* The nearest timer is always at the head of the list */ if (eventLoop->timeEventHead) { Jim_TimeEvent *shortest = eventLoop->timeEventHead; /* Calculate the time missing for the nearest * timer to fire. */ sleep_us = shortest->when - JimGetTimeUsec(eventLoop); if (sleep_us < 0) { sleep_us = 0; } } else { /* Wait forever */ sleep_us = -1; } } #ifdef HAVE_SELECT if (flags & JIM_FILE_EVENTS) { int retval; struct timeval tv, *tvp = NULL; fd_set rfds, wfds, efds; int maxfd = -1; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); /* Check file events */ while (fe != NULL) { if (fe->mask & JIM_EVENT_READABLE) FD_SET(fe->fd, &rfds); if (fe->mask & JIM_EVENT_WRITABLE) FD_SET(fe->fd, &wfds); if (fe->mask & JIM_EVENT_EXCEPTION) FD_SET(fe->fd, &efds); if (maxfd < fe->fd) maxfd = fe->fd; fe = fe->next; } if (sleep_us >= 0) { tvp = &tv; tvp->tv_sec = sleep_us / 1000000; tvp->tv_usec = sleep_us % 1000000; } retval = select(maxfd + 1, &rfds, &wfds, &efds, tvp); if (retval < 0) { if (errno == EINVAL) { /* This can happen on mingw32 if a non-socket filehandle is passed */ Jim_SetResultString(interp, "non-waitable filehandle", -1); return -2; } } else if (retval > 0) { fe = eventLoop->fileEventHead; while (fe != NULL) { int mask = 0; int fd = fe->fd; if ((fe->mask & JIM_EVENT_READABLE) && FD_ISSET(fd, &rfds)) mask |= JIM_EVENT_READABLE; if (fe->mask & JIM_EVENT_WRITABLE && FD_ISSET(fd, &wfds)) mask |= JIM_EVENT_WRITABLE; if (fe->mask & JIM_EVENT_EXCEPTION && FD_ISSET(fd, &efds)) mask |= JIM_EVENT_EXCEPTION; if (mask) { int ret = fe->fileProc(interp, fe->clientData, mask); if (ret != JIM_OK && ret != JIM_RETURN) { /* Remove the element on handler error */ Jim_DeleteFileHandler(interp, fd, mask); /* At this point fe is no longer valid - it will be assigned below */ } processed++; /* After an event is processed our file event list * may no longer be the same, so what we do * is to clear the bit for this file descriptor and * restart again from the head. */ FD_CLR(fd, &rfds); FD_CLR(fd, &wfds); FD_CLR(fd, &efds); fe = eventLoop->fileEventHead; } else { fe = fe->next; } } } } #else if (sleep_us > 0) { usleep(sleep_us); } #endif /* Check time events */ te = eventLoop->timeEventHead; maxId = eventLoop->timeEventNextId; while (te) { jim_wide id; if (te->id > maxId) { te = te->next; continue; } if (JimGetTimeUsec(eventLoop) >= te->when) { id = te->id; /* Remove from the list before executing */ Jim_RemoveTimeHandler(eventLoop, id); te->timeProc(interp, te->clientData); /* After an event is processed our time event list may * no longer be the same, so we restart from head. * Still we make sure to don't process events registered * by event handlers itself in order to don't loop forever * even in case an [after 0] that continuously register * itself. To do so we saved the max ID we want to handle. */ Jim_FreeTimeHandler(interp, te); te = eventLoop->timeEventHead; processed++; } else { te = te->next; } } return processed; }
/* Process every pending time event, then every pending file event * (that may be registered by time event callbacks just processed). * Without special flags the function sleeps until some file event * fires, or when the next time event occurrs (if any). * * If flags is 0, the function does nothing and returns. * if flags has JIM_ALL_EVENTS set, all the kind of events are processed. * if flags has JIM_FILE_EVENTS set, file events are processed. * if flags has JIM_TIME_EVENTS set, time events are processed. * if flags has JIM_DONT_WAIT set the function returns ASAP until all * the events that's possible to process without to wait are processed. * * The function returns the number of events processed. */ int Jim_ProcessEvents(Jim_Interp *interp, int flags) { int maxfd = 0, numfd = 0, processed = 0; fd_set rfds, wfds, efds; Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); Jim_FileEvent *fe = eventLoop->fileEventHead; Jim_TimeEvent *te; jim_wide maxId; JIM_NOTUSED(flags); FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); /* Check file events */ while (fe != NULL) { int fd = fileno((FILE*)fe->handle); if (fe->mask & JIM_EVENT_READABLE) FD_SET(fd, &rfds); if (fe->mask & JIM_EVENT_WRITABLE) FD_SET(fd, &wfds); if (fe->mask & JIM_EVENT_EXCEPTION) FD_SET(fd, &efds); if (maxfd < fd) maxfd = fd; numfd++; fe = fe->next; } /* Note that we want call select() even if there are no * file events to process as long as we want to process time * events, in order to sleep until the next time event is ready * to fire. */ if (numfd || ((flags & JIM_TIME_EVENTS) && !(flags & JIM_DONT_WAIT))) { int retval; Jim_TimeEvent *shortest; struct timeval tv, *tvp; jim_wide dt; shortest = JimSearchNearestTimer(eventLoop); if (shortest) { long now_sec, now_ms; /* Calculate the time missing for the nearest * timer to fire. */ JimGetTime(&now_sec, &now_ms); tvp = &tv; dt = 1000 * (shortest->when_sec - now_sec); dt += ( shortest->when_ms - now_ms); if (dt < 0) { dt = 1; } tvp->tv_sec = dt / 1000; tvp->tv_usec = dt % 1000; // fprintf(stderr,"Next %d.% 8d\n",(int)tvp->tv_sec,(int)tvp->tv_usec); } else { tvp = NULL; /* wait forever */ // fprintf(stderr,"No Event\n"); } retval = select(maxfd+1, &rfds, &wfds, &efds, tvp); if (retval < 0) { switch (errno) { case EINTR: fprintf(stderr,"select EINTR\n"); break; case EINVAL: fprintf(stderr,"select EINVAL\n"); break; case ENOMEM: fprintf(stderr,"select ENOMEM\n"); break; } } else if (retval > 0) { fe = eventLoop->fileEventHead; while(fe != NULL) { int fd = fileno((FILE*)fe->handle); // fprintf(stderr,"fd: %d mask: %02x \n",fd,fe->mask); if ((fe->mask & JIM_EVENT_READABLE && FD_ISSET(fd, &rfds)) || (fe->mask & JIM_EVENT_WRITABLE && FD_ISSET(fd, &wfds)) || (fe->mask & JIM_EVENT_EXCEPTION && FD_ISSET(fd, &efds))) { int mask = 0; if (fe->mask & JIM_EVENT_READABLE && FD_ISSET(fd, &rfds)) { mask |= JIM_EVENT_READABLE; if ((fe->mask & JIM_EVENT_FEOF) && feof((FILE *)fe->handle)) mask |= JIM_EVENT_FEOF; } if (fe->mask & JIM_EVENT_WRITABLE && FD_ISSET(fd, &wfds)) mask |= JIM_EVENT_WRITABLE; if (fe->mask & JIM_EVENT_EXCEPTION && FD_ISSET(fd, &efds)) mask |= JIM_EVENT_EXCEPTION; if (fe->fileProc(interp, fe->clientData, mask) == JIM_ERR) { /* Remove the element on handler error */ Jim_DeleteFileHandler(interp, fe->handle); } processed++; /* After an event is processed our file event list * may no longer be the same, so what we do * is to clear the bit for this file descriptor and * restart again from the head. */ fe = eventLoop->fileEventHead; FD_CLR(fd, &rfds); FD_CLR(fd, &wfds); FD_CLR(fd, &efds); } else { fe = fe->next; } } } } /* Check time events */ te = eventLoop->timeEventHead; maxId = eventLoop->timeEventNextId-1; while(te) { long now_sec, now_ms; jim_wide id; if (te->id > maxId) { te = te->next; continue; } JimGetTime(&now_sec, &now_ms); if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) { id = te->id; te->timeProc(interp, te->clientData); /* After an event is processed our time event list may * no longer be the same, so we restart from head. * Still we make sure to don't process events registered * by event handlers itself in order to don't loop forever * even in case an [after 0] that continuously register * itself. To do so we saved the max ID we want to handle. */ Jim_DeleteTimeHandler(interp, id); te = eventLoop->timeEventHead; } else { te = te->next; } } return processed; }