625137047a0477902a246a1d9c82492a5c2c3dc8
[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
48 #define JIM_EXTENSION
49 #define __JIM_EVENTLOOP_CORE__
50 #ifdef __ECOS
51 #include <pkgconf/jimtcl.h>
52 #endif
53 #ifdef __ECOS
54 #include <cyg/jimtcl/jim.h>
55 #include <cyg/jimtcl/jim-eventloop.h>
56 #else
57 #include "jim.h"
58 #include "jim-eventloop.h"
59 #endif
60
61 /* POSIX includes */
62 #include <sys/time.h>
63 #include <sys/types.h>
64 #include <unistd.h>
65 #include <errno.h>
66
67 #include "replacements.h"
68
69
70 /* --- */
71
72 /* File event structure */
73 typedef struct Jim_FileEvent {
74 void *handle;
75 int mask; /* one of JIM_EVENT_(READABLE|WRITABLE|EXCEPTION) */
76 Jim_FileProc *fileProc;
77 Jim_EventFinalizerProc *finalizerProc;
78 void *clientData;
79 struct Jim_FileEvent *next;
80 } Jim_FileEvent;
81
82 /* Time event structure */
83 typedef struct Jim_TimeEvent {
84 jim_wide id; /* time event identifier. */
85 int mode; /* restart, repetitive .. UK */
86 long initialms; /* initial relativ timer value UK */
87 long when_sec; /* seconds */
88 long when_ms; /* milliseconds */
89 Jim_TimeProc *timeProc;
90 Jim_EventFinalizerProc *finalizerProc;
91 void *clientData;
92 struct Jim_TimeEvent *next;
93 } Jim_TimeEvent;
94
95 /* Per-interp stucture containing the state of the event loop */
96 typedef struct Jim_EventLoop {
97 jim_wide timeEventNextId;
98 Jim_FileEvent *fileEventHead;
99 Jim_TimeEvent *timeEventHead;
100 } Jim_EventLoop;
101
102 void Jim_CreateFileHandler(Jim_Interp *interp, void *handle, int mask,
103 Jim_FileProc *proc, void *clientData,
104 Jim_EventFinalizerProc *finalizerProc)
105 {
106 Jim_FileEvent *fe;
107 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
108
109 // fprintf(stderr,"rein\n");
110 fe = Jim_Alloc(sizeof(*fe));
111 fe->handle = handle;
112 fe->mask = mask;
113 fe->fileProc = proc;
114 fe->finalizerProc = finalizerProc;
115 fe->clientData = clientData;
116 fe->next = eventLoop->fileEventHead;
117 eventLoop->fileEventHead = fe;
118 // fprintf(stderr,"raus\n");
119 }
120
121 void Jim_DeleteFileHandler(Jim_Interp *interp, void *handle)
122 {
123 Jim_FileEvent *fe, *prev = NULL;
124 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
125
126 fe = eventLoop->fileEventHead;
127 while(fe) {
128 if (fe->handle == handle) {
129 if (prev == NULL)
130 eventLoop->fileEventHead = fe->next;
131 else
132 prev->next = fe->next;
133 if (fe->finalizerProc)
134 fe->finalizerProc(interp, fe->clientData);
135 Jim_Free(fe);
136 return;
137 }
138 prev = fe;
139 fe = fe->next;
140 }
141 }
142
143 // The same for signals.
144 void Jim_CreateSignalHandler(Jim_Interp *interp, int signum,
145 Jim_FileProc *proc, void *clientData,
146 Jim_EventFinalizerProc *finalizerProc)
147 {
148 }
149 void Jim_DeleteSignalHandler(Jim_Interp *interp, int signum)
150 {
151 }
152
153 /* That's another part of this extension that needs to be ported
154 * to WIN32. */
155 static void JimGetTime(long *seconds, long *milliseconds)
156 {
157 struct timeval tv;
158
159 gettimeofday(&tv, NULL);
160 *seconds = tv.tv_sec;
161 *milliseconds = tv.tv_usec/1000;
162 }
163
164 jim_wide Jim_CreateTimeHandler(Jim_Interp *interp, jim_wide milliseconds,
165 Jim_TimeProc *proc, void *clientData,
166 Jim_EventFinalizerProc *finalizerProc)
167 {
168 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
169 jim_wide id = eventLoop->timeEventNextId++;
170 Jim_TimeEvent *te;
171 long cur_sec, cur_ms;
172
173 JimGetTime(&cur_sec, &cur_ms);
174
175 te = Jim_Alloc(sizeof(*te));
176 te->id = id;
177 te->mode = 0;
178 te->initialms = milliseconds;
179 te->when_sec = cur_sec + milliseconds/1000;
180 te->when_ms = cur_ms + milliseconds%1000;
181 if (te->when_ms >= 1000) {
182 te->when_sec ++;
183 te->when_ms -= 1000;
184 }
185 te->timeProc = proc;
186 te->finalizerProc = finalizerProc;
187 te->clientData = clientData;
188 te->next = eventLoop->timeEventHead;
189 eventLoop->timeEventHead = te;
190 return id;
191 }
192
193 jim_wide Jim_DeleteTimeHandler(Jim_Interp *interp, jim_wide id)
194 {
195 Jim_TimeEvent *te, *prev = NULL;
196 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
197 long cur_sec, cur_ms;
198 jim_wide remain ;
199
200 JimGetTime(&cur_sec, &cur_ms);
201
202 te = eventLoop->timeEventHead;
203 if (id >= eventLoop->timeEventNextId)
204 return -2; /* wrong event ID */
205 while(te) {
206 if (te->id == id) {
207 remain = (te->when_sec - cur_sec) * 1000;
208 remain += (te->when_ms - cur_ms) ;
209 remain = (remain < 0) ? 0 : remain ;
210
211 if (prev == NULL)
212 eventLoop->timeEventHead = te->next;
213 else
214 prev->next = te->next;
215 if (te->finalizerProc)
216 te->finalizerProc(interp, te->clientData);
217 Jim_Free(te);
218 return remain;
219 }
220 prev = te;
221 te = te->next;
222 }
223 return -1; /* NO event with the specified ID found */
224 }
225
226 /* Search the first timer to fire.
227 * This operation is useful to know how many time the select can be
228 * put in sleep without to delay any event.
229 * If there are no timers NULL is returned. */
230 static Jim_TimeEvent *JimSearchNearestTimer(Jim_EventLoop *eventLoop)
231 {
232 Jim_TimeEvent *te = eventLoop->timeEventHead;
233 Jim_TimeEvent *nearest = NULL;
234
235 while(te) {
236 if (!nearest || te->when_sec < nearest->when_sec ||
237 (te->when_sec == nearest->when_sec &&
238 te->when_ms < nearest->when_ms))
239 nearest = te;
240 te = te->next;
241 }
242 return nearest;
243 }
244
245 /* --- POSIX version of Jim_ProcessEvents, for now the only available --- */
246 #define JIM_FILE_EVENTS 1
247 #define JIM_TIME_EVENTS 2
248 #define JIM_ALL_EVENTS (JIM_FILE_EVENTS|JIM_TIME_EVENTS)
249 #define JIM_DONT_WAIT 4
250
251 /* Process every pending time event, then every pending file event
252 * (that may be registered by time event callbacks just processed).
253 * Without special flags the function sleeps until some file event
254 * fires, or when the next time event occurrs (if any).
255 *
256 * If flags is 0, the function does nothing and returns.
257 * if flags has JIM_ALL_EVENTS set, all the kind of events are processed.
258 * if flags has JIM_FILE_EVENTS set, file events are processed.
259 * if flags has JIM_TIME_EVENTS set, time events are processed.
260 * if flags has JIM_DONT_WAIT set the function returns ASAP until all
261 * the events that's possible to process without to wait are processed.
262 *
263 * The function returns the number of events processed. */
264 int Jim_ProcessEvents(Jim_Interp *interp, int flags)
265 {
266 int maxfd = 0, numfd = 0, processed = 0;
267 fd_set rfds, wfds, efds;
268 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
269 Jim_FileEvent *fe = eventLoop->fileEventHead;
270 Jim_TimeEvent *te;
271 jim_wide maxId;
272 JIM_NOTUSED(flags);
273
274 FD_ZERO(&rfds);
275 FD_ZERO(&wfds);
276 FD_ZERO(&efds);
277
278 /* Check file events */
279 while (fe != NULL) {
280 int fd = fileno((FILE*)fe->handle);
281
282 if (fe->mask & JIM_EVENT_READABLE)
283 FD_SET(fd, &rfds);
284 if (fe->mask & JIM_EVENT_WRITABLE) FD_SET(fd, &wfds);
285 if (fe->mask & JIM_EVENT_EXCEPTION) FD_SET(fd, &efds);
286 if (maxfd < fd) maxfd = fd;
287 numfd++;
288 fe = fe->next;
289 }
290 /* Note that we want call select() even if there are no
291 * file events to process as long as we want to process time
292 * events, in order to sleep until the next time event is ready
293 * to fire. */
294 if (numfd || ((flags & JIM_TIME_EVENTS) && !(flags & JIM_DONT_WAIT))) {
295 int retval;
296 Jim_TimeEvent *shortest;
297 struct timeval tv, *tvp;
298 jim_wide dt;
299
300 shortest = JimSearchNearestTimer(eventLoop);
301 if (shortest) {
302 long now_sec, now_ms;
303
304 /* Calculate the time missing for the nearest
305 * timer to fire. */
306 JimGetTime(&now_sec, &now_ms);
307 tvp = &tv;
308 dt = 1000 * (shortest->when_sec - now_sec);
309 dt += ( shortest->when_ms - now_ms);
310 if (dt < 0) {
311 dt = 1;
312 }
313 tvp->tv_sec = dt / 1000;
314 tvp->tv_usec = dt % 1000;
315 // fprintf(stderr,"Next %d.% 8d\n",(int)tvp->tv_sec,(int)tvp->tv_usec);
316 } else {
317 tvp = NULL; /* wait forever */
318 // fprintf(stderr,"No Event\n");
319 }
320
321 retval = select(maxfd+1, &rfds, &wfds, &efds, tvp);
322 if (retval < 0) {
323 switch (errno) {
324 case EINTR: fprintf(stderr,"select EINTR\n"); break;
325 case EINVAL: fprintf(stderr,"select EINVAL\n"); break;
326 case ENOMEM: fprintf(stderr,"select ENOMEM\n"); break;
327 }
328 } else if (retval > 0) {
329 fe = eventLoop->fileEventHead;
330 while(fe != NULL) {
331 int fd = fileno((FILE*)fe->handle);
332
333 // fprintf(stderr,"fd: %d mask: %02x \n",fd,fe->mask);
334
335 if ((fe->mask & JIM_EVENT_READABLE && FD_ISSET(fd, &rfds)) ||
336 (fe->mask & JIM_EVENT_WRITABLE && FD_ISSET(fd, &wfds)) ||
337 (fe->mask & JIM_EVENT_EXCEPTION && FD_ISSET(fd, &efds)))
338 {
339 int mask = 0;
340
341 if (fe->mask & JIM_EVENT_READABLE && FD_ISSET(fd, &rfds)) {
342 mask |= JIM_EVENT_READABLE;
343 if ((fe->mask & JIM_EVENT_FEOF) && feof((FILE *)fe->handle))
344 mask |= JIM_EVENT_FEOF;
345 }
346 if (fe->mask & JIM_EVENT_WRITABLE && FD_ISSET(fd, &wfds))
347 mask |= JIM_EVENT_WRITABLE;
348 if (fe->mask & JIM_EVENT_EXCEPTION && FD_ISSET(fd, &efds))
349 mask |= JIM_EVENT_EXCEPTION;
350 if (fe->fileProc(interp, fe->clientData, mask) == JIM_ERR) {
351 /* Remove the element on handler error */
352 Jim_DeleteFileHandler(interp, fe->handle);
353 }
354 processed++;
355 /* After an event is processed our file event list
356 * may no longer be the same, so what we do
357 * is to clear the bit for this file descriptor and
358 * restart again from the head. */
359 fe = eventLoop->fileEventHead;
360 FD_CLR(fd, &rfds);
361 FD_CLR(fd, &wfds);
362 FD_CLR(fd, &efds);
363 } else {
364 fe = fe->next;
365 }
366 }
367 }
368 }
369 /* Check time events */
370 te = eventLoop->timeEventHead;
371 maxId = eventLoop->timeEventNextId-1;
372 while(te) {
373 long now_sec, now_ms;
374 jim_wide id;
375
376 if (te->id > maxId) {
377 te = te->next;
378 continue;
379 }
380 JimGetTime(&now_sec, &now_ms);
381 if (now_sec > te->when_sec ||
382 (now_sec == te->when_sec && now_ms >= te->when_ms))
383 {
384 id = te->id;
385 te->timeProc(interp, te->clientData);
386 /* After an event is processed our time event list may
387 * no longer be the same, so we restart from head.
388 * Still we make sure to don't process events registered
389 * by event handlers itself in order to don't loop forever
390 * even in case an [after 0] that continuously register
391 * itself. To do so we saved the max ID we want to handle. */
392 Jim_DeleteTimeHandler(interp, id);
393 te = eventLoop->timeEventHead;
394 } else {
395 te = te->next;
396 }
397 }
398
399 return processed;
400 }
401 /* ---------------------------------------------------------------------- */
402
403 void JimELAssocDataDeleProc(Jim_Interp *interp, void *data)
404 {
405 void *next;
406 Jim_FileEvent *fe;
407 Jim_TimeEvent *te;
408 Jim_EventLoop *eventLoop = data;
409
410 fe = eventLoop->fileEventHead;
411 while(fe) {
412 next = fe->next;
413 if (fe->finalizerProc)
414 fe->finalizerProc(interp, fe->clientData);
415 Jim_Free(fe);
416 fe = next;
417 }
418
419 te = eventLoop->timeEventHead;
420 while(te) {
421 next = te->next;
422 if (te->finalizerProc)
423 te->finalizerProc(interp, te->clientData);
424 Jim_Free(te);
425 te = next;
426 }
427 Jim_Free(data);
428 }
429
430 static int JimELVwaitCommand(Jim_Interp *interp, int argc,
431 Jim_Obj *const *argv)
432 {
433 Jim_Obj *oldValue;
434
435 if (argc != 2) {
436 Jim_WrongNumArgs(interp, 1, argv, "name");
437 return JIM_ERR;
438 }
439 oldValue = Jim_GetGlobalVariable(interp, argv[1], JIM_NONE);
440 if (oldValue) Jim_IncrRefCount(oldValue);
441 while (1) {
442 Jim_Obj *currValue;
443
444 Jim_ProcessEvents(interp, JIM_ALL_EVENTS);
445 currValue = Jim_GetGlobalVariable(interp, argv[1], JIM_NONE);
446 /* Stop the loop if the vwait-ed variable changed value,
447 * or if was unset and now is set (or the contrary). */
448 if ((oldValue && !currValue) ||
449 (!oldValue && currValue) ||
450 (oldValue && currValue &&
451 !Jim_StringEqObj(oldValue, currValue, JIM_CASESENS)))
452 break;
453 }
454 if (oldValue) Jim_DecrRefCount(interp, oldValue);
455 return JIM_OK;
456 }
457
458 void JimAfterTimeHandler(Jim_Interp *interp, void *clientData)
459 {
460 Jim_Obj *objPtr = clientData;
461
462 Jim_EvalObjBackground(interp, objPtr);
463 }
464
465 void JimAfterTimeEventFinalizer(Jim_Interp *interp, void *clientData)
466 {
467 Jim_Obj *objPtr = clientData;
468
469 Jim_DecrRefCount(interp, objPtr);
470 }
471
472 static int JimELAfterCommand(Jim_Interp *interp, int argc,
473 Jim_Obj *const *argv)
474 {
475 jim_wide ms, id;
476 Jim_Obj *objPtr, *idObjPtr;
477 const char *options[] = {
478 "info", "cancel", "restart", "expire", NULL
479 };
480 enum {INFO, CANCEL, RESTART, EXPIRE, CREATE };
481 int option = CREATE ;
482
483 if (argc < 3) {
484 Jim_WrongNumArgs(interp, 1, argv, "<after milliseconds> script");
485 return JIM_ERR;
486 }
487 if (Jim_GetWide(interp, argv[1], &ms) != JIM_OK)
488 if (Jim_GetEnum(interp, argv[1], options, &option, "after options",
489 JIM_ERRMSG) != JIM_OK)
490 return JIM_ERR;
491 switch (option) {
492 case CREATE:
493 Jim_IncrRefCount(argv[2]);
494 id = Jim_CreateTimeHandler(interp, ms, JimAfterTimeHandler, argv[2],
495 JimAfterTimeEventFinalizer);
496 objPtr = Jim_NewStringObj(interp, NULL, 0);
497 Jim_AppendString(interp, objPtr, "after#", -1);
498 idObjPtr = Jim_NewIntObj(interp, id);
499 Jim_IncrRefCount(idObjPtr);
500 Jim_AppendObj(interp, objPtr, idObjPtr);
501 Jim_DecrRefCount(interp, idObjPtr);
502 Jim_SetResult(interp, objPtr);
503 return JIM_OK;
504 case CANCEL:
505 {
506 int tlen ;
507 jim_wide remain = 0;
508 const char *tok = Jim_GetString(argv[2], &tlen);
509 if ( sscanf(tok,"after#%lld",&id) == 1) {
510 remain = Jim_DeleteTimeHandler(interp, id);
511 if (remain > -2) {
512 Jim_SetResult(interp, Jim_NewIntObj(interp, remain));
513 return JIM_OK;
514 }
515 }
516 Jim_SetResultString(interp, "invalid event" , -1);
517 return JIM_ERR;
518 }
519 default:
520 fprintf(stderr,"unserviced option to after %d\n",option);
521 }
522 return JIM_OK;
523 }
524
525 /* This extension is not dynamically loaded, instead it's linked statically,
526 which is why we shouldn't use the unspecific 'Jim_OnLoad' name */
527 int Jim_EventLoopOnLoad(Jim_Interp *interp)
528 {
529 Jim_EventLoop *eventLoop;
530
531 Jim_InitExtension(interp);
532 if (Jim_PackageProvide(interp, "eventloop", "1.0", JIM_ERRMSG) != JIM_OK)
533 return JIM_ERR;
534
535 eventLoop = Jim_Alloc(sizeof(*eventLoop));
536 eventLoop->fileEventHead = NULL;
537 eventLoop->timeEventHead = NULL;
538 eventLoop->timeEventNextId = 1;
539 Jim_SetAssocData(interp, "eventloop", JimELAssocDataDeleProc, eventLoop);
540
541 Jim_CreateCommand(interp, "vwait", JimELVwaitCommand, NULL, NULL);
542 Jim_CreateCommand(interp, "after", JimELAfterCommand, NULL, NULL);
543
544 /* Export events API */
545 Jim_RegisterApi(interp, "Jim_CreateFileHandler", Jim_CreateFileHandler);
546 Jim_RegisterApi(interp, "Jim_DeleteFileHandler", Jim_DeleteFileHandler);
547 Jim_RegisterApi(interp, "Jim_CreateTimeHandler", Jim_CreateTimeHandler);
548 Jim_RegisterApi(interp, "Jim_DeleteTimeHandler", Jim_DeleteTimeHandler);
549 Jim_RegisterApi(interp, "Jim_ProcessEvents", Jim_ProcessEvents);
550 return JIM_OK;
551 }

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)