~ubuntu-branches/ubuntu/trusty/librep/trusty

« back to all changes in this revision

Viewing changes to src/timers.c

  • Committer: Bazaar Package Importer
  • Author(s): Christian Marillat
  • Date: 2001-11-13 15:06:22 UTC
  • Revision ID: james.westby@ubuntu.com-20011113150622-vgmgmk6srj3kldr3
Tags: upstream-0.15.2
ImportĀ upstreamĀ versionĀ 0.15.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* timers.c -- call a function after a period of time has passed
 
2
   Copyright (C) 1999 John Harper <john@dcs.warwick.ac.uk>
 
3
   $Id: timers.c,v 1.14 2001/08/02 07:12:36 jsh Exp $
 
4
 
 
5
   This file is part of librep.
 
6
 
 
7
   librep is free software; you can redistribute it and/or modify it
 
8
   under the terms of the GNU General Public License as published by
 
9
   the Free Software Foundation; either version 2, or (at your option)
 
10
   any later version.
 
11
 
 
12
   librep is distributed in the hope that it will be useful, but
 
13
   WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
   GNU General Public License for more details.
 
16
 
 
17
   You should have received a copy of the GNU General Public License
 
18
   along with librep; see the file COPYING.     If not, write to
 
19
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
20
 
 
21
#define _GNU_SOURCE
 
22
 
 
23
/* AIX requires this to be the first thing in the file.  */
 
24
#include <config.h>
 
25
#ifdef __GNUC__
 
26
# define alloca __builtin_alloca
 
27
#else
 
28
# if HAVE_ALLOCA_H
 
29
#  include <alloca.h>
 
30
# else
 
31
#  ifdef _AIX
 
32
 #pragma alloca
 
33
#  else
 
34
#   ifndef alloca /* predefined by HP cc +Olibcalls */
 
35
char *alloca ();
 
36
#   endif
 
37
#  endif
 
38
# endif
 
39
#endif
 
40
 
 
41
#include "repint.h"
 
42
#include <signal.h>
 
43
#include <time.h>
 
44
#include <assert.h>
 
45
 
 
46
#ifdef HAVE_UNISTD_H
 
47
# include <unistd.h>
 
48
#endif
 
49
 
 
50
#ifdef HAVE_SYS_TIME_H
 
51
# include <sys/time.h>
 
52
#endif
 
53
 
 
54
static int timer_type;
 
55
 
 
56
#define TIMER(v)  ((Lisp_Timer *)rep_PTR(v))
 
57
#define TIMERP(v) rep_CELL16_TYPEP(v, timer_type)
 
58
 
 
59
typedef struct lisp_timer {
 
60
    repv car;
 
61
    struct lisp_timer *next;
 
62
    struct lisp_timer *next_alloc;
 
63
    repv function;
 
64
    long secs, msecs;
 
65
    long rel_secs, rel_msecs;
 
66
    u_int fired : 1;
 
67
    u_int deleted : 1;
 
68
} Lisp_Timer;
 
69
 
 
70
/* List of all allocated timer objects, linked through next_alloc field */
 
71
static Lisp_Timer *allocated_timers;
 
72
 
 
73
/* List of all pending timers, linked through next field. Only ever
 
74
   touch this variable if SIGALRM is blocked! */
 
75
static Lisp_Timer *timer_chain;
 
76
 
 
77
/* Pipe used to trigger the input callback */
 
78
static int pipe_fds[2];
 
79
 
 
80
/* Contains SIGALRM */
 
81
static sigset_t alrm_sigset;
 
82
 
 
83
 
 
84
 
 
85
static RETSIGTYPE
 
86
timer_signal_handler (int sig)
 
87
{
 
88
    int dummy = 0;
 
89
    Lisp_Timer *t = timer_chain;
 
90
    assert (t != 0);
 
91
    t->rel_secs = t->rel_msecs = 0;
 
92
    while (t != 0 && t->rel_secs == 0 && t->rel_msecs == 0)
 
93
    {
 
94
        t->fired = 1;
 
95
        t = t->next;
 
96
    }
 
97
    write (pipe_fds[1], &dummy, sizeof (dummy));
 
98
}
 
