23115fe6713304f0dc246b85fba735e110dd24ad
[openocd.git] / src / helper / jim-eventloop.c
1 /* Jim - A small embeddable Tcl interpreter
2 *
3 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
4 * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
5 * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net>
6 * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com
7 * Copyright 2008 Andrew Lunn <andrew@lunn.ch>
8 * Copyright 2008 Duane Ellis <openocd@duaneellis.com>
9 * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>
10 *
11 * The FreeBSD license
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 *
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above
20 * copyright notice, this list of conditions and the following
21 * disclaimer in the documentation and/or other materials
22 * provided with the distribution.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
25 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
26 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * The views and conclusions contained in the software and documentation
38 * are those of the authors and should not be interpreted as representing
39 * official policies, either expressed or implied, of the Jim Tcl Project.
40 **/
41 /* TODO:
42 *
43 * - to really use flags in Jim_ProcessEvents()
44 * - more complete [after] command with [after info] and other subcommands.
45 * - Win32 port
46 */
47 #ifdef HAVE_CONFIG_H
48 #include "config.h"
49 #endif
50
51 #define JIM_EXTENSION
52 #define __JIM_EVENTLOOP_CORE__
53 #ifdef __ECOS
54 #include <pkgconf/jimtcl.h>
55 #include <sys/time.h>
56 #include <cyg/jimtcl/jim.h>
57 #include <cyg/jimtcl/jim-eventloop.h>
58 #else
59 #include "jim.h"
60 #include "jim-eventloop.h"
61 #endif
62
63 /* File event structure */
64 typedef struct Jim_FileEvent {
65 void *handle;
66 int mask; /* one of JIM_EVENT_(READABLE | WRITABLE | EXCEPTION) */
67 Jim_FileProc *fileProc;
68 Jim_EventFinalizerProc *finalizerProc;
69 void *clientData;
70 struct Jim_FileEvent *next;
71 } Jim_FileEvent;
72
73 /* Time event structure */
74 typedef struct Jim_TimeEvent {
75 jim_wide id; /* time event identifier. */
76 int mode; /* restart, repetitive .. UK */
77 long initialms; /* initial relativ timer value UK */
78 long when_sec; /* seconds */
79 long when_ms; /* milliseconds */
80 Jim_TimeProc *timeProc;
81 Jim_EventFinalizerProc *finalizerProc;
82 void *clientData;
83 struct Jim_TimeEvent *next;
84 } Jim_TimeEvent;
85
86 /* Per-interp stucture containing the state of the event loop */
87 typedef struct Jim_EventLoop {
88 jim_wide timeEventNextId;
89 Jim_FileEvent *fileEventHead;
90 Jim_TimeEvent *timeEventHead;
91 } Jim_EventLoop;
92
93 static void Jim_CreateFileHandler(Jim_Interp *interp, void *handle, int mask,
94 Jim_FileProc *proc, void *clientData,
95 Jim_EventFinalizerProc *finalizerProc)
96 {
97 Jim_FileEvent *fe;
98 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
99
100 // fprintf(stderr,"rein\n");
101 fe = Jim_Alloc(sizeof(*fe));
102 fe->handle = handle;
103 fe->mask = mask;
104 fe->fileProc = proc;
105 fe->finalizerProc = finalizerProc;
106 fe->clientData = clientData;
107 fe->next = eventLoop->fileEventHead;
108 eventLoop->fileEventHead = fe;
109 // fprintf(stderr,"raus\n");
110 }
111
112 static void Jim_DeleteFileHandler(Jim_Interp *interp, void *handle)
113 {
114 Jim_FileEvent *fe, *prev = NULL;
115 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
116
117 fe = eventLoop->fileEventHead;
118 while (fe) {
119 if (fe->handle == handle) {
120 if (prev == NULL)
121 eventLoop->fileEventHead = fe->next;
122 else
123 prev->next = fe->next;
124 if (fe->finalizerProc)
125 fe->finalizerProc(interp, fe->clientData);
126 Jim_Free(fe);
127 return;
128 }
129 prev = fe;
130 fe = fe->next;
131 }
132 }
133
134 // The same for signals.
135 void Jim_CreateSignalHandler(Jim_Interp *interp, int signum,
136 Jim_FileProc *proc, void *clientData,
137 Jim_EventFinalizerProc *finalizerProc)
138 {
139 }
140 void Jim_DeleteSignalHandler(Jim_Interp *interp, int signum)
141 {
142 }
143
144 /* That's another part of this extension that needs to be ported
145 * to WIN32. */
146 static void JimGetTime(long *seconds, long *milliseconds)
147 {
148 struct timeval tv;
149
150 gettimeofday(&tv, NULL);
151 *seconds = tv.tv_sec;
152 *milliseconds = tv.tv_usec/1000;
153 }
154
155 static jim_wide Jim_CreateTimeHandler(Jim_Interp *interp, jim_wide milliseconds,
156 Jim_TimeProc *proc, void *clientData,
157 Jim_EventFinalizerProc *finalizerProc)
158 {
159 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
160 jim_wide id = eventLoop->timeEventNextId++;
161 Jim_TimeEvent *te;
162 long cur_sec, cur_ms;
163
164 JimGetTime(&cur_sec, &cur_ms);
165
166 te = Jim_Alloc(sizeof(*te));
167 te->id = id;
168 te->mode = 0;
169 te->initialms = milliseconds;
170 te->when_sec = cur_sec + milliseconds/1000;
171 te->when_ms = cur_ms + milliseconds%1000;
172 if (te->when_ms >= 1000) {
173 te->when_sec ++;
174 te->when_ms -= 1000;
175 }
176 te->timeProc = proc;
177 te->finalizerProc = finalizerProc;
178 te->clientData = clientData;
179 te->next = eventLoop->timeEventHead;
180 eventLoop->timeEventHead = te;
181 return id;
182 }
183
184 static jim_wide Jim_DeleteTimeHandler(Jim_Interp *interp, jim_wide id)
185 {
186 Jim_TimeEvent *te, *prev = NULL;
187 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
188 long cur_sec, cur_ms;
189 jim_wide remain ;
190
191 JimGetTime(&cur_sec, &cur_ms);
192
193 te = eventLoop->timeEventHead;
194 if (id >= eventLoop->timeEventNextId)
195 return -2; /* wrong event ID */
196 while (te) {
197 if (te->id == id) {
198 remain = (te->when_sec - cur_sec) * 1000;
199 remain += (te->when_ms - cur_ms) ;
200 remain = (remain < 0) ? 0 : remain ;
201
202 if (prev == NULL)
203 eventLoop->timeEventHead = te->next;
204 else
205 prev->next = te->next;
206 if (te->finalizerProc)
207 te->finalizerProc(interp, te->clientData);
208 Jim_Free(te);
209 return remain;
210 }
211 prev = te;
212 te = te->next;
213 }
214 return -1; /* NO event with the specified ID found */
215 }
216
217 /* Search the first timer to fire.
218 * This operation is useful to know how many time the select can be
219 * put in sleep without to delay any event.
220 * If there are no timers NULL is returned. */
221 static Jim_TimeEvent *JimSearchNearestTimer(Jim_EventLoop *eventLoop)
222 {
223 Jim_TimeEvent *te = eventLoop->timeEventHead;
224 Jim_TimeEvent *nearest = NULL;
225
226 while (te) {
227 if (!nearest || te->when_sec < nearest->when_sec ||
228 (te->when_sec == nearest->when_sec &&
229 te->when_ms < nearest->when_ms))
230 nearest = te;
231 te = te->next;
232 }
233 return nearest;
234 }
235
236 /* --- POSIX version of Jim_ProcessEvents, for now the only available --- */
237 #define JIM_FILE_EVENTS 1
238 #define JIM_TIME_EVENTS 2
239 #define JIM_ALL_EVENTS (JIM_FILE_EVENTS | JIM_TIME_EVENTS)
240 #define JIM_DONT_WAIT 4
241
242 /* Process every pending time event, then every pending file event
243 * (that may be registered by time event callbacks just processed).
244 * Without special flags the function sleeps until some file event
245 * fires, or when the next time event occurrs (if any).
246 *
247 * If flags is 0, the function does nothing and returns.
248 * if flags has JIM_ALL_EVENTS set, all the kind of events are processed.
249 * if flags has JIM_FILE_EVENTS set, file events are processed.
250 * if flags has JIM_TIME_EVENTS set, time events are processed.
251 * if flags has JIM_DONT_WAIT set the function returns ASAP until all
252 * the events that's possible to process without to wait are processed.
253 *
254 * The function returns the number of events processed. */
255 int Jim_ProcessEvents(Jim_Interp *interp, int flags)
256 {
257 int maxfd = 0, numfd = 0, processed = 0;
258 fd_set rfds, wfds, efds;
259 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
260 Jim_FileEvent *fe = eventLoop->fileEventHead;
261 Jim_TimeEvent *te;
262 jim_wide maxId;
263 JIM_NOTUSED(flags);
264
265 FD_ZERO(&rfds);
266 FD_ZERO(&wfds);
267 FD_ZERO(&efds);
268
269 /* Check file events */
270 while (fe != NULL) {
271 int fd = fileno((FILE*)fe->handle);
272
273 if (fe->mask & JIM_EVENT_READABLE)
274 FD_SET(fd, &rfds);
275 if (fe->mask & JIM_EVENT_WRITABLE) FD_SET(fd, &wfds);
276 if (fe->mask & JIM_EVENT_EXCEPTION) FD_SET(fd, &efds);
277 if (maxfd < fd) maxfd = fd;
278 numfd++;
279 fe = fe->next;
280 }
281 /* Note that we want call select() even if there are no
282 * file events to process as long as we want to process time
283 * events, in order to sleep until the next time event is ready
284 * to fire. */
285 if (numfd || ((flags & JIM_TIME_EVENTS) && !(flags & JIM_DONT_WAIT))) {
286 int retval;
287 Jim_TimeEvent *shortest;
288 struct timeval tv, *tvp;
289 jim_wide dt;
290
291 shortest = JimSearchNearestTimer(eventLoop);
292 if (shortest) {
293 long now_sec, now_ms;
294
295 /* Calculate the time missing for the nearest
296 * timer to fire. */
297 JimGetTime(&now_sec, &now_ms);
298 tvp = &tv;
299 dt = 1000 * (shortest->when_sec - now_sec);
300 dt += (shortest->when_ms - now_ms);
301 if (dt < 0) {
302 dt = 1;
303 }
304 tvp->tv_sec = dt / 1000;
305 tvp->tv_usec = dt % 1000;
306 // fprintf(stderr,"Next %d.% 8d\n",(int)tvp->tv_sec,(int)tvp->tv_usec);
307 } else {
308 tvp = NULL; /* wait forever */
309 // fprintf(stderr,"No Event\n");
310 }
311
312 retval = select(maxfd + 1, &rfds, &wfds, &efds, tvp);
313 if (retval < 0) {
314 switch (errno) {
315 case EINTR: fprintf(stderr,"select EINTR\n"); break;
316 case EINVAL: fprintf(stderr,"select EINVAL\n"); break;
317 case ENOMEM: fprintf(stderr,"select ENOMEM\n"); break;
318 }
319 } else if (retval > 0) {
320 fe = eventLoop->fileEventHead;
321 while (fe != NULL) {
322 int fd = fileno((FILE*)fe->handle);
323
324 // fprintf(stderr,"fd: %d mask: %02x \n",fd,fe->mask);
325
326 if ((fe->mask & JIM_EVENT_READABLE && FD_ISSET(fd, &rfds)) ||
327 (fe->mask & JIM_EVENT_WRITABLE && FD_ISSET(fd, &wfds)) ||
328 (fe->mask & JIM_EVENT_EXCEPTION && FD_ISSET(fd, &efds)))
329 {
330 int mask = 0;
331
332 if (fe->mask & JIM_EVENT_READABLE && FD_ISSET(fd, &rfds)) {
333 mask |= JIM_EVENT_READABLE;
334 if ((fe->mask & JIM_EVENT_FEOF) && feof((FILE *)fe->handle))
335 mask |= JIM_EVENT_FEOF;
336 }
337 if (fe->mask & JIM_EVENT_WRITABLE && FD_ISSET(fd, &wfds))
338 mask |= JIM_EVENT_WRITABLE;
339 if (fe->mask & JIM_EVENT_EXCEPTION && FD_ISSET(fd, &efds))
340 mask |= JIM_EVENT_EXCEPTION;
341 if (fe->fileProc(interp, fe->clientData, mask) == JIM_ERR) {
342 /* Remove the element on handler error */
343 Jim_DeleteFileHandler(interp, fe->handle);
344 }
345 processed++;
346 /* After an event is processed our file event list
347 * may no longer be the same, so what we do
348 * is to clear the bit for this file descriptor and
349 * restart again from the head. */
350 fe = eventLoop->fileEventHead;
351 FD_CLR(fd, &rfds);
352 FD_CLR(fd, &wfds);
353 FD_CLR(fd, &efds);
354 } else {
355 fe = fe->next;
356 }
357 }
358 }
359 }
360 /* Check time events */
361 te = eventLoop->timeEventHead;
362 maxId = eventLoop->timeEventNextId-1;
363 while (te) {
364 long now_sec, now_ms;
365 jim_wide id;
366
367 if (te->id > maxId) {
368 te = te->next;
369 continue;
370 }
371 JimGetTime(&now_sec, &now_ms);
372 if (now_sec > te->when_sec ||
373 (now_sec == te->when_sec && now_ms >= te->when_ms))
374 {
375 id = te->id;
376 te->timeProc(interp, te->clientData);
377 /* After an event is processed our time event list may
378 * no longer be the same, so we restart from head.
379 * Still we make sure to don't process events registered
380 * by event handlers itself in order to don't loop forever
381 * even in case an [after 0] that continuously register
382 * itself. To do so we saved the max ID we want to handle. */
383 Jim_DeleteTimeHandler(interp, id);
384 te = eventLoop->timeEventHead;
385 } else {
386 te = te->next;
387 }
388 }
389
390 return processed;
391 }
392 /* ---------------------------------------------------------------------- */
393
394 static void JimELAssocDataDeleProc(Jim_Interp *interp, void *data)
395 {
396 void *next;
397 Jim_FileEvent *fe;
398 Jim_TimeEvent *te;
399 Jim_EventLoop *eventLoop = data;
400
401 fe = eventLoop->fileEventHead;
402 while (fe) {
403 next = fe->next;
404 if (fe->finalizerProc)
405 fe->finalizerProc(interp, fe->clientData);
406 Jim_Free(fe);
407 fe = next;
408 }
409
410 te = eventLoop->timeEventHead;
411 while (te) {
412 next = te->next;
413 if (te->finalizerProc)
414 te->finalizerProc(interp, te->clientData);
415 Jim_Free(te);
416 te = next;
417 }
418 Jim_Free(data);
419 }
420
421 static int JimELVwaitCommand(Jim_Interp *interp, int argc,
422 Jim_Obj *const *argv)
423 {
424 Jim_Obj *oldValue;
425
426 if (argc != 2) {
427 Jim_WrongNumArgs(interp, 1, argv, "name");
428 return JIM_ERR;
429 }
430 oldValue = Jim_GetGlobalVariable(interp, argv[1], JIM_NONE);
431 if (oldValue) Jim_IncrRefCount(oldValue);
432 while (1) {
433 Jim_Obj *currValue;
434
435 Jim_ProcessEvents(interp, JIM_ALL_EVENTS);
436 currValue = Jim_GetGlobalVariable(interp, argv[1], JIM_NONE);
437 /* Stop the loop if the vwait-ed variable changed value,
438 * or if was unset and now is set (or the contrary). */
439 if ((oldValue && !currValue) ||
440 (!oldValue && currValue) ||
441 (oldValue && currValue &&
442 !Jim_StringEqObj(oldValue, currValue, JIM_CASESENS)))
443 break;
444 }
445 if (oldValue) Jim_DecrRefCount(interp, oldValue);
446 return JIM_OK;
447 }
448
449 static void JimAfterTimeHandler(Jim_Interp *interp, void *clientData)
450 {
451 Jim_Obj *objPtr = clientData;
452
453 Jim_EvalObjBackground(interp, objPtr);
454 }
455
456 static void JimAfterTimeEventFinalizer(Jim_Interp *interp, void *clientData)
457 {
458 Jim_Obj *objPtr = clientData;
459
460 Jim_DecrRefCount(interp, objPtr);
461 }
462
463 static int JimELAfterCommand(Jim_Interp *interp, int argc,
464 Jim_Obj *const *argv)
465 {
466 jim_wide ms, id;
467 Jim_Obj *objPtr, *idObjPtr;
468 const char *options[] = {
469 "info", "cancel", "restart", "expire", NULL
470 };
471 enum {INFO, CANCEL, RESTART, EXPIRE, CREATE };
472 int option = CREATE ;
473
474 if (argc < 3) {
475 Jim_WrongNumArgs(interp, 1, argv, "<after milliseconds> script");
476 return JIM_ERR;
477 }
478 if (Jim_GetWide(interp, argv[1], &ms) != JIM_OK)
479 if (Jim_GetEnum(interp, argv[1], options, &option, "after options",
480 JIM_ERRMSG) != JIM_OK)
481 return JIM_ERR;
482 switch (option) {
483 case CREATE:
484 Jim_IncrRefCount(argv[2]);
485 id = Jim_CreateTimeHandler(interp, ms, JimAfterTimeHandler, argv[2],
486 JimAfterTimeEventFinalizer);
487 objPtr = Jim_NewStringObj(interp, NULL, 0);
488 Jim_AppendString(interp, objPtr, "after#", -1);
489 idObjPtr = Jim_NewIntObj(interp, id);
490 Jim_IncrRefCount(idObjPtr);
491 Jim_AppendObj(interp, objPtr, idObjPtr);
492 Jim_DecrRefCount(interp, idObjPtr);
493 Jim_SetResult(interp, objPtr);
494 return JIM_OK;
495 case CANCEL:
496 {
497 int tlen ;
498 jim_wide remain = 0;
499 const char *tok = Jim_GetString(argv[2], &tlen);
500 if (sscanf(tok,"after#%" JIM_WIDE_MODIFIER, &id) == 1) {
501 remain = Jim_DeleteTimeHandler(interp, id);
502 if (remain > -2) {
503 Jim_SetResult(interp, Jim_NewIntObj(interp, remain));
504 return JIM_OK;
505 }
506 }
507 Jim_SetResultString(interp, "invalid event" , -1);
508 return JIM_ERR;
509 }
510 default:
511 fprintf(stderr,"unserviced option to after %d\n",option);
512 }
513 return JIM_OK;
514 }
515
516 /* This extension is not dynamically loaded, instead it's linked statically,
517 which is why we shouldn't use the unspecific 'Jim_OnLoad' name */
518 int Jim_EventLoopOnLoad(Jim_Interp *interp)
519 {
520 Jim_EventLoop *eventLoop;
521
522 Jim_InitExtension(interp);
523 if (Jim_PackageProvide(interp, "eventloop", "1.0", JIM_ERRMSG) != JIM_OK)
524 return JIM_ERR;
525
526 eventLoop = Jim_Alloc(sizeof(*eventLoop));
527 eventLoop->fileEventHead = NULL;
528 eventLoop->timeEventHead = NULL;
529 eventLoop->timeEventNextId = 1;
530 Jim_SetAssocData(interp, "eventloop", JimELAssocDataDeleProc, eventLoop);
531
532 Jim_CreateCommand(interp, "vwait", JimELVwaitCommand, NULL, NULL);
533 Jim_CreateCommand(interp, "after", JimELAfterCommand, NULL, NULL);
534
535 /* Export events API */
536 Jim_RegisterApi(interp, "Jim_CreateFileHandler", Jim_CreateFileHandler);
537 Jim_RegisterApi(interp, "Jim_DeleteFileHandler", Jim_DeleteFileHandler);
538 Jim_RegisterApi(interp, "Jim_CreateTimeHandler", Jim_CreateTimeHandler);
539 Jim_RegisterApi(interp, "Jim_DeleteTimeHandler", Jim_DeleteTimeHandler);
540 Jim_RegisterApi(interp, "Jim_ProcessEvents", Jim_ProcessEvents);
541 return JIM_OK;
542 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)