aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam/erl_bif_timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/beam/erl_bif_timer.c')
-rw-r--r--erts/emulator/beam/erl_bif_timer.c701
1 files changed, 701 insertions, 0 deletions
diff --git a/erts/emulator/beam/erl_bif_timer.c b/erts/emulator/beam/erl_bif_timer.c
new file mode 100644
index 0000000000..172bb37952
--- /dev/null
+++ b/erts/emulator/beam/erl_bif_timer.c
@@ -0,0 +1,701 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2005-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "erl_bif_timer.h"
+#include "global.h"
+#include "bif.h"
+#include "error.h"
+#include "big.h"
+
+/****************************************************************************
+** BIF Timer support
+****************************************************************************/
+
+#define BTM_FLG_SL_TIMER (((Uint32) 1) << 0)
+#define BTM_FLG_CANCELED (((Uint32) 1) << 1)
+#define BTM_FLG_HEAD (((Uint32) 1) << 2)
+#define BTM_FLG_BYNAME (((Uint32) 1) << 3)
+#define BTM_FLG_WRAP (((Uint32) 1) << 4)
+
+struct ErtsBifTimer_ {
+ struct {
+ union {
+ ErtsBifTimer **head;
+ ErtsBifTimer *prev;
+ } u;
+ ErtsBifTimer *next;
+ } tab;
+ union {
+ Eterm name;
+ struct {
+ ErtsBifTimer *prev;
+ ErtsBifTimer *next;
+ Process *ess;
+ } proc;
+ } receiver;
+ ErlTimer tm;
+ ErlHeapFragment* bp;
+ Uint32 flags;
+ Eterm message;
+ Uint32 ref_numbers[ERTS_REF_NUMBERS];
+};
+
+#ifdef SMALL_MEMORY
+#define TIMER_HASH_VEC_SZ 3331
+#define BTM_PREALC_SZ 10
+#else
+#define TIMER_HASH_VEC_SZ 10007
+#define BTM_PREALC_SZ 100
+#endif
+static ErtsBifTimer **bif_timer_tab;
+static Uint no_bif_timers;
+
+
+static erts_smp_rwmtx_t bif_timer_lock;
+
+#define erts_smp_safe_btm_rwlock(P, L) \
+ safe_btm_lock((P), (L), 1)
+#define erts_smp_safe_btm_rlock(P, L) \
+ safe_btm_lock((P), (L), 0)
+#define erts_smp_btm_rwlock() \
+ erts_smp_rwmtx_rwlock(&bif_timer_lock)
+#define erts_smp_btm_tryrwlock() \
+ erts_smp_rwmtx_tryrwlock(&bif_timer_lock)
+#define erts_smp_btm_rwunlock() \
+ erts_smp_rwmtx_rwunlock(&bif_timer_lock)
+#define erts_smp_btm_rlock() \
+ erts_smp_rwmtx_rlock(&bif_timer_lock)
+#define erts_smp_btm_tryrlock() \
+ erts_smp_rwmtx_tryrlock(&bif_timer_lock)
+#define erts_smp_btm_runlock() \
+ erts_smp_rwmtx_runlock(&bif_timer_lock)
+#define erts_smp_btm_lock_init() \
+ erts_smp_rwmtx_init(&bif_timer_lock, "bif_timers")
+
+
+static ERTS_INLINE int
+safe_btm_lock(Process *c_p, ErtsProcLocks c_p_locks, int rw_lock)
+{
+ ASSERT(c_p && c_p_locks);
+#ifdef ERTS_SMP
+ if ((rw_lock ? erts_smp_btm_tryrwlock() : erts_smp_btm_tryrlock()) != EBUSY)
+ return 0;
+ erts_smp_proc_unlock(c_p, c_p_locks);
+ if (rw_lock)
+ erts_smp_btm_rwlock();
+ else
+ erts_smp_btm_rlock();
+ erts_smp_proc_lock(c_p, c_p_locks);
+ if (ERTS_PROC_IS_EXITING(c_p)) {
+ if (rw_lock)
+ erts_smp_btm_rwunlock();
+ else
+ erts_smp_btm_runlock();
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+ERTS_SCHED_PREF_PALLOC_IMPL(btm_pre, ErtsBifTimer, BTM_PREALC_SZ)
+
+static ERTS_INLINE int
+get_index(Uint32 *ref_numbers, Uint32 len)
+{
+ Uint32 hash;
+ /* len can potentially be larger than ERTS_REF_NUMBERS
+ if it has visited another node... */
+ if (len > ERTS_REF_NUMBERS)
+ len = ERTS_REF_NUMBERS;
+
+#if ERTS_REF_NUMBERS != 3
+#error "ERTS_REF_NUMBERS changed. Update me..."
+#endif
+ switch (len) {
+ case 3: if (!ref_numbers[2]) len = 2;
+ case 2: if (!ref_numbers[1]) len = 1;
+ default: break;
+ }
+
+ ASSERT(1 <= len && len <= ERTS_REF_NUMBERS);
+
+ hash = block_hash((byte *) ref_numbers, len * sizeof(Uint32), 0x08d12e65);
+ return (int) (hash % ((Uint32) TIMER_HASH_VEC_SZ));
+}
+
+static Eterm
+create_ref(Uint *hp, Uint32 *ref_numbers, Uint32 len)
+{
+ Uint32 *datap;
+ int i;
+
+
+ if (len > ERTS_MAX_REF_NUMBERS) {
+ /* Such large refs should no be able to appear in the emulator */
+ erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__);
+ }
+
+#ifdef ARCH_64
+ hp[0] = make_ref_thing_header(len/2 + 1);
+ datap = (Uint32 *) &hp[1];
+ *(datap++) = len;
+#else
+ hp[0] = make_ref_thing_header(len);
+ datap = (Uint32 *) &hp[1];
+#endif
+
+ for (i = 0; i < len; i++)
+ datap[i] = ref_numbers[i];
+
+ return make_internal_ref(hp);
+}
+
+static int
+eq_non_standard_ref_numbers(Uint32 *rn1, Uint32 len1, Uint32 *rn2, Uint32 len2)
+{
+#ifdef ARCH_64
+#define MAX_REF_HEAP_SZ (1+(ERTS_MAX_REF_NUMBERS/2+1))
+#else
+#define MAX_REF_HEAP_SZ (1+ERTS_MAX_REF_NUMBERS)
+#endif
+ Uint r1_hp[MAX_REF_HEAP_SZ];
+ Uint r2_hp[MAX_REF_HEAP_SZ];
+
+ return eq(create_ref(r1_hp, rn1, len1), create_ref(r2_hp, rn2, len2));
+#undef MAX_REF_HEAP_SZ
+}
+
+static ERTS_INLINE int
+eq_ref_numbers(Uint32 *rn1, Uint32 len1, Uint32 *rn2, Uint32 len2)
+{
+ int res;
+ if (len1 != ERTS_REF_NUMBERS || len2 != ERTS_REF_NUMBERS) {
+ /* Can potentially happen, but will never... */
+ return eq_non_standard_ref_numbers(rn1, len1, rn2, len2);
+ }
+
+#if ERTS_REF_NUMBERS != 3
+#error "ERTS_REF_NUMBERS changed. Update me..."
+#endif
+ res = rn1[0] == rn2[0] && rn1[1] == rn2[1] && rn1[2] == rn2[2];
+
+ ASSERT(res
+ ? eq_non_standard_ref_numbers(rn1, len1, rn2, len2)
+ : !eq_non_standard_ref_numbers(rn1, len1, rn2, len2));
+
+ return res;
+}
+
+static ERTS_INLINE ErtsBifTimer *
+tab_find(Eterm ref)
+{
+ Uint32 *ref_numbers = internal_ref_numbers(ref);
+ Uint32 ref_numbers_len = internal_ref_no_of_numbers(ref);
+ int ix = get_index(ref_numbers, ref_numbers_len);
+ ErtsBifTimer* btm;
+
+ for (btm = bif_timer_tab[ix]; btm; btm = btm->tab.next)
+ if (eq_ref_numbers(ref_numbers, ref_numbers_len,
+ btm->ref_numbers, ERTS_REF_NUMBERS))
+ return btm;
+ return NULL;
+}
+
+static ERTS_INLINE void
+tab_remove(ErtsBifTimer* btm)
+{
+ if (btm->flags & BTM_FLG_HEAD) {
+ *btm->tab.u.head = btm->tab.next;
+ if (btm->tab.next) {
+ btm->tab.next->flags |= BTM_FLG_HEAD;
+ btm->tab.next->tab.u.head = btm->tab.u.head;
+ }
+ }
+ else {
+ btm->tab.u.prev->tab.next = btm->tab.next;
+ if (btm->tab.next)
+ btm->tab.next->tab.u.prev = btm->tab.u.prev;
+ }
+ btm->flags |= BTM_FLG_CANCELED;
+ ASSERT(no_bif_timers > 0);
+ no_bif_timers--;
+}
+
+static ERTS_INLINE void
+tab_insert(ErtsBifTimer* btm)
+{
+ int ix = get_index(btm->ref_numbers, ERTS_REF_NUMBERS);
+ ErtsBifTimer* btm_list = bif_timer_tab[ix];
+
+ if (btm_list) {
+ btm_list->flags &= ~BTM_FLG_HEAD;
+ btm_list->tab.u.prev = btm;
+ }
+
+ btm->flags |= BTM_FLG_HEAD;
+ btm->tab.u.head = &bif_timer_tab[ix];
+ btm->tab.next = btm_list;
+ bif_timer_tab[ix] = btm;
+ no_bif_timers++;
+}
+
+static ERTS_INLINE void
+link_proc(Process *p, ErtsBifTimer* btm)
+{
+ btm->receiver.proc.ess = p;
+ btm->receiver.proc.prev = NULL;
+ btm->receiver.proc.next = p->bif_timers;
+ if (p->bif_timers)
+ p->bif_timers->receiver.proc.prev = btm;
+ p->bif_timers = btm;
+}
+
+static ERTS_INLINE void
+unlink_proc(ErtsBifTimer* btm)
+{
+ if (btm->receiver.proc.prev)
+ btm->receiver.proc.prev->receiver.proc.next = btm->receiver.proc.next;
+ else
+ btm->receiver.proc.ess->bif_timers = btm->receiver.proc.next;
+ if (btm->receiver.proc.next)
+ btm->receiver.proc.next->receiver.proc.prev = btm->receiver.proc.prev;
+}
+
+static void
+bif_timer_cleanup(ErtsBifTimer* btm)
+{
+ ASSERT(btm);
+
+ if (btm->bp)
+ free_message_buffer(btm->bp);
+
+ if (!btm_pre_free(btm)) {
+ if (btm->flags & BTM_FLG_SL_TIMER)
+ erts_free(ERTS_ALC_T_SL_BIF_TIMER, (void *) btm);
+ else
+ erts_free(ERTS_ALC_T_LL_BIF_TIMER, (void *) btm);
+ }
+}
+
+static void
+bif_timer_timeout(ErtsBifTimer* btm)
+{
+ ASSERT(btm);
+
+
+ erts_smp_btm_rwlock();
+
+ if (btm->flags & BTM_FLG_CANCELED) {
+ /*
+ * A concurrent cancel is ongoing. Do not send the timeout message,
+ * but cleanup here since the cancel call-back won't be called.
+ */
+#ifndef ERTS_SMP
+ ASSERT(0);
+#endif
+ }
+ else {
+ ErtsProcLocks rp_locks = 0;
+ Process* rp;
+
+ tab_remove(btm);
+
+ ASSERT(!erts_get_current_process());
+
+ if (btm->flags & BTM_FLG_BYNAME)
+ rp = erts_whereis_process(NULL,0,btm->receiver.name,0,ERTS_P2P_FLG_SMP_INC_REFC);
+ else {
+ rp = btm->receiver.proc.ess;
+ erts_smp_proc_inc_refc(rp);
+ unlink_proc(btm);
+ }
+
+ if (rp) {
+ Eterm message;
+ ErlHeapFragment *bp;
+
+ bp = btm->bp;
+ btm->bp = NULL; /* Prevent cleanup of message buffer... */
+
+ if (!(btm->flags & BTM_FLG_WRAP))
+ message = btm->message;
+ else {
+#if ERTS_REF_NUMBERS != 3
+#error "ERTS_REF_NUMBERS changed. Update me..."
+#endif
+ Eterm ref;
+ Uint *hp;
+ Uint wrap_size = REF_THING_SIZE + 4;
+ message = btm->message;
+
+ if (!bp) {
+ ErlOffHeap *ohp;
+ ASSERT(is_immed(message));
+ hp = erts_alloc_message_heap(wrap_size,
+ &bp,
+ &ohp,
+ rp,
+ &rp_locks);
+ } else {
+ Eterm old_size = bp->size;
+ bp = erts_resize_message_buffer(bp, old_size + wrap_size,
+ &message, 1);
+ hp = &bp->mem[0] + old_size;
+ }
+
+ write_ref_thing(hp,
+ btm->ref_numbers[0],
+ btm->ref_numbers[1],
+ btm->ref_numbers[2]);
+ ref = make_internal_ref(hp);
+ hp += REF_THING_SIZE;
+ message = TUPLE3(hp, am_timeout, ref, message);
+ }
+
+ erts_queue_message(rp, &rp_locks, bp, message, NIL);
+ erts_smp_proc_unlock(rp, rp_locks);
+ erts_smp_proc_dec_refc(rp);
+ }
+ }
+
+ erts_smp_btm_rwunlock();
+
+ bif_timer_cleanup(btm);
+}
+
+static Eterm
+setup_bif_timer(Uint32 xflags,
+ Process *c_p,
+ Eterm time,
+ Eterm receiver,
+ Eterm message)
+{
+ Process *rp;
+ ErtsBifTimer* btm;
+ Uint timeout;
+ Eterm ref;
+ Uint32 *ref_numbers;
+
+ if (!term_to_Uint(time, &timeout))
+ return THE_NON_VALUE;
+#ifdef ARCH_64
+ if ((timeout >> 32) != 0)
+ return THE_NON_VALUE;
+#endif
+ if (is_not_internal_pid(receiver) && is_not_atom(receiver))
+ return THE_NON_VALUE;
+
+ ref = erts_make_ref(c_p);
+
+ if (is_atom(receiver))
+ rp = NULL;
+ else {
+ rp = erts_pid2proc(c_p, ERTS_PROC_LOCK_MAIN,
+ receiver, ERTS_PROC_LOCK_MSGQ);
+ if (!rp)
+ return ref;
+ }
+
+ if (timeout < ERTS_ALC_MIN_LONG_LIVED_TIME) {
+ if (timeout < 1000) {
+ btm = btm_pre_alloc();
+ if (!btm)
+ goto sl_timer_alloc;
+ btm->flags = 0;
+ }
+ else {
+ sl_timer_alloc:
+ btm = (ErtsBifTimer *) erts_alloc(ERTS_ALC_T_SL_BIF_TIMER,
+ sizeof(ErtsBifTimer));
+ btm->flags = BTM_FLG_SL_TIMER;
+ }
+ }
+ else {
+ btm = (ErtsBifTimer *) erts_alloc(ERTS_ALC_T_LL_BIF_TIMER,
+ sizeof(ErtsBifTimer));
+ btm->flags = 0;
+ }
+
+ if (rp) {
+ link_proc(rp, btm);
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ);
+ }
+ else {
+ ASSERT(is_atom(receiver));
+ btm->receiver.name = receiver;
+ btm->flags |= BTM_FLG_BYNAME;
+ }
+
+ btm->flags |= xflags;
+
+ ref_numbers = internal_ref_numbers(ref);
+ ASSERT(internal_ref_no_of_numbers(ref) == 3);
+#if ERTS_REF_NUMBERS != 3
+#error "ERTS_REF_NUMBERS changed. Update me..."
+#endif
+ btm->ref_numbers[0] = ref_numbers[0];
+ btm->ref_numbers[1] = ref_numbers[1];
+ btm->ref_numbers[2] = ref_numbers[2];
+
+ ASSERT(eq_ref_numbers(btm->ref_numbers, ERTS_REF_NUMBERS,
+ ref_numbers, ERTS_REF_NUMBERS));
+
+ if (is_immed(message)) {
+ btm->bp = NULL;
+ btm->message = message;
+ }
+ else {
+ ErlHeapFragment* bp;
+ Eterm* hp;
+ Uint size;
+
+ size = size_object(message);
+ btm->bp = bp = new_message_buffer(size);
+ hp = bp->mem;
+ btm->message = copy_struct(message, size, &hp, &bp->off_heap);
+ }
+
+ tab_insert(btm);
+ ASSERT(btm == tab_find(ref));
+ btm->tm.active = 0; /* MUST be initalized */
+ erl_set_timer(&btm->tm,
+ (ErlTimeoutProc) bif_timer_timeout,
+ (ErlCancelProc) bif_timer_cleanup,
+ (void *) btm,
+ timeout);
+ return ref;
+}
+
+/* send_after(Time, Pid, Message) -> Ref */
+BIF_RETTYPE send_after_3(BIF_ALIST_3)
+{
+ Eterm res;
+
+ if (erts_smp_safe_btm_rwlock(BIF_P, ERTS_PROC_LOCK_MAIN))
+ ERTS_BIF_EXITED(BIF_P);
+
+ res = setup_bif_timer(0, BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+
+ erts_smp_btm_rwunlock();
+
+ if (is_non_value(res)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ else {
+ ASSERT(is_internal_ref(res));
+ BIF_RET(res);
+ }
+}
+
+/* start_timer(Time, Pid, Message) -> Ref */
+BIF_RETTYPE start_timer_3(BIF_ALIST_3)
+{
+ Eterm res;
+
+ if (erts_smp_safe_btm_rwlock(BIF_P, ERTS_PROC_LOCK_MAIN))
+ ERTS_BIF_EXITED(BIF_P);
+
+ res = setup_bif_timer(BTM_FLG_WRAP, BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
+
+ erts_smp_btm_rwunlock();
+
+ if (is_non_value(res)) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ else {
+ ASSERT(is_internal_ref(res));
+ BIF_RET(res);
+ }
+}
+
+/* cancel_timer(Ref) -> false | RemainingTime */
+BIF_RETTYPE cancel_timer_1(BIF_ALIST_1)
+{
+ Eterm res;
+ ErtsBifTimer *btm;
+
+ if (is_not_internal_ref(BIF_ARG_1)) {
+ if (is_ref(BIF_ARG_1)) {
+ BIF_RET(am_false);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ if (erts_smp_safe_btm_rwlock(BIF_P, ERTS_PROC_LOCK_MAIN))
+ ERTS_BIF_EXITED(BIF_P);
+
+ btm = tab_find(BIF_ARG_1);
+ if (!btm || btm->flags & BTM_FLG_CANCELED) {
+ erts_smp_btm_rwunlock();
+ res = am_false;
+ }
+ else {
+ Uint left = time_left(&btm->tm);
+ if (!(btm->flags & BTM_FLG_BYNAME)) {
+ erts_smp_proc_lock(btm->receiver.proc.ess, ERTS_PROC_LOCK_MSGQ);
+ unlink_proc(btm);
+ erts_smp_proc_unlock(btm->receiver.proc.ess, ERTS_PROC_LOCK_MSGQ);
+ }
+ tab_remove(btm);
+ ASSERT(!tab_find(BIF_ARG_1));
+ erl_cancel_timer(&btm->tm);
+ erts_smp_btm_rwunlock();
+ res = erts_make_integer(left, BIF_P);
+ }
+
+ BIF_RET(res);
+}
+
+/* read_timer(Ref) -> false | RemainingTime */
+BIF_RETTYPE read_timer_1(BIF_ALIST_1)
+{
+ Eterm res;
+ ErtsBifTimer *btm;
+
+ if (is_not_internal_ref(BIF_ARG_1)) {
+ if (is_ref(BIF_ARG_1)) {
+ BIF_RET(am_false);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ if (erts_smp_safe_btm_rlock(BIF_P, ERTS_PROC_LOCK_MAIN))
+ ERTS_BIF_EXITED(BIF_P);
+
+ btm = tab_find(BIF_ARG_1);
+ if (!btm || btm->flags & BTM_FLG_CANCELED) {
+ res = am_false;
+ }
+ else {
+ Uint left = time_left(&btm->tm);
+ res = erts_make_integer(left, BIF_P);
+ }
+
+ erts_smp_btm_runlock();
+
+ BIF_RET(res);
+}
+
+void
+erts_print_bif_timer_info(int to, void *to_arg)
+{
+ int i;
+ int lock = !ERTS_IS_CRASH_DUMPING;
+
+ if (lock)
+ erts_smp_btm_rlock();
+
+ for (i = 0; i < TIMER_HASH_VEC_SZ; i++) {
+ ErtsBifTimer *btm;
+ for (btm = bif_timer_tab[i]; btm; btm = btm->tab.next) {
+ Eterm receiver = (btm->flags & BTM_FLG_BYNAME
+ ? btm->receiver.name
+ : btm->receiver.proc.ess->id);
+ erts_print(to, to_arg, "=timer:%T\n", receiver);
+ erts_print(to, to_arg, "Message: %T\n", btm->message);
+ erts_print(to, to_arg, "Time left: %d ms\n", time_left(&btm->tm));
+ }
+ }
+
+ if (lock)
+ erts_smp_btm_runlock();
+}
+
+
+void
+erts_cancel_bif_timers(Process *p, ErtsProcLocks plocks)
+{
+ ErtsBifTimer *btm;
+
+ if (erts_smp_btm_tryrwlock() == EBUSY) {
+ erts_smp_proc_unlock(p, plocks);
+ erts_smp_btm_rwlock();
+ erts_smp_proc_lock(p, plocks);
+ }
+
+ btm = p->bif_timers;
+ while (btm) {
+ ErtsBifTimer *tmp_btm;
+ ASSERT(!(btm->flags & BTM_FLG_CANCELED));
+ tab_remove(btm);
+ tmp_btm = btm;
+ btm = btm->receiver.proc.next;
+ erl_cancel_timer(&tmp_btm->tm);
+ }
+
+ p->bif_timers = NULL;
+
+ erts_smp_btm_rwunlock();
+}
+
+void erts_bif_timer_init(void)
+{
+ int i;
+ no_bif_timers = 0;
+ init_btm_pre_alloc();
+ erts_smp_btm_lock_init();
+ bif_timer_tab = erts_alloc(ERTS_ALC_T_BIF_TIMER_TABLE,
+ sizeof(ErtsBifTimer *)*TIMER_HASH_VEC_SZ);
+ for (i = 0; i < TIMER_HASH_VEC_SZ; ++i)
+ bif_timer_tab[i] = NULL;
+}
+
+Uint
+erts_bif_timer_memory_size(void)
+{
+ Uint res;
+ int lock = !ERTS_IS_CRASH_DUMPING;
+
+ if (lock)
+ erts_smp_btm_rlock();
+
+ res = (sizeof(ErtsBifTimer *)*TIMER_HASH_VEC_SZ
+ + no_bif_timers*sizeof(ErtsBifTimer));
+
+ if (lock)
+ erts_smp_btm_runlock();
+
+ return res;
+}
+
+
+void
+erts_bif_timer_foreach(void (*func)(Eterm, Eterm, ErlHeapFragment *, void *),
+ void *arg)
+{
+ int i;
+
+ ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0));
+
+ for (i = 0; i < TIMER_HASH_VEC_SZ; i++) {
+ ErtsBifTimer *btm;
+ for (btm = bif_timer_tab[i]; btm; btm = btm->tab.next) {
+ (*func)((btm->flags & BTM_FLG_BYNAME
+ ? btm->receiver.name
+ : btm->receiver.proc.ess->id),
+ btm->message,
+ btm->bp,
+ arg);
+ }
+ }
+}