99
 
 
100
/* only call with SIGALRM blocked */
 
101
static void
 
102
setup_next_timer (void)
 
103
{
 
104
    if (timer_chain != 0
 
105
        && (timer_chain->rel_secs > 0 || timer_chain->rel_msecs > 0))
 
106
    {
 
107
#ifdef HAVE_SETITIMER
 
108
        struct itimerval it, tem;
 
109
        it.it_interval.tv_usec = 0;
 
110
        it.it_interval.tv_sec = 0;
 
111
        it.it_value.tv_usec = timer_chain->rel_msecs * 1000;
 
112
        it.it_value.tv_sec = timer_chain->rel_secs;
 
113
        setitimer (ITIMER_REAL, &it, &tem);
 
114
#else
 
115
        alarm (timer_chain->secs);
 
116
#endif
 
117
        signal (SIGALRM, timer_signal_handler);
 
118
    }
 
119
    else
 
120
        signal (SIGALRM, SIG_IGN);
 
121
}
 
122
 
 
123
static inline void
 
124
fix_time (long *secs, long *msecs)
 
125
{
 
126
    while (*msecs < 0)
 
127
    {
 
128
        *msecs += 1000;
 
129
        (*secs)--;
 
130
    }
 
131
    while (*msecs > 1000)
 
132
    {
 
133
        *msecs -= 1000;
 
134
        (*secs)++;
 
135
    }
 
136
}
 
137
 
 
138
static void
 
139
insert_timer (Lisp_Timer *t)
 
140
{
 
141
    sigset_t old;
 
142
    sigprocmask (SIG_BLOCK, &alrm_sigset, &old);
 
143
    if (t->secs > 0 || t->msecs > 0)
 
144
    {
 
145
        Lisp_Timer **x;
 
146
        t->rel_secs = t->secs;
 
147
        t->rel_msecs = t->msecs;
 
148
        t->fired = 0;
 
149
        t->deleted = 0;
 
150
        x = &timer_chain;
 
151
        while (*x != 0
 
152
               && ((*x)->rel_secs < t->rel_secs
 
153
                   || ((*x)->rel_secs == t->rel_secs
 
154
                       && (*x)->rel_msecs <= t->rel_msecs)))
 
155
        {
 
156
            t->rel_msecs -= (*x)->rel_msecs;
 
157
            t->rel_secs -= (*x)->rel_secs;
 
158
            fix_time (&t->rel_secs, &t->rel_msecs);
 
159
            x = &((*x)->next);
 
160
        }
 
161
        if (*x != 0)
 
162
        {
 
163
            (*x)->rel_msecs -= t->rel_msecs;
 
164
            (*x)->rel_secs -= t->rel_secs;
 
165
            fix_time (&(*x)->rel_secs, &(*x)->rel_msecs);
 
166
        }
 
167
        t->next = *x;
 
168
        *x = t;
 
169
        if (timer_chain == t)
 
170
            setup_next_timer ();
 
171
    }
 
172
    sigprocmask (SIG_SETMASK, &old, 0);
 
173
}
 
174
 
 
175
static void
 
176
delete_timer (Lisp_Timer *t)
 
177
{
 
178
    Lisp_Timer **x;
 
179
    sigset_t old;
 
180
 
 
181
    sigprocmask (SIG_BLOCK, &alrm_sigset, &old);
 
182
    t->deleted = 1;
 
183
    x = &timer_chain;
 
184
    while (*x != 0 && (*x) != t)
 
185
        x = &((*x)->next);
 
186
    if (*x == t)
 
187
    {
 
188
        if (t->next != 0)
 
189
        {
 
190
            t->next->rel_msecs += t->rel_msecs;
 
191
            t->next->rel_secs += t->rel_secs;
 
192
            fix_time (&t->next->rel_secs, &t->next->rel_msecs);
 
193
        }
 
194
        t->rel_secs = t->rel_msecs = 0;
 
195
        *x = t->next;
 
196
        if (x == &timer_chain)
 
197
            setup_next_timer ();
 
198
    }
 
199
    sigprocmask (SIG_SETMASK, &old, 0);
 
200
}
 
