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

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)