OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [generic/] [tclTimer.c] - Blame information for rev 1774

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tclTimer.c --
3
 *
4
 *      This file provides timer event management facilities for Tcl,
5
 *      including the "after" command.
6
 *
7
 * Copyright (c) 1997 by Sun Microsystems, Inc.
8
 *
9
 * See the file "license.terms" for information on usage and redistribution
10
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11
 *
12
 * RCS: @(#) $Id: tclTimer.c,v 1.1.1.1 2002-01-16 10:25:29 markom Exp $
13
 */
14
 
15
#include "tclInt.h"
16
#include "tclPort.h"
17
 
18
/*
19
 * This flag indicates whether this module has been initialized.
20
 */
21
 
22
static int initialized = 0;
23
 
24
/*
25
 * For each timer callback that's pending there is one record of the following
26
 * type.  The normal handlers (created by Tcl_CreateTimerHandler) are chained
27
 * together in a list sorted by time (earliest event first).
28
 */
29
 
30
typedef struct TimerHandler {
31
    Tcl_Time time;                      /* When timer is to fire. */
32
    Tcl_TimerProc *proc;                /* Procedure to call. */
33
    ClientData clientData;              /* Argument to pass to proc. */
34
    Tcl_TimerToken token;               /* Identifies handler so it can be
35
                                         * deleted. */
36
    struct TimerHandler *nextPtr;       /* Next event in queue, or NULL for
37
                                         * end of queue. */
38
} TimerHandler;
39
 
40
static TimerHandler *firstTimerHandlerPtr = NULL;
41
                                        /* First event in queue. */
42
static int lastTimerId;                 /* Timer identifier of most recently
43
                                         * created timer. */
44
static int timerPending;                /* 1 if a timer event is in the queue. */
45
 
46
/*
47
 * The data structure below is used by the "after" command to remember
48
 * the command to be executed later.  All of the pending "after" commands
49
 * for an interpreter are linked together in a list.
50
 */
51
 
52
typedef struct AfterInfo {
53
    struct AfterAssocData *assocPtr;
54
                                /* Pointer to the "tclAfter" assocData for
55
                                 * the interp in which command will be
56
                                 * executed. */
57
    char *command;              /* Command to execute.  Malloc'ed, so must
58
                                 * be freed when structure is deallocated. */
59
    int id;                     /* Integer identifier for command;  used to
60
                                 * cancel it. */
61
    Tcl_TimerToken token;       /* Used to cancel the "after" command.  NULL
62
                                 * means that the command is run as an
63
                                 * idle handler rather than as a timer
64
                                 * handler.  NULL means this is an "after
65
                                 * idle" handler rather than a
66
                                 * timer handler. */
67
    struct AfterInfo *nextPtr;  /* Next in list of all "after" commands for
68
                                 * this interpreter. */
69
} AfterInfo;
70
 
71
/*
72
 * One of the following structures is associated with each interpreter
73
 * for which an "after" command has ever been invoked.  A pointer to
74
 * this structure is stored in the AssocData for the "tclAfter" key.
75
 */
76
 
77
typedef struct AfterAssocData {
78
    Tcl_Interp *interp;         /* The interpreter for which this data is
79
                                 * registered. */
80
    AfterInfo *firstAfterPtr;   /* First in list of all "after" commands
81
                                 * still pending for this interpreter, or
82
                                 * NULL if none. */
83
} AfterAssocData;
84
 
85
/*
86
 * There is one of the following structures for each of the
87
 * handlers declared in a call to Tcl_DoWhenIdle.  All of the
88
 * currently-active handlers are linked together into a list.
89
 */
90
 
91
typedef struct IdleHandler {
92
    Tcl_IdleProc (*proc);       /* Procedure to call. */
93
    ClientData clientData;      /* Value to pass to proc. */
94
    int generation;             /* Used to distinguish older handlers from
95
                                 * recently-created ones. */
96
    struct IdleHandler *nextPtr;/* Next in list of active handlers. */
97
} IdleHandler;
98
 
99
static IdleHandler *idleList;
100
                                /* First in list of all idle handlers. */
101
static IdleHandler *lastIdlePtr;
102
                                /* Last in list (or NULL for empty list). */
103
static int idleGeneration;      /* Used to fill in the "generation" fields
104
                                 * of IdleHandler structures.  Increments
105
                                 * each time Tcl_DoOneEvent starts calling
106
                                 * idle handlers, so that all old handlers
107
                                 * can be called without calling any of the
108
                                 * new ones created by old ones. */
109
 
110
/*
111
 * Prototypes for procedures referenced only in this file:
112
 */
113
 
114
static void             AfterCleanupProc _ANSI_ARGS_((ClientData clientData,
115
                            Tcl_Interp *interp));
116
static void             AfterProc _ANSI_ARGS_((ClientData clientData));
117
static void             FreeAfterPtr _ANSI_ARGS_((AfterInfo *afterPtr));
118
static AfterInfo *      GetAfterEvent _ANSI_ARGS_((AfterAssocData *assocPtr,
119
                            char *string));
120
static void             InitTimer _ANSI_ARGS_((void));
121
static void             TimerExitProc _ANSI_ARGS_((ClientData clientData));
122
static int              TimerHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
123
                            int flags));
