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

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)