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

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)