124
static void             TimerCheckProc _ANSI_ARGS_((ClientData clientData,
125
                            int flags));
126
static void             TimerSetupProc _ANSI_ARGS_((ClientData clientData,
127
                            int flags));
128
 
129
/*
130
 *----------------------------------------------------------------------
131
 *
132
 * InitTimer --
133
 *
134
 *      This function initializes the timer module.
135
 *
136
 * Results:
137
 *      None.
138
 *
139
 * Side effects:
140
 *      Registers the idle and timer event sources.
141
 *
142
 *----------------------------------------------------------------------
143
 */
144
 
145
static void
146
InitTimer()
147
{
148
    initialized = 1;
149
    lastTimerId = 0;
150
    timerPending = 0;
151
    idleGeneration = 0;
152
    firstTimerHandlerPtr = NULL;
153
    lastIdlePtr = NULL;
154
    idleList = NULL;
155
 
156
    Tcl_CreateEventSource(TimerSetupProc, TimerCheckProc, NULL);
157
    Tcl_CreateExitHandler(TimerExitProc, NULL);
158
}
159
 
160
/*
161
 *----------------------------------------------------------------------
162
 *
163
 * TimerExitProc --
164
 *
165
 *      This function is call at exit or unload time to remove the
166
 *      timer and idle event sources.
167
 *
168
 * Results:
169
 *      None.
170
 *
171
 * Side effects:
172
 *      Removes the timer and idle event sources.
173
 *
174
 *----------------------------------------------------------------------
175
 */
176
 
177
static void
178
TimerExitProc(clientData)
179
    ClientData clientData;      /* Not used. */
180
{
181
    Tcl_DeleteEventSource(TimerSetupProc, TimerCheckProc, NULL);
182
    initialized = 0;
183
}
184
 
185
/*
186
 *--------------------------------------------------------------
187
 *
188
 * Tcl_CreateTimerHandler --
189
 *
190
 *      Arrange for a given procedure to be invoked at a particular
191
 *      time in the future.
192
 *
193
 * Results:
194
 *      The return value is a token for the timer event, which
195
 *      may be used to delete the event before it fires.
196
 *
197
 * Side effects:
198
 *      When milliseconds have elapsed, proc will be invoked
199
 *      exactly once.
200
 *
201
 *--------------------------------------------------------------
202
 */
203
 
204
Tcl_TimerToken
205
Tcl_CreateTimerHandler(milliseconds, proc, clientData)
206
    int milliseconds;           /* How many milliseconds to wait
207
                                 * before invoking proc. */
208
    Tcl_TimerProc *proc;        /* Procedure to invoke. */
209
    ClientData clientData;      /* Arbitrary data to pass to proc. */
210
{
211
    register TimerHandler *timerHandlerPtr, *tPtr2, *prevPtr;
212
    Tcl_Time time;
213
 
214
    if (!initialized) {
215
        InitTimer();
216
    }
217
 
218
    timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));
219
 
220
    /*
221
     * Compute when the event should fire.
222
     */
223
 
224
    TclpGetTime(&time);
225
    timerHandlerPtr->time.sec = time.sec + milliseconds/1000;
226
    timerHandlerPtr->time.usec = time.usec + (milliseconds%1000)*1000;
227
    if (timerHandlerPtr->time.usec >= 1000000) {
228
        timerHandlerPtr->time.usec -= 1000000;
229
        timerHandlerPtr->time.sec += 1;
230
    }
231
 
232
    /*
233
     * Fill in other fields for the event.
234
     */
235
 
236
    timerHandlerPtr->proc = proc;
237
    timerHandlerPtr->clientData = clientData;
238
    lastTimerId++;
239
    timerHandlerPtr->token = (Tcl_TimerToken) lastTimerId;
240
 
241
    /*
242
     * Add the event to the queue in the correct position
243
     * (ordered by event firing time).
244
     */
245
 
246
    for (tPtr2 = firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL;
247
            prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) {
248
        if ((tPtr2->time.sec > timerHandlerPtr->time.sec)
249
                || ((tPtr2->time.sec == timerHandlerPtr->time.sec)
250
                && (tPtr2->time.usec > timerHandlerPtr->time.usec))) {
251
            break;
252
        }
253
    }
254
    timerHandlerPtr->nextPtr = tPtr2;
255
    if (prevPtr == NULL) {
256
        firstTimerHandlerPtr = timerHandlerPtr;
257
    } else {
258
        prevPtr->nextPtr = timerHandlerPtr;
259
    }
260
 
261
    TimerSetupProc(NULL, TCL_ALL_EVENTS);
262
    return timerHandlerPtr->token;
263
}
264
 
