aboutsummaryrefslogtreecommitdiff
path: root/jim-eventloop.c
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2010-09-16 10:01:27 +1000
committerSteve Bennett <steveb@workware.net.au>2010-10-15 11:02:54 +1000
commitb4a77b8c3c18870009b5a2c193a1772552b5e4b5 (patch)
tree10bc85e5e1a702f07547b6cd0ee8fc077d03cd99 /jim-eventloop.c
parent1f3eccbfe50172710a1190bd1d13f03778d587a1 (diff)
downloadjimtcl-b4a77b8c3c18870009b5a2c193a1772552b5e4b5.zip
jimtcl-b4a77b8c3c18870009b5a2c193a1772552b5e4b5.tar.gz
jimtcl-b4a77b8c3c18870009b5a2c193a1772552b5e4b5.tar.bz2
eventloop improvements and enhancements
Move Jim_EvalObjBackground() out of the core to eventloop Time events are now kept and triggered in time order Time handlers are removed before execution Add 'update' Add 'after info' and 'after idle' Include time events in the return from Jim_ProcessEvents() Add Tcl eventloop tests Signed-off-by: Steve Bennett <steveb@workware.net.au>
Diffstat (limited to 'jim-eventloop.c')
-rw-r--r--jim-eventloop.c312
1 files changed, 252 insertions, 60 deletions
diff --git a/jim-eventloop.c b/jim-eventloop.c
index 0f34dd3..992b607 100644
--- a/jim-eventloop.c
+++ b/jim-eventloop.c
@@ -4,7 +4,7 @@
* Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
* Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
* Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net>
- * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com
+ * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com
* Copyright 2008 Andrew Lunn <andrew@lunn.ch>
* Copyright 2008 Duane Ellis <openocd@duaneellis.com>
* Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>
@@ -91,8 +91,49 @@ typedef struct Jim_EventLoop
jim_wide timeEventNextId;
Jim_FileEvent *fileEventHead;
Jim_TimeEvent *timeEventHead;
+ int suppress_bgerror; /* bgerror returned break, so don't call it again */
} Jim_EventLoop;
+static void JimAfterTimeHandler(Jim_Interp *interp, void *clientData);
+static void JimAfterTimeEventFinalizer(Jim_Interp *interp, void *clientData);
+
+int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
+{
+ Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
+ Jim_CallFrame *savedFramePtr;
+ int retval;
+
+ savedFramePtr = interp->framePtr;
+ interp->framePtr = interp->topFramePtr;
+ retval = Jim_EvalObj(interp, scriptObjPtr);
+ interp->framePtr = savedFramePtr;
+ /* Try to report the error (if any) via the bgerror proc */
+ if (retval != JIM_OK && !eventLoop->suppress_bgerror) {
+ Jim_Obj *objv[2];
+ int rc = JIM_ERR;
+
+ objv[0] = Jim_NewStringObj(interp, "bgerror", -1);
+ objv[1] = Jim_GetResult(interp);
+ Jim_IncrRefCount(objv[0]);
+ Jim_IncrRefCount(objv[1]);
+ if (Jim_GetCommand(interp, objv[0], JIM_NONE) == NULL || (rc = Jim_EvalObjVector(interp, 2, objv)) != JIM_OK) {
+ if (rc == JIM_BREAK) {
+ /* No more bgerror calls */
+ eventLoop->suppress_bgerror++;
+ }
+ else {
+ /* Report the error to stderr. */
+ fprintf(stderr, "Background error:" JIM_NL);
+ Jim_PrintErrorMessage(interp);
+ }
+ }
+ Jim_DecrRefCount(interp, objv[0]);
+ Jim_DecrRefCount(interp, objv[1]);
+ }
+ return retval;
+}
+
+
void Jim_CreateFileHandler(Jim_Interp *interp, FILE * handle, int mask,
Jim_FileProc * proc, void *clientData, Jim_EventFinalizerProc * finalizerProc)
{
@@ -147,7 +188,7 @@ jim_wide Jim_CreateTimeHandler(Jim_Interp *interp, jim_wide milliseconds,
{
Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
jim_wide id = eventLoop->timeEventNextId++;
- Jim_TimeEvent *te;
+ Jim_TimeEvent *te, *e, *prev;
long cur_sec, cur_ms;
JimGetTime(&cur_sec, &cur_ms);
@@ -165,61 +206,117 @@ jim_wide Jim_CreateTimeHandler(Jim_Interp *interp, jim_wide milliseconds,
te->timeProc = proc;
te->finalizerProc = finalizerProc;
te->clientData = clientData;
+
+ /* Add to the appropriate place in the list */
+ if (eventLoop->timeEventHead) {
+ prev = NULL;
+ for (e = eventLoop->timeEventHead; e; e = e->next) {
+ if (te->when_sec < e->when_sec || (te->when_sec == e->when_sec && te->when_ms < e->when_ms)) {
+ break;
+ }
+ prev = e;
+ }
+ if (prev) {
+ te->next = prev->next;
+ prev->next = te;
+ return id;
+ }
+ }
+
te->next = eventLoop->timeEventHead;
eventLoop->timeEventHead = te;
+
return id;
}
-jim_wide Jim_DeleteTimeHandler(Jim_Interp *interp, jim_wide id)
+static jim_wide JimParseAfterId(Jim_Obj *idObj)
{
- Jim_TimeEvent *te, *prev = NULL;
- Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
- long cur_sec, cur_ms;
- jim_wide remain;
+ int len;
+ const char *tok = Jim_GetString(idObj, &len);
+ jim_wide id;
- JimGetTime(&cur_sec, &cur_ms);
+ if (strncmp(tok, "after#", 6) == 0 && Jim_StringToWide(tok + 6, &id, 10) == JIM_OK) {
+ /* Got an event by id */
+ return id;
+ }
+ return -1;
+}
- te = eventLoop->timeEventHead;
- if (id >= eventLoop->timeEventNextId) {
- return -2; /* wrong event ID */
+static jim_wide JimFindAfterByScript(Jim_EventLoop *eventLoop, Jim_Obj *scriptObj)
+{
+ Jim_TimeEvent *te;
+
+ for (te = eventLoop->timeEventHead; te; te = te->next) {
+ /* Is this an 'after' event? */
+ if (te->timeProc == JimAfterTimeHandler) {
+ if (Jim_StringCompareObj(scriptObj, te->clientData, 0) == 0) {
+ return te->id;
+ }
+ }
}
- while (te) {
+ return -1; /* NO event with the specified ID found */
+}
+
+static Jim_TimeEvent *JimFindTimeHandlerById(Jim_EventLoop *eventLoop, jim_wide id)
+{
+ Jim_TimeEvent *te;
+
+ for (te = eventLoop->timeEventHead; te; te = te->next) {
if (te->id == id) {
- remain = (te->when_sec - cur_sec) * 1000;
- remain += (te->when_ms - cur_ms);
- remain = (remain < 0) ? 0 : remain;
+ return te;
+ }
+ }
+ return NULL;
+}
+
+static Jim_TimeEvent *Jim_RemoveTimeHandler(Jim_EventLoop *eventLoop, jim_wide id)
+{
+ Jim_TimeEvent *te, *prev = NULL;
+ for (te = eventLoop->timeEventHead; te; te = te->next) {
+ if (te->id == id) {
if (prev == NULL)
eventLoop->timeEventHead = te->next;
else
prev->next = te->next;
- if (te->finalizerProc)
- te->finalizerProc(interp, te->clientData);
- Jim_Free(te);
- return remain;
+ return te;
}
prev = te;
- te = te->next;
}
- return -1; /* NO event with the specified ID found */
+ return NULL;
}
-/* Search the first timer to fire.
- * This operation is useful to know how many time the select can be
- * put in sleep without to delay any event.
- * If there are no timers NULL is returned. */
-static Jim_TimeEvent *JimSearchNearestTimer(Jim_EventLoop * eventLoop)
+static void Jim_FreeTimeHandler(Jim_Interp *interp, Jim_TimeEvent *te)
{
- Jim_TimeEvent *te = eventLoop->timeEventHead;
- Jim_TimeEvent *nearest = NULL;
+ if (te->finalizerProc)
+ te->finalizerProc(interp, te->clientData);
+ Jim_Free(te);
+}
- while (te) {
- if (!nearest || te->when_sec < nearest->when_sec ||
- (te->when_sec == nearest->when_sec && te->when_ms < nearest->when_ms))
- nearest = te;
- te = te->next;
+jim_wide Jim_DeleteTimeHandler(Jim_Interp *interp, jim_wide id)
+{
+ Jim_TimeEvent *te;
+ Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
+
+ if (id >= eventLoop->timeEventNextId) {
+ return -2; /* wrong event ID */
}
- return nearest;
+
+ te = Jim_RemoveTimeHandler(eventLoop, id);
+ if (te) {
+ jim_wide remain;
+ long cur_sec, cur_ms;
+
+ JimGetTime(&cur_sec, &cur_ms);
+
+ remain = (te->when_sec - cur_sec) * 1000;
+ remain += (te->when_ms - cur_ms);
+ remain = (remain < 0) ? 0 : remain;
+
+ Jim_FreeTimeHandler(interp, te);
+ return remain;
+ }
+ return -1; /* NO event with the specified ID found */
}
/* --- POSIX version of Jim_ProcessEvents, for now the only available --- */
@@ -282,12 +379,18 @@ int Jim_ProcessEvents(Jim_Interp *interp, int flags)
* 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) {
+ if (flags & JIM_DONT_WAIT) {
+ /* Wait no time */
+ tvp = &tv;
+ tvp->tv_sec = 0;
+ tvp->tv_usec = 0;
+ }
+ /* The nearest timer is always at the head of the list */
+ else if (eventLoop->timeEventHead) {
+ Jim_TimeEvent *shortest = eventLoop->timeEventHead;
long now_sec, now_ms;
/* Calculate the time missing for the nearest
@@ -364,6 +467,8 @@ int Jim_ProcessEvents(Jim_Interp *interp, int flags)
JimGetTime(&now_sec, &now_ms);
if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) {
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.
@@ -371,8 +476,10 @@ int Jim_ProcessEvents(Jim_Interp *interp, int flags)
* 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);
+ Jim_FreeTimeHandler(interp, te);
+
te = eventLoop->timeEventHead;
+ processed++;
}
else {
te = te->next;
@@ -413,6 +520,7 @@ void JimELAssocDataDeleProc(Jim_Interp *interp, void *data)
static int JimELVwaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
+ Jim_EventLoop *eventLoop = Jim_CmdPrivData(interp);
Jim_Obj *oldValue;
if (argc != 2) {
@@ -420,8 +528,6 @@ static int JimELVwaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
return JIM_ERR;
}
- interp->suppress_bgerror = 0;
-
oldValue = Jim_GetGlobalVariable(interp, argv[1], JIM_NONE);
if (oldValue) {
Jim_IncrRefCount(oldValue);
@@ -434,6 +540,9 @@ static int JimELVwaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
return JIM_ERR;
}
}
+
+ eventLoop->suppress_bgerror = 0;
+
while (1) {
Jim_Obj *currValue;
@@ -456,14 +565,40 @@ static int JimELVwaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
return JIM_OK;
}
-void JimAfterTimeHandler(Jim_Interp *interp, void *clientData)
+static int JimELUpdateCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ Jim_EventLoop *eventLoop = Jim_CmdPrivData(interp);
+ static const char *options[] = {
+ "idletasks", NULL
+ };
+ enum { UPDATE_IDLE, UPDATE_NONE };
+ int option = UPDATE_NONE;
+ int flags = JIM_TIME_EVENTS;
+
+ if (argc == 1) {
+ flags = JIM_ALL_EVENTS;
+ }
+ else if (argc > 2 || Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
+ Jim_WrongNumArgs(interp, 1, argv, "?idletasks?");
+ return JIM_ERR;
+ }
+
+ eventLoop->suppress_bgerror = 0;
+
+ while (Jim_ProcessEvents(interp, flags | JIM_DONT_WAIT) > 0) {
+ }
+
+ return JIM_OK;
+}
+
+static void JimAfterTimeHandler(Jim_Interp *interp, void *clientData)
{
Jim_Obj *objPtr = clientData;
Jim_EvalObjBackground(interp, objPtr);
}
-void JimAfterTimeEventFinalizer(Jim_Interp *interp, void *clientData)
+static void JimAfterTimeEventFinalizer(Jim_Interp *interp, void *clientData)
{
Jim_Obj *objPtr = clientData;
@@ -472,23 +607,25 @@ void JimAfterTimeEventFinalizer(Jim_Interp *interp, void *clientData)
static int JimELAfterCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
- jim_wide ms, id;
+ Jim_EventLoop *eventLoop = Jim_CmdPrivData(interp);
+ jim_wide ms = 0, id;
Jim_Obj *objPtr, *idObjPtr;
const char *options[] = {
- "cancel", NULL
+ "cancel", "info", "idle", NULL
};
enum
- { AFTER_CANCEL, AFTER_INFO, AFTER_RESTART, AFTER_EXPIRE, AFTER_CREATE };
+ { AFTER_CANCEL, AFTER_INFO, AFTER_IDLE, AFTER_RESTART, AFTER_EXPIRE, AFTER_CREATE };
int option = AFTER_CREATE;
if (argc < 2) {
- Jim_WrongNumArgs(interp, 1, argv, "<after milliseconds> ?script|cancel <id>?");
+ Jim_WrongNumArgs(interp, 1, argv, "option ?arg ...?");
return JIM_ERR;
}
if (Jim_GetWide(interp, argv[1], &ms) != JIM_OK) {
- if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
+ if (Jim_GetEnum(interp, argv[1], options, &option, "argument", JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
+ Jim_SetEmptyResult(interp);
}
else if (argc == 2) {
/* Simply a sleep */
@@ -496,10 +633,18 @@ static int JimELAfterCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
usleep((ms % 1000) * 1000);
return JIM_OK;
}
+
switch (option) {
- case AFTER_CREATE:
- Jim_IncrRefCount(argv[2]);
- id = Jim_CreateTimeHandler(interp, ms, JimAfterTimeHandler, argv[2],
+ case AFTER_IDLE:
+ if (argc < 3) {
+ Jim_WrongNumArgs(interp, 2, argv, "script ?script ...?");
+ return JIM_ERR;
+ }
+ /* fall through */
+ case AFTER_CREATE: {
+ Jim_Obj *scriptObj = Jim_ConcatObj(interp, argc - 2, argv + 2);
+ Jim_IncrRefCount(scriptObj);
+ id = Jim_CreateTimeHandler(interp, ms, JimAfterTimeHandler, scriptObj,
JimAfterTimeEventFinalizer);
objPtr = Jim_NewStringObj(interp, NULL, 0);
Jim_AppendString(interp, objPtr, "after#", -1);
@@ -509,21 +654,66 @@ static int JimELAfterCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
Jim_DecrRefCount(interp, idObjPtr);
Jim_SetResult(interp, objPtr);
return JIM_OK;
- case AFTER_CANCEL:{
- int tlen;
+ }
+ case AFTER_CANCEL:
+ if (argc < 3) {
+ Jim_WrongNumArgs(interp, 2, argv, "id|command");
+ return JIM_ERR;
+ }
+ else {
jim_wide remain = 0;
- const char *tok = Jim_GetString(argv[2], &tlen);
- if (strncmp(tok, "after#", 6) == 0 && Jim_StringToWide(tok + 6, &id, 10) == JIM_OK) {
- remain = Jim_DeleteTimeHandler(interp, id);
- if (remain > -2) {
- Jim_SetResult(interp, Jim_NewIntObj(interp, remain));
+ id = JimParseAfterId(argv[2]);
+ if (id < 0) {
+ /* Not an event id, so search by script */
+ Jim_Obj *scriptObj = Jim_ConcatObj(interp, argc - 2, argv + 2);
+ id = JimFindAfterByScript(eventLoop, scriptObj);
+ Jim_FreeNewObj(interp, scriptObj);
+ if (id < 0) {
+ /* Not found */
+ break;
+ }
+ }
+ remain = Jim_DeleteTimeHandler(interp, id);
+ if (remain >= 0) {
+ Jim_SetResultInt(interp, remain);
+ }
+ }
+ break;
+
+ case AFTER_INFO:
+ if (argc == 2) {
+ Jim_TimeEvent *te = eventLoop->timeEventHead;
+ Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
+ char buf[30];
+
+ while (te) {
+ snprintf(buf, sizeof(buf), "after#%" JIM_WIDE_MODIFIER, te->id);
+ Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, buf, -1));
+ te = te->next;
+ }
+ Jim_SetResult(interp, listObj);
+ }
+ else if (argc == 3) {
+ jim_wide id = JimParseAfterId(argv[2]);
+ if (id >= 0) {
+ Jim_TimeEvent *e = JimFindTimeHandlerById(eventLoop, id);
+ if (e && e->timeProc == JimAfterTimeHandler) {
+ Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
+ Jim_ListAppendElement(interp, listObj, e->clientData);
+ Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, e->initialms ? "timer" : "idle", -1));
+ Jim_SetResult(interp, listObj);
return JIM_OK;
}
}
- Jim_SetResultString(interp, "invalid event", -1);
+ Jim_SetResultFormatted(interp, "event \"%#s\" doesn't exist", argv[2]);
return JIM_ERR;
}
+ else {
+ Jim_WrongNumArgs(interp, 2, argv, "?id?");
+ return JIM_ERR;
+ }
+ break;
}
return JIM_OK;
}
@@ -536,10 +726,12 @@ int Jim_eventloopInit(Jim_Interp *interp)
eventLoop->fileEventHead = NULL;
eventLoop->timeEventHead = NULL;
eventLoop->timeEventNextId = 1;
+ eventLoop->suppress_bgerror = 0;
Jim_SetAssocData(interp, "eventloop", JimELAssocDataDeleProc, eventLoop);
- Jim_CreateCommand(interp, "vwait", JimELVwaitCommand, NULL, NULL);
- Jim_CreateCommand(interp, "after", JimELAfterCommand, NULL, NULL);
+ Jim_CreateCommand(interp, "vwait", JimELVwaitCommand, eventLoop, NULL);
+ Jim_CreateCommand(interp, "update", JimELUpdateCommand, eventLoop, NULL);
+ Jim_CreateCommand(interp, "after", JimELAfterCommand, eventLoop, NULL);
return JIM_OK;
}