root/src/common/timer.c @ 25

Revision 1, 12.4 kB (checked in by jinshiro, 17 years ago)
Line 
1// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
2// For more information, see LICENCE in the main folder
3
4#include "../common/cbasetypes.h"
5#include "../common/malloc.h"
6#include "../common/showmsg.h"
7#include "timer.h"
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <time.h>
13
14#ifdef WIN32
15#define WIN32_LEAN_AND_MEAN
16#include <windows.h> // GetTickCount()
17#else
18#include <unistd.h>
19#include <sys/time.h> // struct timeval, gettimeofday()
20#endif
21
22// If the server can't handle processing thousands of monsters
23// or many connected clients, please increase TIMER_MIN_INTERVAL.
24#define TIMER_MIN_INTERVAL 50
25
26// timers
27static struct TimerData* timer_data     = NULL;
28static int timer_data_max       = 0;
29static int timer_data_num       = 0;
30
31// free timers
32static int* free_timer_list             = NULL;
33static int free_timer_list_max  = 0;
34static int free_timer_list_pos  = 0;
35
36//NOTE: using a binary heap should improve performance [FlavioJS]
37// timer heap (ordered array of tid's)
38static int timer_heap_num = 0;
39static int timer_heap_max = 0;
40static int* timer_heap = NULL;
41
42// server startup time
43time_t start_time;
44
45/*----------------------------
46 *      Timer debugging
47 *----------------------------*/
48struct timer_func_list {
49        struct timer_func_list* next;
50        TimerFunc func;
51        char* name;
52} *tfl_root = NULL;
53
54/// Sets the name of a timer function.
55int add_timer_func_list(TimerFunc func, char* name)
56{
57        struct timer_func_list* tfl;
58
59        if (name) {
60                for( tfl=tfl_root; tfl != NULL; tfl=tfl->next )
61                {// check suspicious cases
62                        if( func == tfl->func )
63                                ShowWarning("add_timer_func_list: duplicating function %08x(%s) as %s.\n",(int)tfl->func,tfl->name,name);
64                        else if( strcmp(name,tfl->name) == 0 )
65                                ShowWarning("add_timer_func_list: function %08X has the same name as %08X(%s)\n",(int)func,(int)tfl->func,tfl->name);
66                }
67                CREATE(tfl,struct timer_func_list,1);
68                tfl->next = tfl_root;
69                tfl->func = func;
70                tfl->name = aStrdup(name);
71                tfl_root = tfl;
72        }
73        return 0;
74}
75
76/// Returns the name of the timer function.
77char* search_timer_func_list(TimerFunc func)
78{
79        struct timer_func_list* tfl;
80
81        for( tfl=tfl_root; tfl != NULL; tfl=tfl->next )
82                if (func == tfl->func)
83                        return tfl->name;
84
85        return "unknown timer function";
86}
87
88/*----------------------------
89 *      Get tick time
90 *----------------------------*/
91
92/// platform-abstracted tick retrieval
93static unsigned int tick(void)
94{
95#if defined(WIN32)
96        return GetTickCount();
97#elif (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK) /* posix compliant */) || (defined(__FreeBSD_cc_version) && __FreeBSD_cc_version >= 500005 /* FreeBSD >= 5.1.0 */)
98        struct timespec tval;
99        clock_gettime(CLOCK_MONOTONIC, &tval);
100        return tval.tv_sec * 1000 + tval.tv_nsec / 1000000;
101#else
102        struct timeval tval;
103        gettimeofday(&tval, NULL);
104        return tval.tv_sec * 1000 + tval.tv_usec / 1000;
105#endif
106}
107
108//////////////////////////////////////////////////////////////////////////
109#if defined(TICK_CACHE) && TICK_CACHE > 1
110//////////////////////////////////////////////////////////////////////////
111// tick is cached for TICK_CACHE calls
112static unsigned int gettick_cache;
113static int gettick_count = 1;
114
115unsigned int gettick_nocache(void)
116{
117        gettick_count = TICK_CACHE;
118        gettick_cache = tick();
119        return gettick_cache;
120}
121
122unsigned int gettick(void)
123{
124        return ( --gettick_count == 0 ) ? gettick_nocache() : gettick_cache;
125}
126//////////////////////////////
127#else
128//////////////////////////////
129// tick doesn't get cached
130unsigned int gettick_nocache(void)
131{
132        return tick();
133}
134
135unsigned int gettick(void)
136{
137        return tick();
138}
139//////////////////////////////////////////////////////////////////////////
140#endif
141//////////////////////////////////////////////////////////////////////////
142
143/*======================================
144 *      CORE : Timer Heap
145 *--------------------------------------*/
146
147// searches for the target tick's position and stores it in pos (binary search)
148#define HEAP_SEARCH(target,from,to,pos) \
149        do { \
150                int max,pivot; \
151                max = to; \
152                pos = from; \
153                while (pos < max) { \
154                        pivot = (pos + max) / 2; \
155                        if (DIFF_TICK(target, timer_data[timer_heap[pivot]].tick) < 0) \
156                                pos = pivot + 1; \
157                        else \
158                                max = pivot; \
159                } \
160        } while(0)
161
162/// Adds a timer to the timer_heap
163static void push_timer_heap(int tid)
164{
165        int pos;
166
167        // check number of element
168        if (timer_heap_num >= timer_heap_max) {
169                if (timer_heap_max == 0) {
170                        timer_heap_max = 256;
171                        CREATE(timer_heap, int, 256);
172                } else {
173                        timer_heap_max += 256;
174                        RECREATE(timer_heap, int, timer_heap_max);
175                        memset(timer_heap + (timer_heap_max - 256), 0, sizeof(int) * 256);
176                }
177        }
178
179        // do a sorting from higher to lower
180        if( timer_heap_num == 0 || DIFF_TICK(timer_data[tid].tick, timer_data[timer_heap[timer_heap_num - 1]].tick) < 0 )
181                timer_heap[timer_heap_num] = tid; // if lower actual item is higher than new
182        else
183        {
184                // searching position
185                HEAP_SEARCH(timer_data[tid].tick,0,timer_heap_num-1,pos);
186                // move elements
187                memmove(&timer_heap[pos + 1], &timer_heap[pos], sizeof(int) * (timer_heap_num - pos));
188                // save new element
189                timer_heap[pos] = tid;
190        }
191
192        timer_heap_num++;
193}
194
195/*==========================
196 *      Timer Management
197 *--------------------------*/
198
199/// Returns a free timer id.
200static int acquire_timer(void)
201{
202        int tid;
203
204        if (free_timer_list_pos) {
205                do {
206                        tid = free_timer_list[--free_timer_list_pos];
207                } while(tid >= timer_data_num && free_timer_list_pos > 0);
208        } else
209                tid = timer_data_num;
210
211        if (tid >= timer_data_num)
212                for (tid = timer_data_num; tid < timer_data_max && timer_data[tid].type; tid++);
213        if (tid >= timer_data_num && tid >= timer_data_max)
214        {// expand timer array
215                if (timer_data_max == 0)
216                {// create timer data (1st time)
217                        timer_data_max = 256;
218                        CREATE(timer_data, struct TimerData, timer_data_max);
219                } else
220                {// add more timers
221                        timer_data_max += 256;
222                        RECREATE(timer_data, struct TimerData, timer_data_max);
223                        memset(timer_data + (timer_data_max - 256), 0, sizeof(struct TimerData) * 256);
224                }
225        }
226
227        if (tid >= timer_data_num)
228                timer_data_num = tid + 1;
229
230        return tid;
231}
232
233/// Starts a new timer that is deleted once it expires (single-use).
234/// Returns the timer's id.
235int add_timer(unsigned int tick, TimerFunc func, int id, intptr data)
236{
237        int tid;
238       
239        tid = acquire_timer();
240        timer_data[tid].tick     = tick;
241        timer_data[tid].func     = func;
242        timer_data[tid].id       = id;
243        timer_data[tid].data     = data;
244        timer_data[tid].type     = TIMER_ONCE_AUTODEL;
245        timer_data[tid].interval = 1000;
246        push_timer_heap(tid);
247
248        return tid;
249}
250
251/// Starts a new timer that automatically restarts itself (infinite loop until manually removed).
252/// Returns the timer's id, or -1 if it fails.
253int add_timer_interval(unsigned int tick, TimerFunc func, int id, intptr data, int interval)
254{
255        int tid;
256
257        if( interval < 1 ) {
258                ShowError("add_timer_interval : function %08x(%s) has invalid interval %d!\n", (int)func, search_timer_func_list(func), interval);
259                return -1;
260        }
261       
262        tid = acquire_timer();
263        timer_data[tid].tick     = tick;
264        timer_data[tid].func     = func;
265        timer_data[tid].id       = id;
266        timer_data[tid].data     = data;
267        timer_data[tid].type     = TIMER_INTERVAL;
268        timer_data[tid].interval = interval;
269        push_timer_heap(tid);
270
271        return tid;
272}
273
274/// Retrieves internal timer data
275//FIXME: for safety, the return value should be 'const'
276struct TimerData* get_timer(int tid)
277{
278        return &timer_data[tid];
279}
280
281/// Marks a timer specified by 'id' for immediate deletion once it expires.
282/// Param 'func' is used for debug/verification purposes.
283/// Returns 0 on success, < 0 on failure.
284int delete_timer(int tid, TimerFunc func)
285{
286        if( tid < 0 || tid >= timer_data_num ) {
287                ShowError("delete_timer error : no such timer %d (%08x(%s))\n", tid, (int)func, search_timer_func_list(func));
288                return -1;
289        }
290        if( timer_data[tid].func != func ) {
291                ShowError("delete_timer error : function mismatch %08x(%s) != %08x(%s)\n", (int)timer_data[tid].func, search_timer_func_list(timer_data[tid].func), (int)func, search_timer_func_list(func));
292                return -2;
293        }
294
295        timer_data[tid].func = NULL;
296        timer_data[tid].type = TIMER_ONCE_AUTODEL;
297
298        return 0;
299}
300
301/// Adjusts a timer's expiration time.
302/// Returns the new tick value, or -1 if it fails.
303int addtick_timer(int tid, unsigned int tick)
304{
305        return settick_timer(tid, timer_data[tid].tick+tick);
306}
307
308/// Modifies a timer's expiration time (an alternative to deleting a timer and starting a new one).
309/// Returns the new tick value, or -1 if it fails.
310int settick_timer(int tid, unsigned int tick)
311{
312        int old_pos,pos;
313        unsigned int old_tick;
314       
315        old_tick = timer_data[tid].tick;
316        if( old_tick == tick )
317                return tick;
318
319        // search old_tick position
320        HEAP_SEARCH(old_tick,0,timer_heap_num-1,old_pos);
321        while( timer_heap[old_pos] != tid )
322        {// skip timers with the same tick
323                if( old_tick != timer_data[timer_heap[old_pos]].tick )
324                {
325                        ShowError("settick_timer: no such timer %d (%08x(%s))\n", tid, (int)timer_data[tid].func, search_timer_func_list(timer_data[tid].func));
326                        return -1;
327                }
328                ++old_pos;
329        }
330
331        if( DIFF_TICK(tick,timer_data[tid].tick) < 0 )
332        {// Timer is accelerated, shift timer near the end of the heap.
333                if (old_pos == timer_heap_num-1) //Nothing to shift.
334                        pos = old_pos;
335                else {
336                        HEAP_SEARCH(tick,old_pos+1,timer_heap_num-1,pos);
337                        --pos;
338                        if (pos != old_pos)
339                                memmove(&timer_heap[old_pos], &timer_heap[old_pos+1], (pos-old_pos)*sizeof(int));
340                }
341        } else
342        {// Timer is delayed, shift timer near the beginning of the heap.
343                if (old_pos == 0) //Nothing to shift.
344                        pos = old_pos;
345                else {
346                        HEAP_SEARCH(tick,0,old_pos-1,pos);
347                        ++pos;
348                        if (pos != old_pos)
349                                memmove(&timer_heap[pos+1], &timer_heap[pos], (old_pos-pos)*sizeof(int));
350                }
351        }
352
353        timer_heap[pos] = tid;
354        timer_data[tid].tick = tick;
355
356        return tick;
357}
358
359/// Executes all expired timers.
360/// Returns the value of the smallest non-expired timer (or 1 second if there aren't any).
361int do_timer(unsigned int tick)
362{
363        int nextmin = 1000; // return value
364        int i;
365
366        // process all timers one by one
367        while( timer_heap_num )
368        {
369                i = timer_heap[timer_heap_num - 1]; // last element in heap (=>smallest)
370                if( (nextmin = DIFF_TICK(timer_data[i].tick, tick)) > 0 )
371                        break; // no more expired timers to process
372
373                --timer_heap_num; // suppress the actual element from the table
374
375                // mark timer as 'to be removed'
376                timer_data[i].type |= TIMER_REMOVE_HEAP;
377
378                if( timer_data[i].func )
379                {
380                        if( nextmin < -1000 )
381                                // 1•bˆÈã‚̑啝‚È’x‰„‚ª”­¶‚µ‚Ä‚¢‚é‚̂ŁA
382                                // timerˆ—ƒ^ƒCƒ~ƒ“ƒO‚ðŒ»Ý’l‚Æ‚·‚鎖‚Å
383                                // ŒÄ‚яo‚µŽžƒ^ƒCƒ~ƒ“ƒO(ˆø”‚Ìtick)‘Š‘Î‚Åˆ—‚µ‚Ä‚é
384                                // timerŠÖ”‚ÌŽŸ‰ñˆ—ƒ^ƒCƒ~ƒ“ƒO‚ð’x‚点‚é
385                                timer_data[i].func(i, tick, timer_data[i].id, timer_data[i].data);
386                        else
387                                timer_data[i].func(i, timer_data[i].tick, timer_data[i].id, timer_data[i].data);
388                }
389
390                // in the case the function didn't change anything...
391                if( timer_data[i].type & TIMER_REMOVE_HEAP )
392                {
393                        timer_data[i].type &= ~TIMER_REMOVE_HEAP;
394
395                        switch( timer_data[i].type )
396                        {
397                        case TIMER_ONCE_AUTODEL:
398                                timer_data[i].type = 0;
399                                if (free_timer_list_pos >= free_timer_list_max) {
400                                        free_timer_list_max += 256;
401                                        RECREATE(free_timer_list,int,free_timer_list_max);
402                                        memset(free_timer_list + (free_timer_list_max - 256), 0, 256 * sizeof(int));
403                                }
404                                free_timer_list[free_timer_list_pos++] = i;
405                        break;
406                        case TIMER_INTERVAL:
407                                if (DIFF_TICK(timer_data[i].tick , tick) < -1000) {
408                                        timer_data[i].tick = tick + timer_data[i].interval;
409                                } else {
410                                        timer_data[i].tick += timer_data[i].interval;
411                                }
412                                timer_data[i].type &= ~TIMER_REMOVE_HEAP;
413                                push_timer_heap(i);
414                        break;
415                        }
416                }
417        }
418
419        if( nextmin < TIMER_MIN_INTERVAL )
420                nextmin = TIMER_MIN_INTERVAL;
421
422        return nextmin;
423}
424
425unsigned long get_uptime(void)
426{
427        return (unsigned long)difftime(time(NULL), start_time);
428}
429
430void timer_init(void)
431{
432        time(&start_time);
433}
434
435void timer_final(void)
436{
437        struct timer_func_list *tfl;
438        struct timer_func_list *next;
439
440        for( tfl=tfl_root; tfl != NULL; tfl = next ) {
441                next = tfl->next;       // copy next pointer
442                aFree(tfl->name);       // free structures
443                aFree(tfl);
444        }
445
446        if (timer_data) aFree(timer_data);
447        if (timer_heap) aFree(timer_heap);
448        if (free_timer_list) aFree(free_timer_list);
449}
Note: See TracBrowser for help on using the browser.