265
/*
266
 *--------------------------------------------------------------
267
 *
268
 * Tcl_DeleteTimerHandler --
269
 *
270
 *      Delete a previously-registered timer handler.
271
 *
272
 * Results:
273
 *      None.
274
 *
275
 * Side effects:
276
 *      Destroy the timer callback identified by TimerToken,
277
 *      so that its associated procedure will not be called.
278
 *      If the callback has already fired, or if the given
279
 *      token doesn't exist, then nothing happens.
280
 *
281
 *--------------------------------------------------------------
282
 */
283
 
284
void
285
Tcl_DeleteTimerHandler(token)
286
    Tcl_TimerToken token;       /* Result previously returned by
287
                                 * Tcl_DeleteTimerHandler. */
288
{
289
    register TimerHandler *timerHandlerPtr, *prevPtr;
290
 
291
    for (timerHandlerPtr = firstTimerHandlerPtr, prevPtr = NULL;
292
            timerHandlerPtr != NULL; prevPtr = timerHandlerPtr,
293
            timerHandlerPtr = timerHandlerPtr->nextPtr) {
294
        if (timerHandlerPtr->token != token) {
295
            continue;
296
        }
297
        if (prevPtr == NULL) {
298
            firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
299
        } else {
300
            prevPtr->nextPtr = timerHandlerPtr->nextPtr;
301
        }
302
        ckfree((char *) timerHandlerPtr);
303
        return;
304
    }
305
}
306
 
307
/*
308
 *----------------------------------------------------------------------
309
 *
310
 * TimerSetupProc --
311
 *
312
 *      This function is called by Tcl_DoOneEvent to setup the timer
313
 *      event source for before blocking.  This routine checks both the
314
 *      idle and after timer lists.
315
 *
316
 * Results:
317
 *      None.
318
 *
319
 * Side effects:
320
 *      May update the maximum notifier block time.
321
 *
322
 *----------------------------------------------------------------------
323
 */
324
 
325
static void
326
TimerSetupProc(data, flags)
327
    ClientData data;            /* Not used. */
328
    int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
329
{
330
    Tcl_Time blockTime;
331
 
332
    if (((flags & TCL_IDLE_EVENTS) && idleList)
333
            || ((flags & TCL_TIMER_EVENTS) && timerPending)) {
334
        /*
335
         * There is an idle handler or a pending timer event, so just poll.
336
         */
337
 
338
        blockTime.sec = 0;
339
        blockTime.usec = 0;
340
 
341
    } else if ((flags & TCL_TIMER_EVENTS) && firstTimerHandlerPtr) {
342
        /*
343
         * Compute the timeout for the next timer on the list.
344
         */
345
 
346
        TclpGetTime(&blockTime);
347
        blockTime.sec = firstTimerHandlerPtr->time.sec - blockTime.sec;
348
        blockTime.usec = firstTimerHandlerPtr->time.usec - blockTime.usec;
349
        if (blockTime.usec < 0) {
350
            blockTime.sec -= 1;
351
            blockTime.usec += 1000000;
352
        }
353
        if (blockTime.sec < 0) {
354
            blockTime.sec = 0;
355
            blockTime.usec = 0;
356
        }
357
    } else {
358
        return;
359
    }
360
 
361
    Tcl_SetMaxBlockTime(&blockTime);
362
}
363
 
364
/*
365
 *----------------------------------------------------------------------
366
 *
367
 * TimerCheckProc --
368
 *
369
 *      This function is called by Tcl_DoOneEvent to check the timer
370
 *      event source for events.  This routine checks both the
371
 *      idle and after timer lists.
372
 *
373
 * Results:
374
 *      None.
375
 *
376
 * Side effects:
377
 *      May queue an event and update the maximum notifier block time.
378
 *
379
 *----------------------------------------------------------------------
380
 */
381
 
382
static void
383
TimerCheckProc(data, flags)
384
    ClientData data;            /* Not used. */
385
    int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
386
{
387
    Tcl_Event *timerEvPtr;
388
    Tcl_Time blockTime;
389
 
390
    if ((flags & TCL_TIMER_EVENTS) && firstTimerHandlerPtr) {
391
        /*
392
         * Compute the timeout for the next timer on the list.
393
         */
394
 
395
        TclpGetTime(&blockTime);
396
        blockTime.sec = firstTimerHandlerPtr->time.sec - blockTime.sec;
397
        blockTime.usec = firstTimerHandlerPtr->time.usec - blockTime.usec;
398
        if (blockTime.usec < 0) {
399
            blockTime.sec -= 1;
400
            blockTime.usec += 1000000;
401
        }
402
        if (blockTime.sec < 0) {
403
            blockTime.sec = 0;
404
            blockTime.usec = 0;
405
        }
406
 
407
        /*
408
         * If the first timer has expired, stick an event on the queue.
409
         */
410
 
411
        if (blockTime.sec == 0 && blockTime.usec == 0 && !timerPending) {
412
            timerPending = 1;
413
            timerEvPtr = (Tcl_Event *) ckalloc(sizeof(Tcl_Event));
414
            timerEvPtr->proc = TimerHandlerEventProc;
415
            Tcl_QueueEvent(timerEvPtr, TCL_QUEUE_TAIL);
416
        }
417
    }
418
}
419
 
