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

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)