/* * %CopyrightBegin% * * Copyright Ericsson AB 2012. 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% */ /* * Description: Process/Port table implementation. * * Author: Rickard Green */ #ifndef ERL_PTAB_H__ #define ERL_PTAB_H__ #include "sys.h" #include "erl_term.h" #include "erl_time.h" #include "erl_utils.h" #define ERL_THR_PROGRESS_TSD_TYPE_ONLY #include "erl_thr_progress.h" #undef ERL_THR_PROGRESS_TSD_TYPE_ONLY #include "erl_alloc.h" #define ERTS_TRACER_PROC(P) (P)->common.tracer_proc #define ERTS_TRACE_FLAGS(P) (P)->common.trace_flags #define IS_TRACED(p) \ (ERTS_TRACER_PROC((p)) != NIL) #define ARE_TRACE_FLAGS_ON(p,tf) \ ((ERTS_TRACE_FLAGS((p)) & (tf|F_SENSITIVE)) == (tf)) #define IS_TRACED_FL(p,tf) \ ( IS_TRACED(p) && ARE_TRACE_FLAGS_ON(p,tf) ) typedef struct { Eterm id; #ifdef ERTS_SMP erts_atomic32_t refc; #endif Eterm tracer_proc; Uint trace_flags; union { /* --- While being alive --- */ struct { Uint64 started_interval; struct reg_proc *reg; #ifdef ERTS_SMP ErtsSmpPTimer *ptimer; #else ErlTimer tm; #endif } alive; /* --- While being released --- */ #ifdef ERTS_SMP ErtsThrPrgrLaterOp release; #endif } u; } ErtsPTabElementCommon; typedef struct ErtsPTabDeletedElement_ ErtsPTabDeletedElement; typedef struct { erts_smp_rwmtx_t rwmtx; erts_interval_t interval; struct { ErtsPTabDeletedElement *start; ErtsPTabDeletedElement *end; } deleted; int chunks; } ErtsPTabListData; typedef struct { #ifdef ARCH_32 erts_smp_dw_atomic_t last_data; #else erts_smp_atomic_t last_data; #endif erts_smp_atomic32_t count; } ErtsPTabVolatileData; typedef struct { erts_smp_atomic_t *tab; int max; int tab_cache_lines; int pix_per_cache_line; int pix_cl_mask; int pix_cl_shift; int pix_cli_mask; int pix_cli_shift; ErtsPTabElementCommon *invalid_element; Eterm invalid_data; void (*release_element)(void *); } ErtsPTabReadOnlyData; typedef struct { /* * Data mainly modified when someone is listing * the content of the table. */ union { ErtsPTabListData data; char algn[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsPTabListData))]; } list; /* * Frequently modified data. */ union { ErtsPTabVolatileData tile; char algn[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsPTabVolatileData))]; } vola; /* * Read only data. */ union { ErtsPTabReadOnlyData o; char algn[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsPTabReadOnlyData))]; } r; } ErtsPTab; #define ERTS_PTAB_ID_DATA_SIZE 28 #define ERTS_PTAB_ID_DATA_SHIFT (_TAG_IMMED1_SIZE) /* * Currently pids and ports are allowed. */ #if _PID_DATA_SIZE != ERTS_PTAB_ID_DATA_SIZE # error "Unexpected pid data size" #endif #if _PID_DATA_SHIFT != ERTS_PTAB_ID_DATA_SHIFT # error "Unexpected pid tag size" #endif #if _PORT_DATA_SIZE != ERTS_PTAB_ID_DATA_SIZE # error "Unexpected port data size" #endif #if _PORT_DATA_SHIFT != ERTS_PTAB_ID_DATA_SHIFT # error "Unexpected port tag size" #endif #define erts_ptab_is_valid_id(ID) \ (is_internal_pid((ID)) || is_internal_port((ID))) #define ERTS_PTAB_ID2DATA(ID) \ (ASSERT_EXPR(erts_ptab_is_valid_id((ID))), \ (((ID) >> ERTS_PTAB_ID_DATA_SHIFT) \ & ~(~((Uint) 0) << ERTS_PTAB_ID_DATA_SIZE))) void erts_ptab_init(void); void erts_ptab_init_table(ErtsPTab *ptab, ErtsAlcType_t atype, void (*release_element)(void *), ErtsPTabElementCommon *invalid_element, int size, char *name); int erts_ptab_new_element(ErtsPTab *ptab, ErtsPTabElementCommon *ptab_el, void *init_arg, void (*init_ptab_el)(void *, Eterm)); void erts_ptab_delete_element(ErtsPTab *ptab, ErtsPTabElementCommon *ptab_el); int erts_ptab_initialized(ErtsPTab *ptab); ERTS_GLB_INLINE erts_interval_t *erts_ptab_interval(ErtsPTab *ptab); ERTS_GLB_INLINE int erts_ptab_max(ErtsPTab *ptab); ERTS_GLB_INLINE int erts_ptab_count(ErtsPTab *ptab); ERTS_GLB_INLINE int erts_ptab_data2ix(ErtsPTab *ptab, Eterm data); ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_nob(ErtsPTab *ptab, int ix); ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_ddrb(ErtsPTab *ptab, int ix); ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_rb(ErtsPTab *ptab, int ix); ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_acqb(ErtsPTab *ptab, int ix); ERTS_GLB_INLINE void erts_ptab_inc_refc(ErtsPTabElementCommon *ptab_el); ERTS_GLB_INLINE int erts_ptab_dec_test_refc(ErtsPTabElementCommon *ptab_el); ERTS_GLB_INLINE int erts_ptab_add_test_refc(ErtsPTabElementCommon *ptab_el, Sint32 add_refc); ERTS_GLB_INLINE void erts_ptab_rlock(ErtsPTab *ptab); ERTS_GLB_INLINE int erts_ptab_tryrlock(ErtsPTab *ptab); ERTS_GLB_INLINE void erts_ptab_runlock(ErtsPTab *ptab); ERTS_GLB_INLINE void erts_ptab_rwlock(ErtsPTab *ptab); ERTS_GLB_INLINE int erts_ptab_tryrwlock(ErtsPTab *ptab); ERTS_GLB_INLINE void erts_ptab_rwunlock(ErtsPTab *ptab); ERTS_GLB_INLINE int erts_smp_lc_ptab_is_rlocked(ErtsPTab *ptab); ERTS_GLB_INLINE int erts_smp_lc_ptab_is_rwlocked(ErtsPTab *ptab); #if ERTS_GLB_INLINE_INCL_FUNC_DEF ERTS_GLB_INLINE erts_interval_t * erts_ptab_interval(ErtsPTab *ptab) { return &ptab->list.data.interval; } ERTS_GLB_INLINE int erts_ptab_max(ErtsPTab *ptab) { return ptab->r.o.max; } ERTS_GLB_INLINE int erts_ptab_count(ErtsPTab *ptab) { erts_aint32_t res = erts_smp_atomic32_read_nob(&ptab->vola.tile.count); if (res > ptab->r.o.max) return ptab->r.o.max; ASSERT(res >= 0); return (int) res; } ERTS_GLB_INLINE int erts_ptab_data2ix(ErtsPTab *ptab, Eterm data) { int n, pix; n = (int) data; if (ptab->r.o.pix_cl_mask) { pix = ((n & ptab->r.o.pix_cl_mask) << ptab->r.o.pix_cl_shift); pix += ((n >> ptab->r.o.pix_cli_shift) & ptab->r.o.pix_cli_mask); } else { n %= ptab->r.o.max; pix = n % ptab->r.o.tab_cache_lines; pix *= ptab->r.o.pix_per_cache_line; pix += n / ptab->r.o.tab_cache_lines; } ASSERT(0 <= pix && pix < ptab->r.o.max); return pix; } ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_nob(ErtsPTab *ptab, int ix) { ASSERT(0 <= ix && ix < ptab->r.o.max); return erts_smp_atomic_read_nob(&ptab->r.o.tab[ix]); } ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_ddrb(ErtsPTab *ptab, int ix) { ASSERT(0 <= ix && ix < ptab->r.o.max); return erts_smp_atomic_read_ddrb(&ptab->r.o.tab[ix]); } ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_rb(ErtsPTab *ptab, int ix) { ASSERT(0 <= ix && ix < ptab->r.o.max); return erts_smp_atomic_read_rb(&ptab->r.o.tab[ix]); } ERTS_GLB_INLINE erts_aint_t erts_ptab_pix2intptr_acqb(ErtsPTab *ptab, int ix) { ASSERT(0 <= ix && ix < ptab->r.o.max); return erts_smp_atomic_read_acqb(&ptab->r.o.tab[ix]); } ERTS_GLB_INLINE void erts_ptab_inc_refc(ErtsPTabElementCommon *ptab_el) { #ifdef ERTS_SMP #ifdef ERTS_ENABLE_LOCK_CHECK erts_aint32_t refc = erts_atomic32_inc_read_nob(&ptab_el->refc); ERTS_SMP_LC_ASSERT(refc > 1); #else erts_atomic32_inc_nob(&ptab_el->refc); #endif #endif } ERTS_GLB_INLINE int erts_ptab_dec_test_refc(ErtsPTabElementCommon *ptab_el) { #ifdef ERTS_SMP erts_aint32_t refc = erts_atomic32_dec_read_nob(&ptab_el->refc); ERTS_SMP_LC_ASSERT(refc >= 0); return (int) refc; #else return 0; #endif } ERTS_GLB_INLINE int erts_ptab_add_test_refc(ErtsPTabElementCommon *ptab_el, Sint32 add_refc) { #ifdef ERTS_SMP erts_aint32_t refc; #ifndef ERTS_ENABLE_LOCK_CHECK if (add_refc >= 0) { erts_atomic32_add_nob(&ptab_el->refc, (erts_aint32_t) add_refc); return 1; } #endif refc = erts_atomic32_add_read_nob(&ptab_el->refc, (erts_aint32_t) add_refc); ERTS_SMP_LC_ASSERT(refc >= 0); return (int) refc; #else return 0; #endif } ERTS_GLB_INLINE void erts_ptab_rlock(ErtsPTab *ptab) { erts_smp_rwmtx_rlock(&ptab->list.data.rwmtx); } ERTS_GLB_INLINE int erts_ptab_tryrlock(ErtsPTab *ptab) { return erts_smp_rwmtx_tryrlock(&ptab->list.data.rwmtx); } ERTS_GLB_INLINE void erts_ptab_runlock(ErtsPTab *ptab) { erts_smp_rwmtx_runlock(&ptab->list.data.rwmtx); } ERTS_GLB_INLINE void erts_ptab_rwlock(ErtsPTab *ptab) { erts_smp_rwmtx_rwlock(&ptab->list.data.rwmtx); } ERTS_GLB_INLINE int erts_ptab_tryrwlock(ErtsPTab *ptab) { return erts_smp_rwmtx_tryrwlock(&ptab->list.data.rwmtx); } ERTS_GLB_INLINE void erts_ptab_rwunlock(ErtsPTab *ptab) { erts_smp_rwmtx_rwunlock(&ptab->list.data.rwmtx); } ERTS_GLB_INLINE int erts_smp_lc_ptab_is_rlocked(ErtsPTab *ptab) { return erts_smp_lc_rwmtx_is_rlocked(&ptab->list.data.rwmtx); } ERTS_GLB_INLINE int erts_smp_lc_ptab_is_rwlocked(ErtsPTab *ptab) { return erts_smp_lc_rwmtx_is_rwlocked(&ptab->list.data.rwmtx); } #endif #endif #if defined(ERTS_PTAB_WANT_BIF_IMPL__) && !defined(ERTS_PTAB_LIST__) #define ERTS_PTAB_LIST__ #include "erl_process.h" #include "bif.h" BIF_RETTYPE erts_ptab_list(struct process *c_p, ErtsPTab *ptab); #endif #if defined(ERTS_PTAB_WANT_DEBUG_FUNCS__) && !defined(ERTS_PTAB_DEBUG_FUNCS__) #define ERTS_PTAB_DEBUG_FUNCS__ #include "erl_process.h" /* Debug functions */ Sint erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next); Eterm erts_debug_ptab_list(Process *c_p, ErtsPTab *ptab); Eterm erts_debug_ptab_list_bif_info(Process *c_p, ErtsPTab *ptab); #endif