420
/*
421
 *----------------------------------------------------------------------
422
 *
423
 * TimerHandlerEventProc --
424
 *
425
 *      This procedure is called by Tcl_ServiceEvent when a timer event
426
 *      reaches the front of the event queue.  This procedure handles
427
 *      the event by invoking the callbacks for all timers that are
428
 *      ready.
429
 *
430
 * Results:
431
 *      Returns 1 if the event was handled, meaning it should be removed
432
 *      from the queue.  Returns 0 if the event was not handled, meaning
433
 *      it should stay on the queue.  The only time the event isn't
434
 *      handled is if the TCL_TIMER_EVENTS flag bit isn't set.
435
 *
436
 * Side effects:
437
 *      Whatever the timer handler callback procedures do.
438
 *
439
 *----------------------------------------------------------------------
440
 */
441
 
442
static int
443
TimerHandlerEventProc(evPtr, flags)
444
    Tcl_Event *evPtr;           /* Event to service. */
445
    int flags;                  /* Flags that indicate what events to
446
                                 * handle, such as TCL_FILE_EVENTS. */
447
{
448
    TimerHandler *timerHandlerPtr, **nextPtrPtr;
449
    Tcl_Time time;
450
    int currentTimerId;
451
 
452
    /*
453
     * Do nothing if timers aren't enabled.  This leaves the event on the
454
     * queue, so we will get to it as soon as ServiceEvents() is called
455
     * with timers enabled.
456
     */
457
 
458
    if (!(flags & TCL_TIMER_EVENTS)) {
459
        return 0;
460
    }
461
 
462
    /*
463
     * The code below is trickier than it may look, for the following
464
     * reasons:
465
     *
466
     * 1. New handlers can get added to the list while the current
467
     *    one is being processed.  If new ones get added, we don't
468
     *    want to process them during this pass through the list to avoid
469
     *    starving other event sources.  This is implemented using the
470
     *    token number in the handler:  new handlers will have a
471
     *    newer token than any of the ones currently on the list.
472
     * 2. The handler can call Tcl_DoOneEvent, so we have to remove
473
     *    the handler from the list before calling it. Otherwise an
474
     *    infinite loop could result.
475
     * 3. Tcl_DeleteTimerHandler can be called to remove an element from
476
     *    the list while a handler is executing, so the list could
477
     *    change structure during the call.
478
     * 4. Because we only fetch the current time before entering the loop,
479
     *    the only way a new timer will even be considered runnable is if
480
     *    its expiration time is within the same millisecond as the
481
     *    current time.  This is fairly likely on Windows, since it has
482
     *    a course granularity clock.  Since timers are placed
483
     *    on the queue in time order with the most recently created
484
     *    handler appearing after earlier ones with the same expiration
485
     *    time, we don't have to worry about newer generation timers
486
     *    appearing before later ones.
487
     */
488
 
489
    timerPending = 0;
490
    currentTimerId = lastTimerId;
491
    TclpGetTime(&time);
492
    while (1) {
493
        nextPtrPtr = &firstTimerHandlerPtr;
494
        timerHandlerPtr = firstTimerHandlerPtr;
495
        if (timerHandlerPtr == NULL) {
496
            break;
497
        }
498
 
499
        if ((timerHandlerPtr->time.sec > time.sec)
500
                || ((timerHandlerPtr->time.sec == time.sec)
501
                        && (timerHandlerPtr->time.usec > time.usec))) {
502
            break;
503
        }
504
 
505
        /*
506
         * Bail out if the next timer is of a newer generation.
507
         */
508
 
509
        if ((currentTimerId - (int)timerHandlerPtr->token) < 0) {
510
            break;
511
        }
512
 
513
        /*
514
         * Remove the handler from the queue before invoking it,
515
         * to avoid potential reentrancy problems.
516
         */
517
 
518
        (*nextPtrPtr) = timerHandlerPtr->nextPtr;
519
        (*timerHandlerPtr->proc)(timerHandlerPtr->clientData);
520
        ckfree((char *) timerHandlerPtr);
521
    }
522
    TimerSetupProc(NULL, TCL_TIMER_EVENTS);
523
    return 1;
524
}
525
 
526
/*
527
 *--------------------------------------------------------------
528
 *
529
 * Tcl_DoWhenIdle --
530
 *
531
 *      Arrange for proc to be invoked the next time the system is
532
 *      idle (i.e., just before the next time that Tcl_DoOneEvent
533
 *      would have to wait for something to happen).
534
 *
535
 * Results:
536
 *      None.
537
 *
538
 * Side effects:
539
 *      Proc will eventually be called, with clientData as argument.
540
 *      See the manual entry for details.
541
 *
542
 *--------------------------------------------------------------
543
 */
544
 
545
void
546
Tcl_DoWhenIdle(proc, clientData)
547
    Tcl_IdleProc *proc;         /* Procedure to invoke. */
548
    ClientData clientData;      /* Arbitrary value to pass to proc. */
