📄 erl_bif_timer.c
字号:
/* ``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 via the world wide web 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. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings * AB. All Rights Reserved.'' * * $Id$ */#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#endifstatic 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 intsafe_btm_lock(Process *c_p, Uint32 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_SMP_PALLOC_IMPL(btm_pre, ErtsBifTimer, BTM_PREALC_SZ)static ERTS_INLINE intget_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 Etermcreate_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 inteq_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 inteq_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 voidtab_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 voidtab_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 voidlink_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 voidunlink_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 voidbif_timer_cleanup(ErtsBifTimer* btm){ ASSERT(btm); if (btm->bp) free_message_buffer(btm->bp); ERTS_PROC_LESS_MEM(sizeof(ErtsBifTimer)); 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 voidbif_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 { Uint32 rp_locks = ERTS_PROC_LOCKS_MSG_SEND; 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,rp_locks,0); else { rp = btm->receiver.proc.ess; erts_smp_proc_lock(rp, rp_locks); unlink_proc(btm); if (ERTS_PROC_IS_EXITING(rp)) { erts_smp_proc_unlock(rp, rp_locks); rp = NULL; } } if (rp) { if (!ERTS_PROC_PENDING_EXIT(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;#ifndef HEAP_FRAG_ELIM_TEST#ifdef ERTS_SMP if (erts_smp_proc_trylock(rp, ERTS_PROC_LOCK_MAIN) == 0) { rp_locks |= ERTS_PROC_LOCK_MAIN;#endif if (bp != NULL) { erts_link_mbuf_to_proc(rp, bp); bp = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -