1 |
/* Routines for time-delayed actions. |
2 |
* |
3 |
* IRC Services is copyright (c) 1996-2009 Andrew Church. |
4 |
* E-mail: <achurch@achurch.org> |
5 |
* Parts written by Andrew Kempe and others. |
6 |
* This program is free but copyrighted software; see the file GPL.txt for |
7 |
* details. |
8 |
*/ |
9 |
|
10 |
#include "services.h" |
11 |
#define IN_TIMEOUT_C |
12 |
#include "timeout.h" |
13 |
|
14 |
/*************************************************************************/ |
15 |
|
16 |
static Timeout *timeouts = NULL; |
17 |
static int checking_timeouts = 0; |
18 |
|
19 |
/*************************************************************************/ |
20 |
|
21 |
#ifdef DEBUG_COMMANDS |
22 |
|
23 |
/* Send the timeout list to the given user. */ |
24 |
|
25 |
void send_timeout_list(User *u) |
26 |
{ |
27 |
Timeout *to; |
28 |
uint32 now = time_msec(); |
29 |
|
30 |
notice(ServerName, u->nick, "Now: %u.%03u", now/1000, now%1000); |
31 |
LIST_FOREACH (to, timeouts) { |
32 |
notice(ServerName, u->nick, "%p: %u.%03u: %p (%p)", |
33 |
to, to->timeout/1000, to->timeout%1000, to->code, to->data); |
34 |
} |
35 |
} |
36 |
|
37 |
#endif /* DEBUG_COMMANDS */ |
38 |
|
39 |
/*************************************************************************/ |
40 |
|
41 |
/* Check the timeout list for any pending actions. */ |
42 |
|
43 |
void check_timeouts(void) |
44 |
{ |
45 |
Timeout *to, *to2; |
46 |
uint32 now = time_msec(); |
47 |
|
48 |
if (checking_timeouts) |
49 |
fatal("check_timeouts() called recursively!"); |
50 |
checking_timeouts = 1; |
51 |
log_debug(2, "Checking timeouts at time_msec = %u.%03u", |
52 |
now/1000, now%1000); |
53 |
|
54 |
LIST_FOREACH_SAFE (to, timeouts, to2) { |
55 |
if (to->timeout) { |
56 |
if ((int32)(to->timeout - now) > 0) |
57 |
continue; |
58 |
log_debug(3, "Running timeout %p (code=%p repeat=%d)", |
59 |
to, to->code, to->repeat); |
60 |
to->code(to); |
61 |
if (to->repeat) { |
62 |
to->timeout = now + to->repeat; |
63 |
if (!to->timeout) /* watch out for zero! */ |
64 |
to->timeout++; |
65 |
continue; |
66 |
} |
67 |
} |
68 |
LIST_REMOVE(to, timeouts); |
69 |
free(to); |
70 |
} |
71 |
|
72 |
log_debug(2, "Finished timeout list"); |
73 |
checking_timeouts = 0; |
74 |
} |
75 |
|
76 |
/*************************************************************************/ |
77 |
|
78 |
/* Add a timeout to the list to be triggered in `delay' seconds. If |
79 |
* `repeat' is nonzero, do not delete the timeout after it is triggered. |
80 |
* This must maintain the property that timeouts added from within a |
81 |
* timeout routine do not get checked during that run of the timeout list. |
82 |
*/ |
83 |
|
84 |
Timeout *add_timeout(int delay, void (*code)(Timeout *), int repeat) |
85 |
{ |
86 |
if (delay < 0) { |
87 |
log("add_timeout(): called with a negative delay! (%d)", delay); |
88 |
return NULL; |
89 |
} |
90 |
if (!code) { |
91 |
log("add_timeout(): called with code==NULL!"); |
92 |
return NULL; |
93 |
} |
94 |
if (delay > 2147483) { /* 2^31/1000 (watch out for difference overflow) */ |
95 |
log("add_timeout(): delay (%ds) too long, shortening to 2147483s", |
96 |
delay); |
97 |
delay = 2147483; |
98 |
} |
99 |
return add_timeout_ms((uint32)delay*1000, code, repeat); |
100 |
} |
101 |
|
102 |
/*************************************************************************/ |
103 |
|
104 |
/* The same thing, but using milliseconds instead of seconds. */ |
105 |
|
106 |
Timeout *add_timeout_ms(uint32 delay, void (*code)(Timeout *), int repeat) |
107 |
{ |
108 |
Timeout *t; |
109 |
|
110 |
if (!code) { |
111 |
log("add_timeout_ms(): called with code==NULL!"); |
112 |
return NULL; |
113 |
} |
114 |
if (delay > 2147483647) { |
115 |
log("add_timeout_ms(): delay (%dms) too long, shortening to" |
116 |
" 2147483647ms", delay); |
117 |
delay = 2147483647; |
118 |
} |
119 |
t = malloc(sizeof(Timeout)); |
120 |
if (!t) |
121 |
return NULL; |
122 |
t->settime = time(NULL); |
123 |
t->timeout = time_msec() + delay; |
124 |
/* t->timeout==0 is used to signal that the timeout should be deleted; |
125 |
* if the timeout value just happens to wrap around to 0, lengthen it |
126 |
* by a millisecond. */ |
127 |
if (!t->timeout) |
128 |
t->timeout++; |
129 |
t->code = code; |
130 |
t->data = NULL; |
131 |
t->repeat = repeat ? delay : 0; |
132 |
LIST_INSERT(t, timeouts); |
133 |
return t; |
134 |
} |
135 |
|
136 |
/*************************************************************************/ |
137 |
|
138 |
/* Remove a timeout from the list (if it's there). */ |
139 |
|
140 |
void del_timeout(Timeout *t) |
141 |
{ |
142 |
Timeout *ptr; |
143 |
|
144 |
if (!t) { |
145 |
log("del_timeout(): called with t==NULL!"); |
146 |
return; |
147 |
} |
148 |
LIST_FOREACH (ptr, timeouts) { |
149 |
if (ptr == t) |
150 |
break; |
151 |
} |
152 |
if (!ptr) { |
153 |
log("del_timeout(): attempted to remove timeout %p (not on list)", t); |
154 |
return; |
155 |
} |
156 |
if (checking_timeouts) { |
157 |
t->timeout = 0; /* delete it when we hit it in the list */ |
158 |
return; |
159 |
} |
160 |
LIST_REMOVE(t, timeouts); |
161 |
free(t); |
162 |
} |
163 |
|
164 |
/*************************************************************************/ |
165 |
|
166 |
/* |
167 |
* Local variables: |
168 |
* c-file-style: "stroustrup" |
169 |
* c-file-offsets: ((case-label . *) (statement-case-intro . *)) |
170 |
* indent-tabs-mode: nil |
171 |
* End: |
172 |
* |
173 |
* vim: expandtab shiftwidth=4: |
174 |
*/ |