549
{
550
    register IdleHandler *idlePtr;
551
    Tcl_Time blockTime;
552
 
553
    if (!initialized) {
554
        InitTimer();
555
    }
556
 
557
    idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
558
    idlePtr->proc = proc;
559
    idlePtr->clientData = clientData;
560
    idlePtr->generation = idleGeneration;
561
    idlePtr->nextPtr = NULL;
562
    if (lastIdlePtr == NULL) {
563
        idleList = idlePtr;
564
    } else {
565
        lastIdlePtr->nextPtr = idlePtr;
566
    }
567
    lastIdlePtr = idlePtr;
568
 
569
    blockTime.sec = 0;
570
    blockTime.usec = 0;
571
    Tcl_SetMaxBlockTime(&blockTime);
572
}
573
 
574
/*
575
 *----------------------------------------------------------------------
576
 *
577
 * Tcl_CancelIdleCall --
578
 *
579
 *      If there are any when-idle calls requested to a given procedure
580
 *      with given clientData, cancel all of them.
581
 *
582
 * Results:
583
 *      None.
584
 *
585
 * Side effects:
586
 *      If the proc/clientData combination were on the when-idle list,
587
 *      they are removed so that they will never be called.
588
 *
589
 *----------------------------------------------------------------------
590
 */
591
 
592
void
593
Tcl_CancelIdleCall(proc, clientData)
594
    Tcl_IdleProc *proc;         /* Procedure that was previously registered. */
595
    ClientData clientData;      /* Arbitrary value to pass to proc. */
596
{
597
    register IdleHandler *idlePtr, *prevPtr;
598
    IdleHandler *nextPtr;
599
 
600
    for (prevPtr = NULL, idlePtr = idleList; idlePtr != NULL;
601
            prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) {
602
        while ((idlePtr->proc == proc)
603
                && (idlePtr->clientData == clientData)) {
604
            nextPtr = idlePtr->nextPtr;
605
            ckfree((char *) idlePtr);
606
            idlePtr = nextPtr;
607
            if (prevPtr == NULL) {
608
                idleList = idlePtr;
609
            } else {
610
                prevPtr->nextPtr = idlePtr;
611
            }
612
            if (idlePtr == NULL) {
613
                lastIdlePtr = prevPtr;
614
                return;
615
            }
616
        }
617
    }
618
}
619
 
620
/*
621
 *----------------------------------------------------------------------
622
 *
623
 * TclServiceIdle --
624
 *
625
 *      This procedure is invoked by the notifier when it becomes
626
 *      idle.  It will invoke all idle handlers that are present at
627
 *      the time the call is invoked, but not those added during idle
628
 *      processing.
629
 *
630
 * Results:
631
 *      The return value is 1 if TclServiceIdle found something to
632
 *      do, otherwise return value is 0.
633
 *
634
 * Side effects:
635
 *      Invokes all pending idle handlers.
636
 *
637
 *----------------------------------------------------------------------
638
 */
639
 
640
int
641
TclServiceIdle()
642
{
643
    IdleHandler *idlePtr;
644
    int oldGeneration;
645
    Tcl_Time blockTime;
646
 
647
    if (idleList == NULL) {
648
        return 0;
649
    }
650
 
651
    oldGeneration = idleGeneration;
652
    idleGeneration++;
653
 
654
    /*
655
     * The code below is trickier than it may look, for the following
656
     * reasons:
657
     *
658
     * 1. New handlers can get added to the list while the current
659
     *    one is being processed.  If new ones get added, we don't
660
     *    want to process them during this pass through the list (want
661
     *    to check for other work to do first).  This is implemented
662
     *    using the generation number in the handler:  new handlers
663
     *    will have a different generation than any of the ones currently
664
     *    on the list.
665
     * 2. The handler can call Tcl_DoOneEvent, so we have to remove
666
     *    the handler from the list before calling it. Otherwise an
667
     *    infinite loop could result.
668
     * 3. Tcl_CancelIdleCall can be called to remove an element from
669
     *    the list while a handler is executing, so the list could
670
     *    change structure during the call.
671
     */
672
 
673
    for (idlePtr = idleList;
674
            ((idlePtr != NULL)
675
                    && ((oldGeneration - idlePtr->generation) >= 0));
676
            idlePtr = idleList) {
677
        idleList = idlePtr->nextPtr;
678
        if (idleList == NULL) {
679
            lastIdlePtr = NULL;
680
        }
681
        (*idlePtr->proc)(idlePtr->clientData);
682
        ckfree((char *) idlePtr);
683
    }
684
    if (idleList) {
685
        blockTime.sec = 0;
686
        blockTime.usec = 0;
687
        Tcl_SetMaxBlockTime(&blockTime);
688
    }
689
    return 1;
690
}
691
 
692
/*
693
 *----------------------------------------------------------------------
694
 *
695
 * Tcl_AfterObjCmd --
696
 *
697
 *      This procedure is invoked to process the "after" Tcl command.
698
 *      See the user documentation for details on what it does.
699
 *
700
 * Results:
701
 *      A standard Tcl result.
702
 *
703
 * Side effects:
704
 *      See the user documentation.
705
 *
706
 *----------------------------------------------------------------------
707
 */
