]> jfr.im git - irc/quakenet/newserv.git/blame - core/schedule.c
Compiler warning fixes
[irc/quakenet/newserv.git] / core / schedule.c
CommitLineData
2c5db955
CP
1/* schedule.c */
2
3#include "schedule.h"
4#include "error.h"
5#include "hooks.h"
6#include "../lib/array.h"
7#include <string.h>
8#include <stdlib.h>
9#include <stdio.h>
10#include <assert.h>
11
12#define INITSCHEDSIZE 1000
13#define GROWSCHEDSIZE 500
14
15#undef SCHEDDEBUG
16
17schedule **events;
18int heapsize;
19int heapmax;
20
21int schedadds;
22int scheddels;
23int scheddelfast;
24int schedexes;
25
26/* Local prototypes */
27void schedulestats(int hooknum, void *arg);
28
29void initschedule() {
30 initschedulealloc();
31 events=NULL;
32 schedadds=scheddels=schedexes=scheddelfast=0;
33 registerhook(HOOK_CORE_STATSREQUEST, &schedulestats);
34 heapsize=0;
35 heapmax=INITSCHEDSIZE;
36 events=(schedule **)malloc(INITSCHEDSIZE*sizeof(schedule *));
37}
38
39void schedule_heapify(int index) {
40 int firstindex=index;
41 schedule *ep;
42
43 /* If this node is a leaf, do nothing */
44 if ((index*2)+1 >= heapsize) {
45 return;
46 }
47
48 /* Check left child */
49 if (events[index]->nextschedule > events[(index*2)+1]->nextschedule) {
50 firstindex=(index*2)+1;
51 }
52
53 /* Check right (if exists) */
54 if ((index*2)+2 < heapsize) {
55 if (events[firstindex]->nextschedule > events[(index*2)+2]->nextschedule) {
56 firstindex=(index*2)+2;
57 }
58 }
59
60 /* If both children were scheduled after us, we're done */
61 if (firstindex==index) {
62 return;
63 }
64
65 /* Swap the two pointers around in the heap */
66 ep=events[firstindex];
67 events[firstindex]=events[index];
68 events[index]=ep;
69
70 /* Fix up the "index" field in the structures */
71 events[firstindex]->index=firstindex;
72 events[index]->index=index;
73
74 schedule_heapify(firstindex);
75}
76
77void insertschedule (schedule *sp) {
78 int mypos,myparent;
79
80 schedadds++;
81
82 if (heapsize>=heapmax) {
83 /* We need to grow the heap */
84 heapmax+=GROWSCHEDSIZE;
85 events=(schedule **)realloc((void *)events,heapmax*sizeof(schedule *));
86 }
87
88 mypos=heapsize++;
89
90 /* Travel up the heap looking for a slot for this new element */
91 /* mypos points at a (vacant) candidate space; we either put the element
92 * in this space, or pull it's parent down and try again with it's parent's space */
93 for (;;) {
94 myparent=(mypos-1)/2;
95 if (mypos==0 || (sp->nextschedule >= events[myparent]->nextschedule)) {
96 /* We reached the top, or our parent is scheduled before us -- end */
97 events[mypos]=sp;
98 sp->index=mypos;
99 break;
100 } else {
101 /* Pull the parent into this space and move up the heap */
102 events[mypos]=events[myparent];
103 events[mypos]->index=mypos;
104 mypos=myparent;
105 }
106 }
107}
108
109void schedule_remove (int index) {
110 int mypos,myparent;
111 schedule *sp;
112
113 assert(index<heapsize);
114
115#ifdef SCHEDDEBUG
116 Error("schedule",ERR_DEBUG,"schedule_remove: %d",index);
117#endif
118
119 if (index<0)
120 return;
121
122 scheddels++;
123 heapsize--;
124
125 /* Move the last element into the position we just deleted, then heapify
126 * If we happen to be deleting the last element, do nothing */
127 if (index!=heapsize) {
128 events[index]->index=-1;
129 events[index]=events[heapsize];
130 events[index]->index=index;
131 schedule_heapify(index);
132
133 /* Now we may need to float the element up the heap, similar to the insert case */
134 mypos=index;
135 for (;;) {
136 myparent=(mypos-1)/2;
137 if (mypos==0 || (events[mypos]->nextschedule >= events[myparent]->nextschedule)) {
138 break;
139 } else {
140 /* Swap the element up the tree */
141 sp=events[myparent];
142 events[myparent]=events[mypos];
143 events[mypos]=sp;
144 /* Fix up the index members */
145 events[myparent]->index=myparent;
146 events[mypos]->index=mypos;
147
148 mypos=myparent;
149 }
150 }
151 }
152}
153
154void *scheduleoneshot(time_t when, ScheduleCallback callback, void *arg) {
155 schedule *sp;
156
157 sp=getschedule();
158
159 sp->nextschedule=when;
160 sp->type=SCHEDULE_ONESHOT;
161 sp->repeatinterval=0;
162 sp->repeatcount=1;
163 sp->callback=callback;
164 sp->callbackparam=arg;
165
166 insertschedule(sp);
167
168#ifdef SCHEDDEBUG
169 Error("schedule",ERR_DEBUG,"scheduleoneshot: (%ld, %x, %x) = %x",when, callback, arg, sp);
170#endif
171
172 return (void *)sp;
173}
174
175void *schedulerecurring(time_t first, int count, time_t interval, ScheduleCallback callback, void *arg) {
176 schedule *sp;
177
178 if (count==1) {
179 return scheduleoneshot(first, callback, arg);
180 }
181
182 sp=getschedule();
183
184 sp->nextschedule=first;
185 sp->type=SCHEDULE_REPEATING;
186 sp->repeatinterval=interval;
187 sp->repeatcount=(count-1);
188 sp->callback=callback;
189 sp->callbackparam=arg;
190
191 insertschedule(sp);
192
193 return (void *)sp;
194}
195
196void deleteschedule(void *sch, ScheduleCallback callback, void *arg) {
197 schedule *sp;
198 int i;
199
200 /* New (optional) faster path: Clients can track the schedule pointer if they wish and
201 * pass it in here for an O(1) *cough* O(lg n) delete */
202
203#ifdef SCHEDDEBUG
204 Error("schedule",ERR_DEBUG,"deleteschedule(%x,%x,%x)",sch,callback, arg);
205#endif
206
207 if (sch) {
208 sp=(schedule *)sch;
209 /* Double check the params are correct:
210 * because we recycle and never free schedule structs it's
211 * actually OK to try and delete a schedule that has been executed... */
212
213 if (sp->callback==callback && sp->callbackparam==arg) {
214 scheddelfast++;
215 schedule_remove(sp->index);
216 freeschedule(sp);
217 }
218 return;
219 }
220
221 /* Argh, have to find it by brute force */
222
223 for(i=0;i<heapsize;i++) {
224 if ((events[i]->callback==callback) && (events[i]->callbackparam==arg)) {
225 sp=events[i];
226 schedule_remove(sp->index);
227 freeschedule(sp);
228 return;
229 }
230 }
231}
232
233void deleteallschedules(ScheduleCallback callback) {
234 schedule *sp;
235 int i;
236
237trydel:
238 /* OK, this gets to be REALLY cheesy and stupidly slow as well */
239
240 for(i=0;i<heapsize;i++) {
241 if (events[i]->callback==callback) {
242 sp=events[i];
243 schedule_remove(sp->index);
244 freeschedule(sp);
245 goto trydel;
246 }
247 }
248}
249
250void doscheduledevents(time_t when) {
251 void *arg;
252 ScheduleCallback sc;
253 schedule *sp;
254
255 while (heapsize && events[0] && events[0]->nextschedule <= when) {
256 /* Pick out the first element first */
257 sp=events[0];
258 sp->index=-1; /* Invalidate index so that an explicit delete doesn't screw us up */
259
260 /* Remove from the top of the heap */
261 heapsize--;
262 events[0]=events[heapsize];
263 events[0]->index=0;
264 schedule_heapify(0);
265
266 if (sp->callback==NULL) {
267 Error("core",ERR_ERROR,"Tried to call NULL function in doscheduledevents(): (%x, %x, %x)",sp,sp->callback,sp->callbackparam);
268 continue;
269 }
270
271 /* Store the callback */
272 arg=(sp->callbackparam);
273 sc=(sp->callback);
274
275 /* Update the structures _before_ doing the callback.. */
276 switch(sp->type) {
277 case SCHEDULE_ONESHOT:
278 freeschedule(sp);
279 break;
280
281 case SCHEDULE_REPEATING:
282 sp->nextschedule+=sp->repeatinterval;
283 /* Repeat count:
284 * 0 for repeat forever
285 * 1 for repeat set number of times..
286 *
287 * When we schedule it for the last time, change it to a ONESHOT event
288 */
289 if (sp->repeatcount>0) {
290 sp->repeatcount--;
291 if (sp->repeatcount==0) {
292 sp->type=SCHEDULE_ONESHOT;
293 }
294 }
295 insertschedule(sp);
296 break;
297 }
298#ifdef SCHEDDEBUG
299 Error("schedule",ERR_DEBUG,"exec schedule:(%x, %x, %x)", sp, sc, arg);
300#endif
301 (sc)(arg);
302#ifdef SCHEDDEBUG
303 Error("schedule",ERR_DEBUG,"schedule run OK");
304#endif
305 schedexes++;
306 }
307}
308
309void schedulestats(int hooknum, void *arg) {
c3db6f7e 310 long level=(long)arg;
2c5db955
CP
311 char buf[512];
312
313 if (level>5) {
314 sprintf(buf,"Schedule:%7d events scheduled, %7d events executed",schedadds,schedexes);
315 triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
316 sprintf(buf,"Schedule:%7d events deleted, %7d fast deletes (%.2f%%)",scheddels,scheddelfast,(float)(scheddelfast*100)/scheddels);
317 triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
318 sprintf(buf,"Schedule:%7d events currently in queue",heapsize);
319 triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
320 }
321}