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

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)