708
 
709
        /* ARGSUSED */
710
int
711
Tcl_AfterObjCmd(clientData, interp, objc, objv)
712
    ClientData clientData;      /* Points to the "tclAfter" assocData for
713
                                 * this interpreter, or NULL if the assocData
714
                                 * hasn't been created yet.*/
715
    Tcl_Interp *interp;         /* Current interpreter. */
716
    int objc;                   /* Number of arguments. */
717
    Tcl_Obj *CONST objv[];      /* Argument objects. */
718
{
719
    /*
720
     * The variable below is used to generate unique identifiers for
721
     * after commands.  This id can wrap around, which can potentially
722
     * cause problems.  However, there are not likely to be problems
723
     * in practice, because after commands can only be requested to
724
     * about a month in the future, and wrap-around is unlikely to
725
     * occur in less than about 1-10 years.  Thus it's unlikely that
726
     * any old ids will still be around when wrap-around occurs.
727
     */
728
 
729
    static int nextId = 1;
730
    int ms;
731
    AfterInfo *afterPtr;
732
    AfterAssocData *assocPtr = (AfterAssocData *) clientData;
733
    Tcl_CmdInfo cmdInfo;
734
    int length;
735
    char *arg;
736
    int index, result;
737
    static char *subCmds[] = {
738
        "cancel", "idle", "info",
739
        (char *) NULL};
740
 
741
    if (objc < 2) {
742
        Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
743
        return TCL_ERROR;
744
    }
745
 
746
    /*
747
     * Create the "after" information associated for this interpreter,
748
     * if it doesn't already exist.  Associate it with the command too,
749
     * so that it will be passed in as the ClientData argument in the
750
     * future.
751
     */
752
 
753
    if (assocPtr == NULL) {
754
        assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData));
755
        assocPtr->interp = interp;
756
        assocPtr->firstAfterPtr = NULL;
757
        Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc,
758
                (ClientData) assocPtr);
759
        cmdInfo.proc = NULL;
760
        cmdInfo.clientData = (ClientData) NULL;
761
        cmdInfo.objProc = Tcl_AfterObjCmd;
762
        cmdInfo.objClientData = (ClientData) assocPtr;
763
        cmdInfo.deleteProc = NULL;
764
        cmdInfo.deleteData = (ClientData) assocPtr;
765
        Tcl_SetCommandInfo(interp, Tcl_GetStringFromObj(objv[0], &length),
766
                &cmdInfo);
767
    }
768
 
769
    /*
770
     * First lets see if the command was passed a number as the first argument.
771
     */
772
 
773
    arg = Tcl_GetStringFromObj(objv[1], &length);
774
    if (isdigit(UCHAR(arg[0]))) {
775
        if (Tcl_GetIntFromObj(interp, objv[1], &ms) != TCL_OK) {
776
            return TCL_ERROR;
777
        }
778
        if (ms < 0) {
779
            ms = 0;
780
        }
781
        if (objc == 2) {
782
            Tcl_Sleep(ms);
783
            return TCL_OK;
784
        }
785
        afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
786
        afterPtr->assocPtr = assocPtr;
787
        if (objc == 3) {
788
            arg = Tcl_GetStringFromObj(objv[2], &length);
789
            afterPtr->command = (char *) ckalloc((unsigned) (length + 1));
790
            strcpy(afterPtr->command, arg);
791
        } else {
792
            Tcl_Obj *objPtr = Tcl_ConcatObj(objc-2, objv+2);
793
            arg = Tcl_GetStringFromObj(objPtr, &length);
794
            afterPtr->command = (char *) ckalloc((unsigned) (length + 1));
795
            strcpy(afterPtr->command, arg);
796
            Tcl_DecrRefCount(objPtr);
797
        }
798
        afterPtr->id = nextId;
799
        nextId += 1;
800
        afterPtr->token = Tcl_CreateTimerHandler(ms, AfterProc,
801
                (ClientData) afterPtr);
802
        afterPtr->nextPtr = assocPtr->firstAfterPtr;
803
        assocPtr->firstAfterPtr = afterPtr;
804
        sprintf(interp->result, "after#%d", afterPtr->id);
805
        return TCL_OK;
806
    }
807
 
808
    /*
809
     * If it's not a number it must be a subcommand.
810
     */
811
    result = Tcl_GetIndexFromObj(NULL, objv[1], subCmds, "option",
812
            0, (int *) &index);
813
    if (result != TCL_OK) {
814
        Tcl_AppendResult(interp, "bad argument \"", arg,
815
                "\": must be cancel, idle, info, or a number",
816
                (char *) NULL);
817
        return TCL_ERROR;
818
    }
819
 