201
 
 
202
static void
 
203
timer_fd_handler (int fd)
 
204
{
 
205
    int dummy;
 
206
    int ready, i;
 
207
    repv *timers;
 
208
    rep_GC_n_roots gc_timers;
 
209
    Lisp_Timer *t;
 
210
    sigset_t old;
 
211
 
 
212
    read (pipe_fds[0], &dummy, sizeof (dummy));
 
213
    sigprocmask (SIG_BLOCK, &alrm_sigset, &old);
 
214
    ready = 0;
 
215
    for (t = timer_chain; t != 0 && t->fired; t = t->next)
 
216
        ready++;
 
217
    timers = alloca (sizeof (repv) * ready);
 
218
    for (i = 0; i < ready; i++)
 
219
    {
 
220
        timers[i] = rep_VAL(timer_chain);
 
221
        timer_chain = timer_chain->next;
 
222
    }
 
223
    setup_next_timer ();
 
224
    sigprocmask (SIG_SETMASK, &old, 0);
 
225
    rep_PUSHGCN(gc_timers, timers, ready);
 
226
    for (i = 0; i < ready; i++)
 
227
    {
 
228
        if (!TIMER(timers[i])->deleted)
 
229
            rep_call_lisp1 (TIMER(timers[i])->function, timers[i]);
 
230
    }
 
231
    rep_POPGCN;
 
232
}
 
233
 
 
234
 
 
235
/* Lisp interface */
 
236
 
 
237
DEFUN("make-timer", Fmake_timer, Smake_timer,
 
238
      (repv fun, repv secs, repv msecs), rep_Subr3) /*
 
239
::doc:rep.io.timers#make-timer::
 
240
make-timer FUNCTION [SECONDS] [MILLISECONDS]
 
241
 
 
242
Create and return a new one-shot timer object. After SECONDS*1000 +
 
243
MILLISECONDS milliseconds FUNCTION will be called.
 
244
 
 
245
Note that the timer will only fire _once_, use the `set-timer' function
 
246
to re-enable it.
 
247
::end:: */
 
248
{
 
249
    Lisp_Timer *t = rep_ALLOC_CELL (sizeof (Lisp_Timer));
 
250
    rep_data_after_gc += sizeof (Lisp_Timer);
 
251
    t->car = timer_type;
 
252
    t->function = fun;
 
253
    t->secs = rep_get_long_int (secs);
 
254
    t->msecs = rep_get_long_int (msecs);
 
255
    t->next_alloc = allocated_timers;
 
256
    allocated_timers = t;
 
257
    insert_timer (t);
 
258
    return rep_VAL(t);
 
259
}
 
260
 
 
261
DEFUN("delete-timer", Fdelete_timer, Sdelete_timer, (repv timer), rep_Subr1) /*
 
262
::doc:rep.io.timers#delete-timer::
 
263
delete-timer TIMER
 
264
 
 
265
Prevent the one-shot timer TIMER from firing (i.e. calling the function
 
266
associated with it). If the timer has already fired, this function has
 
267
no effect.
 
268
::end:: */
 
269
{
 
270
    rep_DECLARE1(timer, TIMERP);
 
271
    delete_timer (TIMER(timer));
 
272
    return timer;
 
273
}
 
274
 
 
275
DEFUN("set-timer", Fset_timer, Sset_timer,
 
276
      (repv timer, repv secs, repv msecs), rep_Subr3) /*
 
277
::doc:rep.io.timers#set-timer::
 
278
set-timer TIMER [SECONDS] [MILLISECONDS]
 
279
 
 
280
Restart the one-shot timer TIMER. If SECONDS and/or MILLISECONDS is
 
281
defined the period after which it fires will be reset to the specified
 
282
duration. Otherwise, the existing values are preserved.
 
283
::end:: */
 
