76
76
#include "erl_vm.h"
77
77
#include "global.h"
79
#define TIW_SIZE 8192 /* timing wheel size (should be a power of 2) */
79
#ifdef ERTS_ENABLE_LOCK_CHECK
80
#define ASSERT_NO_LOCKED_LOCKS erts_lc_check_exact(NULL, 0)
82
#define ASSERT_NO_LOCKED_LOCKS
86
#if defined(ERTS_TIMER_THREAD) || 1
87
/* I don't yet know why, but using a mutex instead of a spinlock
88
or spin-based rwlock avoids excessive delays at startup. */
89
static erts_smp_rwmtx_t tiw_lock;
90
#define tiw_read_lock() erts_smp_rwmtx_rlock(&tiw_lock)
91
#define tiw_read_unlock() erts_smp_rwmtx_runlock(&tiw_lock)
92
#define tiw_write_lock() erts_smp_rwmtx_rwlock(&tiw_lock)
93
#define tiw_write_unlock() erts_smp_rwmtx_rwunlock(&tiw_lock)
94
#define tiw_init_lock() erts_smp_rwmtx_init(&tiw_lock, "timer_wheel")
96
static erts_smp_rwlock_t tiw_lock;
97
#define tiw_read_lock() erts_smp_read_lock(&tiw_lock)
98
#define tiw_read_unlock() erts_smp_read_unlock(&tiw_lock)
99
#define tiw_write_lock() erts_smp_write_lock(&tiw_lock)
100
#define tiw_write_unlock() erts_smp_write_unlock(&tiw_lock)
101
#define tiw_init_lock() erts_smp_rwlock_init(&tiw_lock, "timer_wheel")
104
/* BEGIN tiw_lock protected variables
106
** The individual timer cells in tiw are also protected by the same mutex.
110
#define TIW_SIZE 8192
112
#define TIW_SIZE 65536 /* timing wheel size (should be a power of 2) */
80
114
static ErlTimer** tiw; /* the timing wheel, allocated in init_time() */
81
115
static Uint tiw_pos; /* current position in wheel */
82
116
static Uint tiw_nto; /* number of timeouts in wheel */
83
static ErlTimer* tm_list; /* new timers created while bumping */
118
/* END tiw_lock protected variables */
85
120
/* Actual interval time chosen by sys_init_time() */
87
static int bump_lock; /* set while bumping */
90
increment_time(int ticks)
96
insert_timer(ErlTimer* p, Uint t)
101
/* The current slot (tiw_pos) in timing wheel is the next slot to be
102
* be processed. Hence no extra time tick is needed.
104
* (x + y - 1)/y is precisely the "number of bins" formula.
106
ticks = (t + itime - 1) / itime;
107
ticks += do_time; /* Add backlog of unprocessed time */
110
tm = (ticks + tiw_pos) % TIW_SIZE;
112
p->count = (Uint) (ticks / TIW_SIZE);
114
/* insert at head of list at slot */
121
static int itime; /* Constant after init */
123
#if defined(ERTS_TIMER_THREAD)
124
static SysTimeval time_start; /* start of current time interval */
125
static long ticks_end; /* time_start+ticks_end == time_wakeup */
126
static long ticks_latest; /* delta from time_start at latest time update*/
128
static ERTS_INLINE long time_gettimeofday(SysTimeval *now)
132
sys_gettimeofday(now);
133
now->tv_usec = 1000 * (now->tv_usec / 1000); /* ms resolution */
134
elapsed = (1000 * (now->tv_sec - time_start.tv_sec) +
135
(now->tv_usec - time_start.tv_usec) / 1000);
136
// elapsed /= CLOCK_RESOLUTION;
140
static long do_time_update(void)
145
elapsed = time_gettimeofday(&now);
146
ticks_latest = elapsed;
150
static ERTS_INLINE long do_time_read(void)
155
static long do_time_reset(void)
160
elapsed = time_gettimeofday(&now);
162
ticks_end = LONG_MAX;
167
static ERTS_INLINE void do_time_init(void)
169
(void)do_time_reset();
173
erts_smp_atomic_t do_time; /* set at clock interrupt */
174
static ERTS_INLINE long do_time_read(void) { return erts_smp_atomic_read(&do_time); }
175
static ERTS_INLINE long do_time_update(void) { return do_time_read(); }
176
static ERTS_INLINE void do_time_init(void) { erts_smp_atomic_init(&do_time, 0L); }
179
/* get the time (in units of itime) to the next timeout,
180
or -1 if there are no timeouts */
182
static int next_time_internal(void) /* PRE: tiw_lock taken by caller */
190
return -1; /* no timeouts in wheel */
192
/* start going through wheel to find next timeout */
194
min = (unsigned int) -1; /* max unsigned int */
201
/* found next timeout */
203
return ((tm >= dt) ? (tm - dt) : 0);
205
/* keep shortest time in 'min' */
206
if (tm + p->count*TIW_SIZE < min)
207
min = tm + p->count*TIW_SIZE;
211
/* when we have found all timeouts the shortest time will be in min */
212
if (nto == tiw_nto) break;
214
i = (i + 1) % TIW_SIZE;
215
} while (i != tiw_pos);
217
return ((min >= dt) ? (min - dt) : 0);
220
#if !defined(ERTS_TIMER_THREAD)
221
/* Private export to erl_time_sup.c */
227
(void)do_time_update();
228
ret = next_time_internal();
234
static ERTS_INLINE void bump_timer_internal(long dt) /* PRE: tiw_lock is write-locked */
127
Uint dtime = do_time; /* local copy */
238
ErlTimer *p, **prev, *timeout_head, **timeout_tail;
239
Uint dtime = (unsigned long)dt;
130
241
/* no need to bump the position if there aren't any timeouts */
131
242
if (tiw_nto == 0) {
135
bump_lock = 1; /* avoid feedback loops in timeout proc */
137
247
/* if do_time > TIW_SIZE we want to go around just once */
138
248
count = (Uint)(dtime / TIW_SIZE) + 1;
139
249
keep_pos = (tiw_pos + dtime) % TIW_SIZE;
140
250
if (dtime > TIW_SIZE) dtime = TIW_SIZE;
253
timeout_tail = &timeout_head;
142
254
while (dtime > 0) {
143
255
/* this is to decrease the counters with the right amount */
144
256
/* when dtime >= TIW_SIZE */
162
275
tiw_pos = (tiw_pos + 1) % TIW_SIZE;
166
278
tiw_pos = keep_pos;
168
/* do_time should be 0 during timeout/set_timer feedback
169
** tiw_pos is also updated before inserting new timers
171
if ((p = tm_list) != NULL) {
174
ErlTimer* p_next = p->next;
175
insert_timer(p, p->count);
282
/* Call timedout timers callbacks */
283
while (timeout_head) {
285
timeout_head = p->next;
286
/* Here comes hairy use of the timer fields!
287
* They are reset without having the lock.
288
* It is assumed that no code but this will
289
* accesses any field until the ->timeout
290
* callback is called.
294
(*p->timeout)(p->arg);
298
#if defined(ERTS_TIMER_THREAD)
299
static void timer_thread_bump_timer(void)
302
bump_timer_internal(do_time_reset());
305
void bump_timer(long dt) /* dt is value from do_time */
308
bump_timer_internal(dt);
182
313
erts_timer_wheel_memory_size(void)
184
315
return (Uint) TIW_SIZE * sizeof(ErlTimer*);
318
#if defined(ERTS_TIMER_THREAD)
319
static struct erts_iwait *timer_thread_iwait;
321
static int timer_thread_setup_delay(SysTimeval *rem_time)
327
elapsed = do_time_update();
328
ticks = next_time_internal();
329
if (ticks == -1) /* timer queue empty */
330
ticks = 100*1000*1000;
334
//ticks *= CLOCK_RESOLUTION;
335
rem_time->tv_sec = ticks / 1000;
336
rem_time->tv_usec = 1000 * (ticks % 1000);
342
static void *timer_thread_start(void *ignore)
346
#ifdef ERTS_ENABLE_LOCK_CHECK
347
erts_lc_set_thread_name("timer");
349
erts_register_blockable_thread();
352
if (timer_thread_setup_delay(&delay)) {
353
erts_smp_activity_begin(ERTS_ACTIVITY_WAIT, NULL, NULL, NULL);
354
ASSERT_NO_LOCKED_LOCKS;
355
erts_iwait_wait(timer_thread_iwait, &delay);
356
ASSERT_NO_LOCKED_LOCKS;
357
erts_smp_activity_end(ERTS_ACTIVITY_WAIT, NULL, NULL, NULL);
360
erts_smp_chk_system_block(NULL, NULL, NULL);
361
timer_thread_bump_timer();
362
ASSERT_NO_LOCKED_LOCKS;
368
static ERTS_INLINE void timer_thread_post_insert(Uint ticks)
370
if ((Sint)ticks < ticks_end)
371
erts_iwait_interrupt(timer_thread_iwait);
374
static void timer_thread_init(void)
378
timer_thread_iwait = erts_iwait_init();
379
erts_thr_create(&tid, timer_thread_start, NULL, 1);
383
static ERTS_INLINE void timer_thread_post_insert(Uint ticks) { }
384
static ERTS_INLINE void timer_thread_init(void) { }
187
387
/* this routine links the time cells into a free list at the start
188
388
and sets the time queue as empty */
194
396
tiw = (ErlTimer**) erts_alloc(ERTS_ALC_T_TIMER_WHEEL,
195
397
TIW_SIZE * sizeof(ErlTimer*));
196
398
for(i = 0; i < TIW_SIZE; i++)
198
do_time = tiw_pos = tiw_nto = 0;
401
tiw_pos = tiw_nto = 0;
202
403
/* system dependent init */
203
404
itime = erts_init_time_sup();
207
410
** Insert a process into the time queue, with a timeout 't'
413
insert_timer(ErlTimer* p, Uint t)
418
/* The current slot (tiw_pos) in timing wheel is the next slot to be
419
* be processed. Hence no extra time tick is needed.
421
* (x + y - 1)/y is precisely the "number of bins" formula.
423
ticks = (t + itime - 1) / itime;
424
ticks += do_time_update(); /* Add backlog of unprocessed time */
427
tm = (ticks + tiw_pos) % TIW_SIZE;
429
p->count = (Uint) (ticks / TIW_SIZE);
431
/* insert at head of list at slot */
436
timer_thread_post_insert(ticks);
210
440
erl_set_timer(ErlTimer* p, ErlTimeoutProc timeout, ErlCancelProc cancel,
211
441
void* arg, Uint t)
213
if (p->active) /* XXX assert ? */
445
if (p->active) { /* XXX assert ? */
215
449
p->timeout = timeout;
216
450
p->cancel = cancel;
222
p->count = t; /* time is saved here used by bump */
455
#if defined(ERTS_SMP) && !defined(ERTS_TIMER_THREAD)
456
erts_wake_io_thread();
323
537
/* print the whole wheel, starting at the current position */
324
erl_printf(COUT, "\ntiw_pos = %d tiw_nto %d\n\r", tiw_pos, tiw_nto);
538
erts_printf("\ntiw_pos = %d tiw_nto %d\n", tiw_pos, tiw_nto);
326
540
if (tiw[i] != NULL) {
327
erl_printf(COUT, "%d:\n\r", i);
541
erts_printf("%d:\n", i);
328
542
for(p = tiw[i]; p != NULL; p = p->next) {
329
erl_printf(COUT, " (count %d, slot %d)\n\r",
543
erts_printf(" (count %d, slot %d)\n",
333
547
for(i = (i+1)%TIW_SIZE; i != tiw_pos; i = (i+1)%TIW_SIZE) {
334
548
if (tiw[i] != NULL) {
335
erl_printf(COUT, "%d:\n\r", i);
549
erts_printf("%d:\n", i);
336
550
for(p = tiw[i]; p != NULL; p = p->next) {
337
erl_printf(COUT, " (count %d, slot %d)\n\r",
551
erts_printf(" (count %d, slot %d)\n",
344
560
#endif /* DEBUG */