820
    switch (index) {
821
        case 0:          /* cancel */
822
            {
823
                char *arg;
824
                Tcl_Obj *objPtr = NULL;
825
 
826
                if (objc < 3) {
827
                    Tcl_WrongNumArgs(interp, 2, objv, "id|command");
828
                    return TCL_ERROR;
829
                }
830
                if (objc == 3) {
831
                    arg = Tcl_GetStringFromObj(objv[2], &length);
832
                } else {
833
                    objPtr = Tcl_ConcatObj(objc-2, objv+2);;
834
                    arg = Tcl_GetStringFromObj(objPtr, &length);
835
                }
836
                for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
837
                     afterPtr = afterPtr->nextPtr) {
838
                    if (strcmp(afterPtr->command, arg) == 0) {
839
                        break;
840
                    }
841
                }
842
                if (afterPtr == NULL) {
843
                    afterPtr = GetAfterEvent(assocPtr, arg);
844
                }
845
                if (objPtr != NULL) {
846
                    Tcl_DecrRefCount(objPtr);
847
                }
848
                if (afterPtr != NULL) {
849
                    if (afterPtr->token != NULL) {
850
                        Tcl_DeleteTimerHandler(afterPtr->token);
851
                    } else {
852
                        Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
853
                    }
854
                    FreeAfterPtr(afterPtr);
855
                }
856
                break;
857
            }
858
        case 1:         /* idle */
859
            if (objc < 3) {
860
                Tcl_WrongNumArgs(interp, 2, objv, "script script ...");
861
                return TCL_ERROR;
862
            }
863
            afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
864
            afterPtr->assocPtr = assocPtr;
865
            if (objc == 3) {
866
                arg = Tcl_GetStringFromObj(objv[2], &length);
867
                afterPtr->command = (char *) ckalloc((unsigned) length + 1);
868
                strcpy(afterPtr->command, arg);
869
            } else {
870
                Tcl_Obj *objPtr = Tcl_ConcatObj(objc-2, objv+2);;
871
                arg = Tcl_GetStringFromObj(objPtr, &length);
872
                afterPtr->command = (char *) ckalloc((unsigned) (length + 1));
873
                strcpy(afterPtr->command, arg);
874
                Tcl_DecrRefCount(objPtr);
875
            }
876
            afterPtr->id = nextId;
877
            nextId += 1;
878
            afterPtr->token = NULL;
879
            afterPtr->nextPtr = assocPtr->firstAfterPtr;
880
            assocPtr->firstAfterPtr = afterPtr;
881
            Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr);
882
            sprintf(interp->result, "after#%d", afterPtr->id);
883
            break;
884
        case 2:         /* info */
885
            if (objc == 2) {
886
                char buffer[30];
887
 
888
                for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
889
                     afterPtr = afterPtr->nextPtr) {
890
                    if (assocPtr->interp == interp) {
891
                        sprintf(buffer, "after#%d", afterPtr->id);
892
                        Tcl_AppendElement(interp, buffer);
893
                    }
894
                }
895
                return TCL_OK;
896
            }
897
            if (objc != 3) {
898
                Tcl_WrongNumArgs(interp, 2, objv, "?id?");
899
                return TCL_ERROR;
900
            }
901
            arg = Tcl_GetStringFromObj(objv[2], &length);
902
            afterPtr = GetAfterEvent(assocPtr, arg);
903
            if (afterPtr == NULL) {
904
                Tcl_AppendResult(interp, "event \"", arg,
905
                        "\" doesn't exist", (char *) NULL);
906
                return TCL_ERROR;
907
            }
908
            Tcl_AppendElement(interp, afterPtr->command);
909
            Tcl_AppendElement(interp,
910
                    (afterPtr->token == NULL) ? "idle" : "timer");
911
            break;
912
    }
913
    return TCL_OK;
914
}
915
 
916
/*
917
 *----------------------------------------------------------------------
918
 *
919
 * GetAfterEvent --
920
 *
921
 *      This procedure parses an "after" id such as "after#4" and
922
 *      returns a pointer to the AfterInfo structure.
923
 *
924
 * Results:
925
 *      The return value is either a pointer to an AfterInfo structure,
926
 *      if one is found that corresponds to "string" and is for interp,
927
 *      or NULL if no corresponding after event can be found.
928
 *
929
 * Side effects:
930
 *      None.
931
 *
932
 *----------------------------------------------------------------------
933
 */
934
 
935
static AfterInfo *
936
GetAfterEvent(assocPtr, string)
937
    AfterAssocData *assocPtr;   /* Points to "after"-related information for
938
                                 * this interpreter. */
939
    char *string;               /* Textual identifier for after event, such
940
                                 * as "after#6". */
941
{
942
    AfterInfo *afterPtr;
943
    int id;
944
    char *end;
945
 
946
    if (strncmp(string, "after#", 6) != 0) {
947
        return NULL;
948
    }
949
    string += 6;
950
    id = strtoul(string, &end, 10);
951
    if ((end == string) || (*end != 0)) {
952
        return NULL;
953
    }
954
    for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
955
            afterPtr = afterPtr->nextPtr) {
956
        if (afterPtr->id == id) {
957
            return afterPtr;
958
        }
959
    }
960
    return NULL;
961
}
962
 