284
{
 
285
    rep_DECLARE1(timer, TIMERP);
 
286
    rep_DECLARE2_OPT(secs, rep_NUMERICP);
 
287
    rep_DECLARE3_OPT(msecs, rep_NUMERICP);
 
288
    delete_timer (TIMER(timer));
 
289
    if (secs != Qnil || msecs != Qnil)
 
290
    {
 
291
        TIMER(timer)->secs = rep_get_long_int (secs);
 
292
        TIMER(timer)->msecs = rep_get_long_int (msecs);
 
293
    }
 
294
    insert_timer (TIMER(timer));
 
295
    return timer;
 
296
}
 
297
 
 
298
 
 
299
/* Type hooks */
 
300
 
 
301
static void
 
302
timer_mark (repv val)
 
303
{
 
304
    rep_MARKVAL (TIMER(val)->function);
 
305
}
 
306
 
 
307
static void
 
308
timer_mark_active (void)
 
309
{
 
310
    Lisp_Timer *t;
 
311
    sigset_t old;
 
312
    sigprocmask (SIG_BLOCK, &alrm_sigset, &old);
 
313
    t = timer_chain;
 
314
    while (t != 0)
 
315
    {
 
316
        rep_MARKVAL (rep_VAL(t));
 
317
        t = t->next;
 
318
    }
 
319
    sigprocmask (SIG_SETMASK, &old, 0);
 
320
}
 
321
 
 
322
static void
 
323
timer_sweep (void)
 
324
{
 
325
    Lisp_Timer *x = allocated_timers;
 
326
    allocated_timers = 0;
 
327
    while (x != 0)
 
328
    {
 
329
        Lisp_Timer *next = x->next_alloc;
 
330
        if (!rep_GC_CELL_MARKEDP (rep_VAL(x)))
 
331
            rep_FREE_CELL (x);
 
332
        else
 
333
        {
 
334
            rep_GC_CLR_CELL (rep_VAL(x));
 
335
            x->next_alloc = allocated_timers;
 
336
            allocated_timers = x;
 
337
        }
 
338
        x = next;
 
339
    }
 
340
}
 
341
 
 
342
static void
 
343
timer_print (repv stream, repv arg)
 
344
{
 
345
    char buf[64];
 
346
#ifdef HAVE_SNPRINTF
 
347
    snprintf (buf, sizeof (buf), "#<timer %lds, %ldms>",
 
348
              TIMER(arg)->secs, TIMER(arg)->msecs);
 
349
#else
 
350
    sprintf (buf, "#<timer %lds, %ldms>", TIMER(arg)->secs, TIMER(arg)->msecs);
 
351
#endif
 
352
    rep_stream_puts (stream, buf, -1, rep_FALSE);
 
353
}
 
354
 
 
355
 
 
356
/* DL hooks */
 
357
 
 
358
repv
 
359
rep_dl_init (void)
 
360
{
 
361
    repv tem;
 
362
    timer_type = rep_register_new_type ("timer", 0, timer_print, timer_print,
 
363
                                        timer_sweep, timer_mark,
 
364
                                        timer_mark_active, 0, 0, 0, 0, 0, 0);
 
365
    pipe (pipe_fds);
 
366
    rep_register_input_fd (pipe_fds[0], timer_fd_handler);
 
367
#ifdef HAVE_UNIX
 
368
    rep_unix_set_fd_cloexec (pipe_fds[1]);
 
369
#endif
 
370
    sigemptyset (&alrm_sigset);
 
371
    sigaddset (&alrm_sigset, SIGALRM);
 
372
    rep_sig_restart (SIGALRM, rep_TRUE);
 
373
 
 
374
    tem = rep_push_structure ("rep.io.timers");
 
375
    /* ::alias:timers rep.io.timers:: */
 
376
    rep_alias_structure ("timers");
 
377
    rep_ADD_SUBR(Smake_timer);
 
378
    rep_ADD_SUBR(Sdelete_timer);
 
379
    rep_ADD_SUBR(Sset_timer);
 
380
    return rep_pop_structure (tem);
 
381
}
 
382
 
 
383
void
 
384
rep_dl_kill (void)
 
385
{
 
386
    rep_deregister_input_fd (pipe_fds[0]);
 
387
    close (pipe_fds[0]);
 
388
    close (pipe_fds[1]);
 
389
    signal (SIGALRM, SIG_IGN);
 
390
}