963
/*
964
 *----------------------------------------------------------------------
965
 *
966
 * AfterProc --
967
 *
968
 *      Timer callback to execute commands registered with the
969
 *      "after" command.
970
 *
971
 * Results:
972
 *      None.
973
 *
974
 * Side effects:
975
 *      Executes whatever command was specified.  If the command
976
 *      returns an error, then the command "bgerror" is invoked
977
 *      to process the error;  if bgerror fails then information
978
 *      about the error is output on stderr.
979
 *
980
 *----------------------------------------------------------------------
981
 */
982
 
983
static void
984
AfterProc(clientData)
985
    ClientData clientData;      /* Describes command to execute. */
986
{
987
    AfterInfo *afterPtr = (AfterInfo *) clientData;
988
    AfterAssocData *assocPtr = afterPtr->assocPtr;
989
    AfterInfo *prevPtr;
990
    int result;
991
    Tcl_Interp *interp;
992
 
993
    /*
994
     * First remove the callback from our list of callbacks;  otherwise
995
     * someone could delete the callback while it's being executed, which
996
     * could cause a core dump.
997
     */
998
 
999
    if (assocPtr->firstAfterPtr == afterPtr) {
1000
        assocPtr->firstAfterPtr = afterPtr->nextPtr;
1001
    } else {
1002
        for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
1003
                prevPtr = prevPtr->nextPtr) {
1004
            /* Empty loop body. */
1005
        }
1006
        prevPtr->nextPtr = afterPtr->nextPtr;
1007
    }
1008
 
1009
    /*
1010
     * Execute the callback.
1011
     */
1012
 
1013
    interp = assocPtr->interp;
1014
    Tcl_Preserve((ClientData) interp);
1015
    result = Tcl_GlobalEval(interp, afterPtr->command);
1016
    if (result != TCL_OK) {
1017
        Tcl_AddErrorInfo(interp, "\n    (\"after\" script)");
1018
        Tcl_BackgroundError(interp);
1019
    }
1020
    Tcl_Release((ClientData) interp);
1021
 
1022
    /*
1023
     * Free the memory for the callback.
1024
     */
1025
 
1026
    ckfree(afterPtr->command);
1027
    ckfree((char *) afterPtr);
1028
}
1029
 
1030
/*
1031
 *----------------------------------------------------------------------
1032
 *
1033
 * FreeAfterPtr --
1034
 *
1035
 *      This procedure removes an "after" command from the list of
1036
 *      those that are pending and frees its resources.  This procedure
1037
 *      does *not* cancel the timer handler;  if that's needed, the
1038
 *      caller must do it.
1039
 *
1040
 * Results:
1041
 *      None.
1042
 *
1043
 * Side effects:
1044
 *      The memory associated with afterPtr is released.
1045
 *
1046
 *----------------------------------------------------------------------
1047
 */
1048
 
1049
static void
1050
FreeAfterPtr(afterPtr)
1051
    AfterInfo *afterPtr;                /* Command to be deleted. */
1052
{
1053
    AfterInfo *prevPtr;
1054
    AfterAssocData *assocPtr = afterPtr->assocPtr;
1055
 
1056
    if (assocPtr->firstAfterPtr == afterPtr) {
1057
        assocPtr->firstAfterPtr = afterPtr->nextPtr;
1058
    } else {
1059
        for (prevPtr = assocPtr->firstAfterPtr; prevPtr->nextPtr != afterPtr;
1060
                prevPtr = prevPtr->nextPtr) {
1061
            /* Empty loop body. */
1062
        }
1063
        prevPtr->nextPtr = afterPtr->nextPtr;
1064
    }
1065
    ckfree(afterPtr->command);
1066
    ckfree((char *) afterPtr);
1067
}
1068
 
1069
/*
1070
 *----------------------------------------------------------------------
1071
 *
1072
 * AfterCleanupProc --
1073
 *
1074
 *      This procedure is invoked whenever an interpreter is deleted
1075
 *      to cleanup the AssocData for "tclAfter".
1076
 *
1077
 * Results:
1078
 *      None.
1079
 *
1080
 * Side effects:
1081
 *      After commands are removed.
1082
 *
1083
 *----------------------------------------------------------------------
1084
 */
1085
 
1086
        /* ARGSUSED */
1087
static void
1088
AfterCleanupProc(clientData, interp)
1089
    ClientData clientData;      /* Points to AfterAssocData for the
1090
                                 * interpreter. */
1091
    Tcl_Interp *interp;         /* Interpreter that is being deleted. */
1092
{
1093
    AfterAssocData *assocPtr = (AfterAssocData *) clientData;
1094
    AfterInfo *afterPtr;
1095
 
1096
    while (assocPtr->firstAfterPtr != NULL) {
1097
        afterPtr = assocPtr->firstAfterPtr;
1098
        assocPtr->firstAfterPtr = afterPtr->nextPtr;
1099
        if (afterPtr->token != NULL) {
1100
            Tcl_DeleteTimerHandler(afterPtr->token);
1101
        } else {
1102
            Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
1103
        }
1104
        ckfree(afterPtr->command);
1105
        ckfree((char *) afterPtr);
1106
    }
1107
    ckfree((char *) assocPtr);
1108
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.