diff options
Diffstat (limited to 'erts/emulator/beam/erl_lock_check.c')
-rw-r--r-- | erts/emulator/beam/erl_lock_check.c | 1162 |
1 files changed, 635 insertions, 527 deletions
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index c53bdc685b..3aab4828cc 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2017. All Rights Reserved. + * Copyright Ericsson AB 2005-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,8 @@ #include "erl_lock_check.h" #include "erl_term.h" #include "erl_threads.h" +#include "erl_atom_table.h" +#include "erl_utils.h" typedef struct { char *name; @@ -75,13 +77,10 @@ static erts_lc_lock_order_t erts_lock_order[] = { * if only one lock use * the lock name)" */ -#ifdef ERTS_SMP + { "NO LOCK", NULL }, { "driver_lock", "driver_name" }, { "port_lock", "port_id" }, -#endif { "port_data_lock", "address" }, -#ifdef ERTS_SMP - { "bif_timers", NULL }, { "reg_tab", NULL }, { "proc_main", "pid" }, { "old_code", "address" }, @@ -89,49 +88,41 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "hipe_mfait_lock", NULL }, #endif { "nodes_monitors", NULL }, + { "meta_name_tab", "address" }, + { "db_tab", "address" }, + { "db_tab_fix", "address" }, + { "db_hash_slot", "address" }, + { "erl_db_catree_base_node", NULL }, + { "erl_db_catree_route_node", "index" }, + { "resource_monitors", "address" }, { "driver_list", NULL }, - { "proc_link", "pid" }, { "proc_msgq", "pid" }, { "proc_btm", "pid" }, { "dist_entry", "address" }, { "dist_entry_links", "address" }, + { "update_persistent_term_permission", NULL }, + { "persistent_term_delete_permission", NULL }, { "code_write_permission", NULL }, { "purge_state", NULL }, { "proc_status", "pid" }, { "proc_trace", "pid" }, - { "ports_snapshot", NULL }, - { "meta_name_tab", "address" }, - { "meta_main_tab_slot", "address" }, - { "db_tab", "address" }, - { "db_tab_fix", "address" }, - { "meta_main_tab_main", NULL }, - { "db_hash_slot", "address" }, { "node_table", NULL }, { "dist_table", NULL }, { "sys_tracers", NULL }, - { "module_tab", NULL }, { "export_tab", NULL }, { "fun_tab", NULL }, { "environ", NULL }, -#ifdef ERTS_NEW_PURGE_STRATEGY { "release_literal_areas", NULL }, -#endif -#endif - { "efile_drv", "address" }, { "drv_ev_state_grow", NULL, }, { "drv_ev_state", "address" }, { "safe_hash", "address" }, - { "pollset_rm_list", NULL }, - { "removed_fd_pre_alloc_lock", "address" }, { "state_prealloc", NULL }, { "schdlr_sspnd", NULL }, { "migration_info_update", NULL }, { "run_queue", "address" }, -#ifdef ERTS_DIRTY_SCHEDULERS { "dirty_run_queue_sleep_list", "address" }, { "dirty_gc_info", NULL }, { "dirty_break_point_index", NULL }, -#endif { "process_table", NULL }, { "cpu_info", NULL }, { "pollset", "address" }, @@ -146,51 +137,31 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "async_enq_mtx", NULL }, { "msacc_list_mutex", NULL }, { "msacc_unmanaged_mutex", NULL }, -#ifdef ERTS_SMP { "atom_tab", NULL }, - { "misc_op_list_pre_alloc_lock", "address" }, - { "message_pre_alloc_lock", "address" }, - { "ptimer_pre_alloc_lock", "address", }, - { "btm_pre_alloc_lock", NULL, }, { "dist_entry_out_queue", "address" }, { "port_sched_lock", "port_id" }, { "sys_msg_q", NULL }, { "tracer_mtx", NULL }, { "port_table", NULL }, -#endif + { "magic_ref_table", "address" }, { "mtrace_op", NULL }, { "instr_x", NULL }, { "instr", NULL }, { "alcu_allocator", "index" }, { "mseg", NULL }, -#ifdef ERTS_SMP - { "port_task_pre_alloc_lock", "address" }, - { "proclist_pre_alloc_lock", "address" }, - { "xports_list_pre_alloc_lock", "address" }, - { "inet_buffer_stack_lock", NULL }, - { "system_block", NULL }, - { "timeofday", NULL }, { "get_time", NULL }, { "get_corrected_time", NULL }, - { "breakpoints", NULL }, - { "pollsets_lock", NULL }, + { "runtime", NULL }, { "pix_lock", "address" }, - { "run_queues_lists", NULL }, { "sched_stat", NULL }, -#endif { "async_init_mtx", NULL }, #ifdef __WIN32__ #ifdef DEBUG { "save_ops_lock", NULL }, #endif #endif -#ifdef USE_VM_PROBES - { "efile_drv dtrace mutex", NULL }, -#endif { "mtrace_buf", NULL }, -#ifdef ERTS_SMP { "os_monotonic_time", NULL }, -#endif { "erts_alloc_hard_debug", NULL }, { "hard_dbg_mseg", NULL }, { "erts_mmap", NULL } @@ -199,257 +170,263 @@ static erts_lc_lock_order_t erts_lock_order[] = { #define ERTS_LOCK_ORDER_SIZE \ (sizeof(erts_lock_order)/sizeof(erts_lc_lock_order_t)) -#define LOCK_IS_TYPE_ORDER_VIOLATION(LCK_FLG, LCKD_FLG) \ - (((LCKD_FLG) & (ERTS_LC_FLG_LT_SPINLOCK|ERTS_LC_FLG_LT_RWSPINLOCK)) \ - && ((LCK_FLG) \ - & ERTS_LC_FLG_LT_ALL \ - & ~(ERTS_LC_FLG_LT_SPINLOCK|ERTS_LC_FLG_LT_RWSPINLOCK))) +#define LOCK_IS_TYPE_ORDER_VIOLATION(LCK_FLG, LCKD_FLG) \ + (((LCKD_FLG) & ERTS_LOCK_FLAGS_MASK_TYPE) == ERTS_LOCK_FLAGS_TYPE_SPINLOCK \ + && \ + ((LCK_FLG) & ERTS_LOCK_FLAGS_MASK_TYPE) != ERTS_LOCK_FLAGS_TYPE_SPINLOCK) static __decl_noreturn void __noreturn lc_abort(void); -static char * -lock_type(Uint16 flags) +static const char *rw_op_str(erts_lock_options_t options) { - switch (flags & ERTS_LC_FLG_LT_ALL) { - case ERTS_LC_FLG_LT_SPINLOCK: return "[spinlock]"; - case ERTS_LC_FLG_LT_RWSPINLOCK: return "[rw(spin)lock]"; - case ERTS_LC_FLG_LT_MUTEX: return "[mutex]"; - case ERTS_LC_FLG_LT_RWMUTEX: return "[rwmutex]"; - case ERTS_LC_FLG_LT_PROCLOCK: return "[proclock]"; - default: return ""; + if(options == ERTS_LOCK_OPTIONS_WRITE) { + ERTS_INTERNAL_ERROR("Only write flag present"); } -} -static char * -rw_op_str(Uint16 flags) -{ - switch (flags & ERTS_LC_FLG_LO_READ_WRITE) { - case ERTS_LC_FLG_LO_READ_WRITE: - return " (rw)"; - case ERTS_LC_FLG_LO_READ: - return " (r)"; - case ERTS_LC_FLG_LO_WRITE: - ERTS_INTERNAL_ERROR("Only write flag present"); - default: - break; - } - return ""; + return erts_lock_options_get_short_desc(options); } -typedef struct erts_lc_locked_lock_t_ erts_lc_locked_lock_t; -struct erts_lc_locked_lock_t_ { - erts_lc_locked_lock_t *next; - erts_lc_locked_lock_t *prev; +typedef struct lc_locked_lock_t_ lc_locked_lock_t; +struct lc_locked_lock_t_ { + lc_locked_lock_t *next; + lc_locked_lock_t *prev; UWord extra; Sint16 id; char *file; unsigned int line; - Uint16 flags; + erts_lock_flags_t flags; + erts_lock_options_t taken_options; + /* + * Pointer back to the lock instance if it exists or NULL for proc locks. + * If set, we use it to allow trylock of other lock instance + * but with identical lock order as an already locked lock. + */ + erts_lc_lock_t *lck; }; typedef struct { - erts_lc_locked_lock_t *first; - erts_lc_locked_lock_t *last; -} erts_lc_locked_lock_list_t; + lc_locked_lock_t *first; + lc_locked_lock_t *last; +} lc_locked_lock_list_t; + +typedef union lc_free_block_t_ lc_free_block_t; +union lc_free_block_t_ { + lc_free_block_t *next; + lc_locked_lock_t lock; +}; + +typedef struct { + /* + * m[X][Y] & 1 if we locked X directly after Y was locked. + * m[X][Y] & 2 if we locked X indirectly after Y was locked. + * m[X][0] = 1 if we locked X when nothing else was locked. + * m[0][] is unused as it would represent locking "NO LOCK" + */ + char m[ERTS_LOCK_ORDER_SIZE][ERTS_LOCK_ORDER_SIZE]; + +} lc_matrix_t; -typedef struct erts_lc_locked_locks_t_ erts_lc_locked_locks_t; -struct erts_lc_locked_locks_t_ { +static lc_matrix_t tot_lc_matrix; + +#define ERTS_LC_FB_CHUNK_SIZE 10 + +typedef struct lc_alloc_chunk_t_ lc_alloc_chunk_t; +struct lc_alloc_chunk_t_ { + lc_alloc_chunk_t* next; + lc_free_block_t array[ERTS_LC_FB_CHUNK_SIZE]; +}; + +typedef struct lc_thread_t_ lc_thread_t; +struct lc_thread_t_ { char *thread_name; int emu_thread; erts_tid_t tid; - erts_lc_locked_locks_t *next; - erts_lc_locked_locks_t *prev; - erts_lc_locked_lock_list_t locked; - erts_lc_locked_lock_list_t required; -}; - -typedef union erts_lc_free_block_t_ erts_lc_free_block_t; -union erts_lc_free_block_t_ { - erts_lc_free_block_t *next; - erts_lc_locked_lock_t lock; + lc_thread_t *next; + lc_thread_t *prev; + lc_locked_lock_list_t locked; + lc_locked_lock_list_t required; + lc_free_block_t *free_blocks; + lc_alloc_chunk_t *chunks; + lc_matrix_t matrix; }; static ethr_tsd_key locks_key; -static erts_lc_locked_locks_t *erts_locked_locks = NULL; - -static erts_lc_free_block_t *free_blocks = NULL; - -#ifdef ERTS_LC_STATIC_ALLOC -#define ERTS_LC_FB_CHUNK_SIZE 10000 -#else -#define ERTS_LC_FB_CHUNK_SIZE 10 -#endif - -static ethr_spinlock_t free_blocks_lock; +static lc_thread_t *lc_threads = NULL; +static ethr_spinlock_t lc_threads_lock; static ERTS_INLINE void -lc_lock(void) +lc_lock_threads(void) { - ethr_spin_lock(&free_blocks_lock); + ethr_spin_lock(&lc_threads_lock); } static ERTS_INLINE void -lc_unlock(void) +lc_unlock_threads(void) { - ethr_spin_unlock(&free_blocks_lock); + ethr_spin_unlock(&lc_threads_lock); } -static ERTS_INLINE void lc_free(void *p) +static ERTS_INLINE void lc_free(lc_thread_t* thr, lc_locked_lock_t *p) { - erts_lc_free_block_t *fb = (erts_lc_free_block_t *) p; + lc_free_block_t *fb = (lc_free_block_t *) p; #ifdef DEBUG - memset((void *) p, 0xdf, sizeof(erts_lc_free_block_t)); + sys_memset((void *) p, 0xdf, sizeof(lc_free_block_t)); #endif - lc_lock(); - fb->next = free_blocks; - free_blocks = fb; - lc_unlock(); + fb->next = thr->free_blocks; + thr->free_blocks = fb; } -#ifdef ERTS_LC_STATIC_ALLOC - -static void *lc_core_alloc(void) -{ - lc_unlock(); - ERTS_INTERNAL_ERROR("Lock checker out of memory!\n"); -} - -#else - -static void *lc_core_alloc(void) +static lc_locked_lock_t *lc_core_alloc(lc_thread_t* thr) { int i; - erts_lc_free_block_t *fbs; - lc_unlock(); - fbs = (erts_lc_free_block_t *) malloc(sizeof(erts_lc_free_block_t) - * ERTS_LC_FB_CHUNK_SIZE); - if (!fbs) { + lc_alloc_chunk_t* chunk; + lc_free_block_t* fbs; + chunk = (lc_alloc_chunk_t*) malloc(sizeof(lc_alloc_chunk_t)); + if (!chunk) { ERTS_INTERNAL_ERROR("Lock checker failed to allocate memory!"); } + chunk->next = thr->chunks; + thr->chunks = chunk; + + fbs = chunk->array; for (i = 1; i < ERTS_LC_FB_CHUNK_SIZE - 1; i++) { #ifdef DEBUG - memset((void *) &fbs[i], 0xdf, sizeof(erts_lc_free_block_t)); + sys_memset((void *) &fbs[i], 0xdf, sizeof(lc_free_block_t)); #endif fbs[i].next = &fbs[i+1]; } #ifdef DEBUG - memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1], - 0xdf, sizeof(erts_lc_free_block_t)); + sys_memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1], + 0xdf, sizeof(lc_free_block_t)); #endif - lc_lock(); - fbs[ERTS_LC_FB_CHUNK_SIZE-1].next = free_blocks; - free_blocks = &fbs[1]; - return (void *) &fbs[0]; + fbs[ERTS_LC_FB_CHUNK_SIZE-1].next = thr->free_blocks; + thr->free_blocks = &fbs[1]; + return &fbs[0].lock; } -#endif - -static ERTS_INLINE void *lc_alloc(void) +static ERTS_INLINE lc_locked_lock_t *lc_alloc(lc_thread_t* thr) { - void *res; - lc_lock(); - if (!free_blocks) - res = lc_core_alloc(); + lc_locked_lock_t *res; + if (!thr->free_blocks) + res = lc_core_alloc(thr); else { - res = (void *) free_blocks; - free_blocks = free_blocks->next; + res = &thr->free_blocks->lock; + thr->free_blocks = thr->free_blocks->next; } - lc_unlock(); return res; } -static erts_lc_locked_locks_t * -create_locked_locks(char *thread_name) +static lc_thread_t * +create_thread_data(char *thread_name) { - erts_lc_locked_locks_t *l_lcks = malloc(sizeof(erts_lc_locked_locks_t)); - if (!l_lcks) + lc_thread_t *thr = malloc(sizeof(lc_thread_t)); + if (!thr) ERTS_INTERNAL_ERROR("Lock checker failed to allocate memory!"); - l_lcks->thread_name = strdup(thread_name ? thread_name : "unknown"); - if (!l_lcks->thread_name) + thr->thread_name = strdup(thread_name ? thread_name : "unknown"); + if (!thr->thread_name) ERTS_INTERNAL_ERROR("Lock checker failed to allocate memory!"); - l_lcks->emu_thread = 0; - l_lcks->tid = erts_thr_self(); - l_lcks->required.first = NULL; - l_lcks->required.last = NULL; - l_lcks->locked.first = NULL; - l_lcks->locked.last = NULL; - l_lcks->prev = NULL; - lc_lock(); - l_lcks->next = erts_locked_locks; - if (erts_locked_locks) - erts_locked_locks->prev = l_lcks; - erts_locked_locks = l_lcks; - lc_unlock(); - erts_tsd_set(locks_key, (void *) l_lcks); - return l_lcks; + thr->emu_thread = 0; + thr->tid = erts_thr_self(); + thr->required.first = NULL; + thr->required.last = NULL; + thr->locked.first = NULL; + thr->locked.last = NULL; + thr->prev = NULL; + thr->free_blocks = NULL; + thr->chunks = NULL; + sys_memzero(&thr->matrix, sizeof(thr->matrix)); + + lc_lock_threads(); + thr->next = lc_threads; + if (lc_threads) + lc_threads->prev = thr; + lc_threads = thr; + lc_unlock_threads(); + erts_tsd_set(locks_key, (void *) thr); + return thr; } +static void collect_matrix(lc_matrix_t*); + static void -destroy_locked_locks(erts_lc_locked_locks_t *l_lcks) -{ - ASSERT(l_lcks->thread_name); - free((void *) l_lcks->thread_name); - ASSERT(l_lcks->required.first == NULL); - ASSERT(l_lcks->required.last == NULL); - ASSERT(l_lcks->locked.first == NULL); - ASSERT(l_lcks->locked.last == NULL); - - lc_lock(); - if (l_lcks->prev) - l_lcks->prev->next = l_lcks->next; +destroy_thread_data(lc_thread_t *thr) +{ + ASSERT(thr->thread_name); + free((void *) thr->thread_name); + ASSERT(thr->required.first == NULL); + ASSERT(thr->required.last == NULL); + ASSERT(thr->locked.first == NULL); + ASSERT(thr->locked.last == NULL); + + lc_lock_threads(); + if (thr->prev) + thr->prev->next = thr->next; else { - ASSERT(erts_locked_locks == l_lcks); - erts_locked_locks = l_lcks->next; + ASSERT(lc_threads == thr); + lc_threads = thr->next; } + if (thr->next) + thr->next->prev = thr->prev; + + collect_matrix(&thr->matrix); - if (l_lcks->next) - l_lcks->next->prev = l_lcks->prev; - lc_unlock(); + lc_unlock_threads(); - free((void *) l_lcks); + while (thr->chunks) { + lc_alloc_chunk_t* free_me = thr->chunks; + thr->chunks = thr->chunks->next; + free(free_me); + } + free((void *) thr); } -static ERTS_INLINE erts_lc_locked_locks_t * +static ERTS_INLINE lc_thread_t * get_my_locked_locks(void) { return erts_tsd_get(locks_key); } -static ERTS_INLINE erts_lc_locked_locks_t * +static ERTS_INLINE lc_thread_t * make_my_locked_locks(void) { - erts_lc_locked_locks_t *l_lcks = get_my_locked_locks(); - if (l_lcks) - return l_lcks; + lc_thread_t *thr = get_my_locked_locks(); + if (thr) + return thr; else - return create_locked_locks(NULL); + return create_thread_data(NULL); } -static ERTS_INLINE erts_lc_locked_lock_t * -new_locked_lock(erts_lc_lock_t *lck, Uint16 op_flags, +static ERTS_INLINE lc_locked_lock_t * +new_locked_lock(lc_thread_t* thr, + erts_lc_lock_t *lck, erts_lock_options_t options, char *file, unsigned int line) { - erts_lc_locked_lock_t *l_lck = (erts_lc_locked_lock_t *) lc_alloc(); - l_lck->next = NULL; - l_lck->prev = NULL; - l_lck->id = lck->id; - l_lck->extra = lck->extra; - l_lck->file = file; - l_lck->line = line; - l_lck->flags = lck->flags | op_flags; - return l_lck; + lc_locked_lock_t *ll = lc_alloc(thr); + ll->next = NULL; + ll->prev = NULL; + ll->id = lck->id; + ll->extra = lck->extra; + ll->file = file; + ll->line = line; + ll->flags = lck->flags; + ll->taken_options = options; + if ((lck->flags & ERTS_LOCK_FLAGS_MASK_TYPE) == ERTS_LOCK_FLAGS_TYPE_PROCLOCK) + ll->lck = NULL; + else + ll->lck = lck; + return ll; } static void -raw_print_lock(char *prefix, Sint16 id, Wterm extra, Uint16 flags, +raw_print_lock(char *prefix, Sint16 id, Wterm extra, erts_lock_flags_t flags, char* file, unsigned int line, char *suffix) { - char *lname = (0 <= id && id < ERTS_LOCK_ORDER_SIZE + char *lname = (1 <= id && id < ERTS_LOCK_ORDER_SIZE ? erts_lock_order[id].name : "unknown"); erts_fprintf(stderr,"%s'%s:",prefix,lname); @@ -458,16 +435,16 @@ raw_print_lock(char *prefix, Sint16 id, Wterm extra, Uint16 flags, erts_fprintf(stderr,"%p",_unchecked_boxed_val(extra)); else erts_fprintf(stderr,"%T",extra); - erts_fprintf(stderr,"%s",lock_type(flags)); + erts_fprintf(stderr,"[%s]",erts_lock_flags_get_type_name(flags)); if (file) erts_fprintf(stderr,"(%s:%d)",file,line); - erts_fprintf(stderr,"'%s%s",rw_op_str(flags),suffix); + erts_fprintf(stderr,"'(%s)%s",rw_op_str(flags),suffix); } static void -print_lock2(char *prefix, Sint16 id, Wterm extra, Uint16 flags, char *suffix) +print_lock2(char *prefix, Sint16 id, Wterm extra, erts_lock_flags_t flags, char *suffix) { raw_print_lock(prefix, id, extra, flags, NULL, 0, suffix); } @@ -479,20 +456,20 @@ print_lock(char *prefix, erts_lc_lock_t *lck, char *suffix) } static void -print_curr_locks(erts_lc_locked_locks_t *l_lcks) +print_curr_locks(lc_thread_t *thr) { - erts_lc_locked_lock_t *l_lck; - if (!l_lcks || !l_lcks->locked.first) + lc_locked_lock_t *ll; + if (!thr || !thr->locked.first) erts_fprintf(stderr, "Currently no locks are locked by the %s thread.\n", - l_lcks->thread_name); + thr->thread_name); else { erts_fprintf(stderr, "Currently these locks are locked by the %s thread:\n", - l_lcks->thread_name); - for (l_lck = l_lcks->locked.first; l_lck; l_lck = l_lck->next) - raw_print_lock(" ", l_lck->id, l_lck->extra, l_lck->flags, - l_lck->file, l_lck->line, "\n"); + thr->thread_name); + for (ll = thr->locked.first; ll; ll = ll->next) + raw_print_lock(" ", ll->id, ll->extra, ll->flags, + ll->file, ll->line, "\n"); } } @@ -521,55 +498,55 @@ uninitialized_lock(void) } static void -lock_twice(char *prefix, erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck, - Uint16 op_flags) +lock_twice(char *prefix, lc_thread_t *thr, erts_lc_lock_t *lck, + erts_lock_options_t options) { - erts_fprintf(stderr, "%s%s", prefix, rw_op_str(op_flags)); + erts_fprintf(stderr, "%s (%s)", prefix, rw_op_str(options)); print_lock(" ", lck, " lock which is already locked by thread!\n"); - print_curr_locks(l_lcks); + print_curr_locks(thr); lc_abort(); } static void -unlock_op_mismatch(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck, - Uint16 op_flags) +unlock_op_mismatch(lc_thread_t *thr, erts_lc_lock_t *lck, + erts_lock_options_t options) { - erts_fprintf(stderr, "Unlocking%s ", rw_op_str(op_flags)); + erts_fprintf(stderr, "Unlocking (%s) ", rw_op_str(options)); print_lock("", lck, " lock which mismatch previous lock operation!\n"); - print_curr_locks(l_lcks); + print_curr_locks(thr); lc_abort(); } static void -unlock_of_not_locked(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck) +unlock_of_not_locked(lc_thread_t *thr, erts_lc_lock_t *lck) { print_lock("Unlocking ", lck, " lock which is not locked by thread!\n"); - print_curr_locks(l_lcks); + print_curr_locks(thr); lc_abort(); } static void -lock_order_violation(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck) +lock_order_violation(lc_thread_t *thr, erts_lc_lock_t *lck) { print_lock("Lock order violation occured when locking ", lck, "!\n"); - print_curr_locks(l_lcks); + print_curr_locks(thr); print_lock_order(); lc_abort(); } static void -type_order_violation(char *op, erts_lc_locked_locks_t *l_lcks, +type_order_violation(char *op, lc_thread_t *thr, erts_lc_lock_t *lck) { erts_fprintf(stderr, "Lock type order violation occured when "); print_lock(op, lck, "!\n"); - ASSERT(l_lcks); - print_curr_locks(l_lcks); + ASSERT(thr); + print_curr_locks(thr); lc_abort(); } static void -lock_mismatch(erts_lc_locked_locks_t *l_lcks, int exact, +lock_mismatch(lc_thread_t *thr, int exact, int failed_have, erts_lc_lock_t *have, int have_len, int failed_have_not, erts_lc_lock_t *have_not, int have_not_len) { @@ -616,39 +593,39 @@ lock_mismatch(erts_lc_locked_locks_t *l_lcks, int exact, print_lock2(" ", have_not[i].id, have_not[i].extra, 0, "\n"); } } - print_curr_locks(l_lcks); + print_curr_locks(thr); lc_abort(); } static void -unlock_of_required_lock(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck) +unlock_of_required_lock(lc_thread_t *thr, erts_lc_lock_t *lck) { print_lock("Unlocking required ", lck, " lock!\n"); - print_curr_locks(l_lcks); + print_curr_locks(thr); lc_abort(); } static void -unrequire_of_not_required_lock(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck) +unrequire_of_not_required_lock(lc_thread_t *thr, erts_lc_lock_t *lck) { print_lock("Unrequire on ", lck, " lock not required!\n"); - print_curr_locks(l_lcks); + print_curr_locks(thr); lc_abort(); } static void -require_twice(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck) +require_twice(lc_thread_t *thr, erts_lc_lock_t *lck) { print_lock("Require on ", lck, " lock already required!\n"); - print_curr_locks(l_lcks); + print_curr_locks(thr); lc_abort(); } static void -required_not_locked(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck) +required_not_locked(lc_thread_t *thr, erts_lc_lock_t *lck) { print_lock("Required ", lck, " lock not locked!\n"); - print_curr_locks(l_lcks); + print_curr_locks(thr); lc_abort(); } @@ -656,15 +633,15 @@ required_not_locked(erts_lc_locked_locks_t *l_lcks, erts_lc_lock_t *lck) static void thread_exit_handler(void) { - erts_lc_locked_locks_t *l_lcks = get_my_locked_locks(); - if (l_lcks) { - if (l_lcks->locked.first) { + lc_thread_t *thr = get_my_locked_locks(); + if (thr) { + if (thr->locked.first) { erts_fprintf(stderr, "Thread exiting while having locked locks!\n"); - print_curr_locks(l_lcks); + print_curr_locks(thr); lc_abort(); } - destroy_locked_locks(l_lcks); + destroy_thread_data(thr); /* erts_tsd_set(locks_key, NULL); */ } } @@ -682,24 +659,24 @@ lc_abort(void) void erts_lc_set_thread_name(char *thread_name) { - erts_lc_locked_locks_t *l_lcks = get_my_locked_locks(); - if (!l_lcks) - l_lcks = create_locked_locks(thread_name); + lc_thread_t *thr = get_my_locked_locks(); + if (!thr) + thr = create_thread_data(thread_name); else { - ASSERT(l_lcks->thread_name); - free((void *) l_lcks->thread_name); - l_lcks->thread_name = strdup(thread_name ? thread_name : "unknown"); - if (!l_lcks->thread_name) + ASSERT(thr->thread_name); + free((void *) thr->thread_name); + thr->thread_name = strdup(thread_name ? thread_name : "unknown"); + if (!thr->thread_name) ERTS_INTERNAL_ERROR("strdup failed"); } - l_lcks->emu_thread = 1; + thr->emu_thread = 1; } int erts_lc_is_emu_thr(void) { - erts_lc_locked_locks_t *l_lcks = get_my_locked_locks(); - return l_lcks->emu_thread; + lc_thread_t *thr = get_my_locked_locks(); + return thr->emu_thread; } int @@ -734,7 +711,7 @@ erts_lc_get_lock_order_id(char *name) erts_fprintf(stderr, "Missing lock name\n"); else { for (i = 0; i < ERTS_LOCK_ORDER_SIZE; i++) - if (strcmp(erts_lock_order[i].name, name) == 0) + if (sys_strcmp(erts_lock_order[i].name, name) == 0) return i; erts_fprintf(stderr, "Lock name '%s' missing in lock order " @@ -745,115 +722,172 @@ erts_lc_get_lock_order_id(char *name) return (Sint16) -1; } - static int -find_lock(erts_lc_locked_lock_t **l_lcks, erts_lc_lock_t *lck) +lc_is_term_order(Sint16 id) { - erts_lc_locked_lock_t *l_lck = *l_lcks; + return erts_lock_order[id].internal_order != NULL + && sys_strcmp(erts_lock_order[id].internal_order, "term") == 0; +} - if (l_lck) { - if (l_lck->id == lck->id && l_lck->extra == lck->extra) { - if ((l_lck->flags & lck->flags) == lck->flags) - return 1; - return 0; - } - else if (l_lck->id < lck->id - || (l_lck->id == lck->id - && l_lck->extra < lck->extra)) { - for (l_lck = l_lck->next; l_lck; l_lck = l_lck->next) { - if (l_lck->id > lck->id - || (l_lck->id == lck->id - && l_lck->extra >= lck->extra)) { - *l_lcks = l_lck; - if (l_lck->id == lck->id - && l_lck->extra == lck->extra - && ((l_lck->flags & lck->flags) == lck->flags)) - return 1; - return 0; - } - } - } - else { - for (l_lck = l_lck->prev; l_lck; l_lck = l_lck->prev) { - if (l_lck->id < lck->id - || (l_lck->id == lck->id - && l_lck->extra <= lck->extra)) { - *l_lcks = l_lck; - if (l_lck->id == lck->id - && l_lck->extra == lck->extra - && ((l_lck->flags & lck->flags) == lck->flags)) - return 1; - return 0; - } - } - } + +static int compare_locked_by_id(lc_locked_lock_t *locked_lock, erts_lc_lock_t *comparand) +{ + if(locked_lock->id < comparand->id) { + return -1; + } else if(locked_lock->id > comparand->id) { + return 1; } + return 0; } +static int compare_locked_by_id_extra(lc_locked_lock_t *ll, erts_lc_lock_t *comparand) +{ + int order = compare_locked_by_id(ll, comparand); + + if(order) { + return order; + } + if (ll->flags & ERTS_LOCK_FLAGS_PROPERTY_TERM_ORDER) { + ASSERT(!is_header(ll->extra) && !is_header(comparand->extra)); + return CMP(ll->extra, comparand->extra); + } + + if(ll->extra < comparand->extra) { + return -1; + } else if(ll->extra > comparand->extra) { + return 1; + } + return 0; +} + +typedef int (*locked_compare_func)(lc_locked_lock_t *, erts_lc_lock_t *); + +/* Searches through a list of taken locks, bailing when it hits an entry whose + * order relative to the search template is the opposite of the one at the + * start of the search. (*closest_neighbor) is either set to the exact match, + * or the one closest to it in the sort order. */ +static int search_locked_list(locked_compare_func compare, + lc_locked_lock_t *locked_locks, + erts_lc_lock_t *search_template, + lc_locked_lock_t **closest_neighbor) +{ + lc_locked_lock_t *iterator = locked_locks; + + (*closest_neighbor) = iterator; + + if(iterator) { + int relative_order = compare(iterator, search_template); + + if(relative_order < 0) { + while((iterator = iterator->next) != NULL) { + relative_order = compare(iterator, search_template); + + if(relative_order >= 0) { + (*closest_neighbor) = iterator; + break; + } + } + } else if(relative_order > 0) { + while((iterator = iterator->prev) != NULL) { + relative_order = compare(iterator, search_template); + + if(relative_order <= 0) { + (*closest_neighbor) = iterator; + break; + } + } + } + + return relative_order == 0; + } + + return 0; +} + +/* Searches for a lock in the given list that matches search_template, and sets + * (*locked_locks) to the closest lock in the sort order. */ static int -find_id(erts_lc_locked_lock_t **l_lcks, Sint16 id) -{ - erts_lc_locked_lock_t *l_lck = *l_lcks; - - if (l_lck) { - if (l_lck->id == id) - return 1; - else if (l_lck->id < id) { - for (l_lck = l_lck->next; l_lck; l_lck = l_lck->next) { - if (l_lck->id >= id) { - *l_lcks = l_lck; - if (l_lck->id == id) - return 1; - return 0; - } - } - } - else { - for (l_lck = l_lck->prev; l_lck; l_lck = l_lck->prev) { - if (l_lck->id <= id) { - *l_lcks = l_lck; - if (l_lck->id == id) - return 1; - return 0; - } - } - } +find_lock(lc_locked_lock_t **locked_locks, erts_lc_lock_t *search_template) +{ + lc_locked_lock_t *closest_neighbor; + int found_lock; + + found_lock = search_locked_list(compare_locked_by_id_extra, + (*locked_locks), + search_template, + &closest_neighbor); + + (*locked_locks) = closest_neighbor; + + if(found_lock) { + erts_lock_options_t relevant_options; + erts_lock_flags_t relevant_flags; + + /* We only care about the options and flags that are set in the + * template. */ + relevant_options = (closest_neighbor->taken_options & search_template->taken_options); + relevant_flags = (closest_neighbor->flags & search_template->flags); + + return search_template->taken_options == relevant_options && + search_template->flags == relevant_flags; } + return 0; } +/* Searches for a lock in the given list by id, and sets (*locked_locks) to the + * closest lock in the sort order. */ +static int +find_id(lc_locked_lock_t **locked_locks, Sint16 id) +{ + lc_locked_lock_t *closest_neighbor; + erts_lc_lock_t search_template; + int found_lock; + + search_template.id = id; + + found_lock = search_locked_list(compare_locked_by_id, + (*locked_locks), + &search_template, + &closest_neighbor); + + (*locked_locks) = closest_neighbor; + + return found_lock; +} + void erts_lc_have_locks(int *resv, erts_lc_lock_t *locks, int len) { - erts_lc_locked_locks_t *l_lcks = get_my_locked_locks(); + lc_thread_t *thr = get_my_locked_locks(); int i; - if (!l_lcks) { + if (!thr) { for (i = 0; i < len; i++) resv[i] = 0; } else { - erts_lc_locked_lock_t *l_lck = l_lcks->locked.first; + lc_locked_lock_t *ll = thr->locked.first; for (i = 0; i < len; i++) - resv[i] = find_lock(&l_lck, &locks[i]); + resv[i] = find_lock(&ll, &locks[i]); } } void erts_lc_have_lock_ids(int *resv, int *ids, int len) { - erts_lc_locked_locks_t *l_lcks = get_my_locked_locks(); + lc_thread_t *thr = get_my_locked_locks(); int i; - if (!l_lcks) { + if (!thr) { for (i = 0; i < len; i++) resv[i] = 0; } else { - erts_lc_locked_lock_t *l_lck = l_lcks->locked.first; + lc_locked_lock_t *ll = thr->locked.first; for (i = 0; i < len; i++) - resv[i] = find_id(&l_lck, ids[i]); + resv[i] = find_id(&ll, ids[i]); } } @@ -862,27 +896,27 @@ erts_lc_check(erts_lc_lock_t *have, int have_len, erts_lc_lock_t *have_not, int have_not_len) { int i; - erts_lc_locked_locks_t *l_lcks = get_my_locked_locks(); - erts_lc_locked_lock_t *l_lck; + lc_thread_t *thr = get_my_locked_locks(); + lc_locked_lock_t *ll; if (have && have_len > 0) { - if (!l_lcks) + if (!thr) lock_mismatch(NULL, 0, -1, have, have_len, -1, have_not, have_not_len); - l_lck = l_lcks->locked.first; + ll = thr->locked.first; for (i = 0; i < have_len; i++) { - if (!find_lock(&l_lck, &have[i])) - lock_mismatch(l_lcks, 0, + if (!find_lock(&ll, &have[i])) + lock_mismatch(thr, 0, i, have, have_len, -1, have_not, have_not_len); } } - if (have_not && have_not_len > 0 && l_lcks) { - l_lck = l_lcks->locked.first; + if (have_not && have_not_len > 0 && thr) { + ll = thr->locked.first; for (i = 0; i < have_not_len; i++) { - if (find_lock(&l_lck, &have_not[i])) - lock_mismatch(l_lcks, 0, + if (find_lock(&ll, &have_not[i])) + lock_mismatch(thr, 0, -1, have, have_len, i, have_not, have_not_len); } @@ -892,8 +926,8 @@ erts_lc_check(erts_lc_lock_t *have, int have_len, void erts_lc_check_exact(erts_lc_lock_t *have, int have_len) { - erts_lc_locked_locks_t *l_lcks = get_my_locked_locks(); - if (!l_lcks) { + lc_thread_t *thr = get_my_locked_locks(); + if (!thr) { if (have && have_len > 0) lock_mismatch(NULL, 1, -1, have, have_len, @@ -901,35 +935,35 @@ erts_lc_check_exact(erts_lc_lock_t *have, int have_len) } else { int i; - erts_lc_locked_lock_t *l_lck = l_lcks->locked.first; + lc_locked_lock_t *ll = thr->locked.first; for (i = 0; i < have_len; i++) { - if (!find_lock(&l_lck, &have[i])) - lock_mismatch(l_lcks, 1, + if (!find_lock(&ll, &have[i])) + lock_mismatch(thr, 1, i, have, have_len, -1, NULL, 0); } - for (i = 0, l_lck = l_lcks->locked.first; l_lck; l_lck = l_lck->next) + for (i = 0, ll = thr->locked.first; ll; ll = ll->next) i++; if (i != have_len) - lock_mismatch(l_lcks, 1, + lock_mismatch(thr, 1, -1, have, have_len, -1, NULL, 0); } } void -erts_lc_check_no_locked_of_type(Uint16 flags) +erts_lc_check_no_locked_of_type(erts_lock_flags_t type) { - erts_lc_locked_locks_t *l_lcks = get_my_locked_locks(); - if (l_lcks) { - erts_lc_locked_lock_t *l_lck = l_lcks->locked.first; - for (l_lck = l_lcks->locked.first; l_lck; l_lck = l_lck->next) { - if (l_lck->flags & flags) { + lc_thread_t *thr = get_my_locked_locks(); + if (thr) { + lc_locked_lock_t *ll = thr->locked.first; + for (ll = thr->locked.first; ll; ll = ll->next) { + if ((ll->flags & ERTS_LOCK_FLAGS_MASK_TYPE) == type) { erts_fprintf(stderr, "Locked lock of type %s found which isn't " "allowed here!\n", - lock_type(l_lck->flags)); - print_curr_locks(l_lcks); + erts_lock_flags_get_type_name(ll->flags)); + print_curr_locks(thr); lc_abort(); } } @@ -937,7 +971,7 @@ erts_lc_check_no_locked_of_type(Uint16 flags) } int -erts_lc_trylock_force_busy_flg(erts_lc_lock_t *lck, Uint16 op_flags) +erts_lc_trylock_force_busy_flg(erts_lc_lock_t *lck, erts_lock_options_t options) { #ifdef ERTS_LC_DO_NOT_FORCE_BUSY_TRYLOCK_ON_LOCK_ORDER_VIOLATION return 0; @@ -947,7 +981,7 @@ erts_lc_trylock_force_busy_flg(erts_lc_lock_t *lck, Uint16 op_flags) * This in order to make sure that caller can handle * the situation without causing a lock order violation. */ - erts_lc_locked_locks_t *l_lcks; + lc_thread_t *thr; if (lck->inited != ERTS_LC_INITITALIZED) uninitialized_lock(); @@ -955,41 +989,42 @@ erts_lc_trylock_force_busy_flg(erts_lc_lock_t *lck, Uint16 op_flags) if (lck->id < 0) return 0; - l_lcks = get_my_locked_locks(); + thr = get_my_locked_locks(); - if (!l_lcks || !l_lcks->locked.first) { - ASSERT(!l_lcks || !l_lcks->locked.last); + if (!thr || !thr->locked.first) { + ASSERT(!thr || !thr->locked.last); return 0; } else { - erts_lc_locked_lock_t *tl_lck; + lc_locked_lock_t *ll; + int order; - ASSERT(l_lcks->locked.last); + ASSERT(thr->locked.last); #if 0 /* Ok when trylocking I guess... */ - if (LOCK_IS_TYPE_ORDER_VIOLATION(lck->flags, l_lcks->locked.last->flags)) - type_order_violation("trylocking ", l_lcks, lck); + if (LOCK_IS_TYPE_ORDER_VIOLATION(lck->flags, thr->locked.last->flags)) + type_order_violation("trylocking ", thr, lck); #endif - if (l_lcks->locked.last->id < lck->id - || (l_lcks->locked.last->id == lck->id - && l_lcks->locked.last->extra < lck->extra)) - return 0; + ll = thr->locked.last; + order = compare_locked_by_id_extra(ll, lck); + + if (order < 0) + return 0; /* - * Lock order violation + * TryLock order violation */ - - /* Check that we are not trying to lock this lock twice */ - for (tl_lck = l_lcks->locked.last; tl_lck; tl_lck = tl_lck->prev) { - if (tl_lck->id < lck->id - || (tl_lck->id == lck->id && tl_lck->extra <= lck->extra)) { - if (tl_lck->id == lck->id && tl_lck->extra == lck->extra) - lock_twice("Trylocking", l_lcks, lck, op_flags); - break; - } - } + /* Check that we are not trying to lock this lock twice */ + do { + if (order == 0 && (ll->lck == lck || !ll->lck)) + lock_twice("Trylocking", thr, lck, options); + ll = ll->prev; + if (!ll) + break; + order = compare_locked_by_id_extra(ll, lck); + } while (order >= 0); #ifndef ERTS_LC_ALLWAYS_FORCE_BUSY_TRYLOCK_ON_LOCK_ORDER_VIOLATION /* We only force busy if a lock order violation would occur @@ -1008,11 +1043,11 @@ erts_lc_trylock_force_busy_flg(erts_lc_lock_t *lck, Uint16 op_flags) #endif } -void erts_lc_trylock_flg_x(int locked, erts_lc_lock_t *lck, Uint16 op_flags, +void erts_lc_trylock_flg_x(int locked, erts_lc_lock_t *lck, erts_lock_options_t options, char *file, unsigned int line) { - erts_lc_locked_locks_t *l_lcks; - erts_lc_locked_lock_t *l_lck; + lc_thread_t *thr; + lc_locked_lock_t *ll; if (lck->inited != ERTS_LC_INITITALIZED) uninitialized_lock(); @@ -1020,128 +1055,129 @@ void erts_lc_trylock_flg_x(int locked, erts_lc_lock_t *lck, Uint16 op_flags, if (lck->id < 0) return; - l_lcks = make_my_locked_locks(); - l_lck = locked ? new_locked_lock(lck, op_flags, file, line) : NULL; + thr = make_my_locked_locks(); + ll = locked ? new_locked_lock(thr, lck, options, file, line) : NULL; - if (!l_lcks->locked.last) { - ASSERT(!l_lcks->locked.first); + if (!thr->locked.last) { + ASSERT(!thr->locked.first); if (locked) - l_lcks->locked.first = l_lcks->locked.last = l_lck; + thr->locked.first = thr->locked.last = ll; } else { - erts_lc_locked_lock_t *tl_lck; + lc_locked_lock_t *tl_lck; #if 0 /* Ok when trylocking I guess... */ - if (LOCK_IS_TYPE_ORDER_VIOLATION(lck->flags, l_lcks->locked.last->flags)) - type_order_violation("trylocking ", l_lcks, lck); + if (LOCK_IS_TYPE_ORDER_VIOLATION(lck->flags, thr->locked.last->flags)) + type_order_violation("trylocking ", thr, lck); #endif - for (tl_lck = l_lcks->locked.last; tl_lck; tl_lck = tl_lck->prev) { - if (tl_lck->id < lck->id - || (tl_lck->id == lck->id && tl_lck->extra <= lck->extra)) { - if (tl_lck->id == lck->id && tl_lck->extra == lck->extra) - lock_twice("Trylocking", l_lcks, lck, op_flags); + for (tl_lck = thr->locked.last; tl_lck; tl_lck = tl_lck->prev) { + int order = compare_locked_by_id_extra(tl_lck, lck); + if (order <= 0) { + if (order == 0 && (tl_lck->lck == lck || !tl_lck->lck)) + lock_twice("Trylocking", thr, lck, options); if (locked) { - l_lck->next = tl_lck->next; - l_lck->prev = tl_lck; + ll->next = tl_lck->next; + ll->prev = tl_lck; if (tl_lck->next) - tl_lck->next->prev = l_lck; + tl_lck->next->prev = ll; else - l_lcks->locked.last = l_lck; - tl_lck->next = l_lck; + thr->locked.last = ll; + tl_lck->next = ll; } return; } } if (locked) { - l_lck->next = l_lcks->locked.first; - l_lcks->locked.first->prev = l_lck; - l_lcks->locked.first = l_lck; + ll->next = thr->locked.first; + thr->locked.first->prev = ll; + thr->locked.first = ll; } } } -void erts_lc_require_lock_flg(erts_lc_lock_t *lck, Uint16 op_flags, +void erts_lc_require_lock_flg(erts_lc_lock_t *lck, erts_lock_options_t options, char *file, unsigned int line) { - erts_lc_locked_locks_t *l_lcks = make_my_locked_locks(); - erts_lc_locked_lock_t *l_lck = l_lcks->locked.first; - if (!find_lock(&l_lck, lck)) - required_not_locked(l_lcks, lck); - l_lck = new_locked_lock(lck, op_flags, file, line); - if (!l_lcks->required.last) { - ASSERT(!l_lcks->required.first); - l_lck->next = l_lck->prev = NULL; - l_lcks->required.first = l_lcks->required.last = l_lck; + lc_thread_t *thr = make_my_locked_locks(); + lc_locked_lock_t *ll = thr->locked.first; + if (!find_lock(&ll, lck)) + required_not_locked(thr, lck); + ll = new_locked_lock(thr, lck, options, file, line); + if (!thr->required.last) { + ASSERT(!thr->required.first); + ll->next = ll->prev = NULL; + thr->required.first = thr->required.last = ll; } else { - erts_lc_locked_lock_t *l_lck2; - ASSERT(l_lcks->required.first); - for (l_lck2 = l_lcks->required.last; + lc_locked_lock_t *l_lck2; + ASSERT(thr->required.first); + for (l_lck2 = thr->required.last; l_lck2; l_lck2 = l_lck2->prev) { - if (l_lck2->id < lck->id - || (l_lck2->id == lck->id && l_lck2->extra < lck->extra)) + int order = compare_locked_by_id_extra(l_lck2, lck); + if (order < 0) break; - else if (l_lck2->id == lck->id && l_lck2->extra == lck->extra) - require_twice(l_lcks, lck); + if (order == 0) + require_twice(thr, lck); } if (!l_lck2) { - l_lck->next = l_lcks->required.first; - l_lck->prev = NULL; - l_lcks->required.first->prev = l_lck; - l_lcks->required.first = l_lck; + ll->next = thr->required.first; + ll->prev = NULL; + thr->required.first->prev = ll; + thr->required.first = ll; } else { - l_lck->next = l_lck2->next; - if (l_lck->next) { - ASSERT(l_lcks->required.last != l_lck2); - l_lck->next->prev = l_lck; + ll->next = l_lck2->next; + if (ll->next) { + ASSERT(thr->required.last != l_lck2); + ll->next->prev = ll; } else { - ASSERT(l_lcks->required.last == l_lck2); - l_lcks->required.last = l_lck; + ASSERT(thr->required.last == l_lck2); + thr->required.last = ll; } - l_lck->prev = l_lck2; - l_lck2->next = l_lck; + ll->prev = l_lck2; + l_lck2->next = ll; } } } -void erts_lc_unrequire_lock_flg(erts_lc_lock_t *lck, Uint16 op_flags) +void erts_lc_unrequire_lock_flg(erts_lc_lock_t *lck, erts_lock_options_t options) { - erts_lc_locked_locks_t *l_lcks = make_my_locked_locks(); - erts_lc_locked_lock_t *l_lck = l_lcks->locked.first; - if (!find_lock(&l_lck, lck)) - required_not_locked(l_lcks, lck); - l_lck = l_lcks->required.first; - if (!find_lock(&l_lck, lck)) - unrequire_of_not_required_lock(l_lcks, lck); - if (l_lck->prev) { - ASSERT(l_lcks->required.first != l_lck); - l_lck->prev->next = l_lck->next; + lc_thread_t *thr = make_my_locked_locks(); + lc_locked_lock_t *ll = thr->locked.first; + if (!find_lock(&ll, lck)) + required_not_locked(thr, lck); + ll = thr->required.first; + if (!find_lock(&ll, lck)) + unrequire_of_not_required_lock(thr, lck); + if (ll->prev) { + ASSERT(thr->required.first != ll); + ll->prev->next = ll->next; } else { - ASSERT(l_lcks->required.first == l_lck); - l_lcks->required.first = l_lck->next; + ASSERT(thr->required.first == ll); + thr->required.first = ll->next; } - if (l_lck->next) { - ASSERT(l_lcks->required.last != l_lck); - l_lck->next->prev = l_lck->prev; + if (ll->next) { + ASSERT(thr->required.last != ll); + ll->next->prev = ll->prev; } else { - ASSERT(l_lcks->required.last == l_lck); - l_lcks->required.last = l_lck->prev; + ASSERT(thr->required.last == ll); + thr->required.last = ll->prev; } - lc_free((void *) l_lck); + lc_free(thr, ll); } -void erts_lc_lock_flg_x(erts_lc_lock_t *lck, Uint16 op_flags, +void erts_lc_lock_flg_x(erts_lc_lock_t *lck, erts_lock_options_t options, char *file, unsigned int line) { - erts_lc_locked_locks_t *l_lcks; - erts_lc_locked_lock_t *l_lck; + lc_thread_t *thr; + lc_locked_lock_t *new_ll; + int order; if (lck->inited != ERTS_LC_INITITALIZED) uninitialized_lock(); @@ -1149,32 +1185,45 @@ void erts_lc_lock_flg_x(erts_lc_lock_t *lck, Uint16 op_flags, if (lck->id < 0) return; - l_lcks = make_my_locked_locks(); - l_lck = new_locked_lock(lck, op_flags, file, line); + thr = make_my_locked_locks(); + new_ll = new_locked_lock(thr, lck, options, file, line); - if (!l_lcks->locked.last) { - ASSERT(!l_lcks->locked.first); - l_lcks->locked.last = l_lcks->locked.first = l_lck; + if (!thr->locked.last) { + ASSERT(!thr->locked.first); + thr->locked.last = thr->locked.first = new_ll; + ASSERT(0 < lck->id && lck->id < ERTS_LOCK_ORDER_SIZE); + thr->matrix.m[lck->id][0] = 1; + return; } - else if (l_lcks->locked.last->id < lck->id - || (l_lcks->locked.last->id == lck->id - && l_lcks->locked.last->extra < lck->extra)) { - if (LOCK_IS_TYPE_ORDER_VIOLATION(lck->flags, l_lcks->locked.last->flags)) - type_order_violation("locking ", l_lcks, lck); - l_lck->prev = l_lcks->locked.last; - l_lcks->locked.last->next = l_lck; - l_lcks->locked.last = l_lck; + order = compare_locked_by_id_extra(thr->locked.last, lck); + if (order < 0) { + lc_locked_lock_t* ll; + if (LOCK_IS_TYPE_ORDER_VIOLATION(lck->flags, thr->locked.last->flags)) { + type_order_violation("locking ", thr, lck); + } + + ASSERT(0 < lck->id && lck->id < ERTS_LOCK_ORDER_SIZE); + ll = thr->locked.last; + thr->matrix.m[lck->id][ll->id] |= 1; + for (ll = ll->prev; ll; ll = ll->prev) { + ASSERT(0 < ll->id && ll->id < ERTS_LOCK_ORDER_SIZE); + thr->matrix.m[lck->id][ll->id] |= 2; + } + + new_ll->prev = thr->locked.last; + thr->locked.last->next = new_ll; + thr->locked.last = new_ll; } - else if (l_lcks->locked.last->id == lck->id && l_lcks->locked.last->extra == lck->extra) - lock_twice("Locking", l_lcks, lck, op_flags); + else if (order == 0) + lock_twice("Locking", thr, lck, options); else - lock_order_violation(l_lcks, lck); + lock_order_violation(thr, lck); } -void erts_lc_unlock_flg(erts_lc_lock_t *lck, Uint16 op_flags) +void erts_lc_unlock_flg(erts_lc_lock_t *lck, erts_lock_options_t options) { - erts_lc_locked_locks_t *l_lcks; - erts_lc_locked_lock_t *l_lck; + lc_thread_t *thr; + lc_locked_lock_t *ll; if (lck->inited != ERTS_LC_INITITALIZED) uninitialized_lock(); @@ -1182,38 +1231,38 @@ void erts_lc_unlock_flg(erts_lc_lock_t *lck, Uint16 op_flags) if (lck->id < 0) return; - l_lcks = get_my_locked_locks(); + thr = get_my_locked_locks(); - if (l_lcks) { - l_lck = l_lcks->required.first; - if (find_lock(&l_lck, lck)) - unlock_of_required_lock(l_lcks, lck); + if (thr) { + ll = thr->required.first; + if (find_lock(&ll, lck)) + unlock_of_required_lock(thr, lck); } - for (l_lck = l_lcks ? l_lcks->locked.last : NULL; l_lck; l_lck = l_lck->prev) { - if (l_lck->id == lck->id && l_lck->extra == lck->extra) { - if ((l_lck->flags & ERTS_LC_FLG_LO_ALL) != op_flags) - unlock_op_mismatch(l_lcks, lck, op_flags); - if (l_lck->prev) - l_lck->prev->next = l_lck->next; + for (ll = thr ? thr->locked.last : NULL; ll; ll = ll->prev) { + if (ll->id == lck->id && ll->extra == lck->extra) { + if ((ll->taken_options & ERTS_LOCK_OPTIONS_RDWR) != options) + unlock_op_mismatch(thr, lck, options); + if (ll->prev) + ll->prev->next = ll->next; else - l_lcks->locked.first = l_lck->next; - if (l_lck->next) - l_lck->next->prev = l_lck->prev; + thr->locked.first = ll->next; + if (ll->next) + ll->next->prev = ll->prev; else - l_lcks->locked.last = l_lck->prev; - lc_free((void *) l_lck); + thr->locked.last = ll->prev; + lc_free(thr, ll); return; } } - unlock_of_not_locked(l_lcks, lck); + unlock_of_not_locked(thr, lck); } -void erts_lc_might_unlock_flg(erts_lc_lock_t *lck, Uint16 op_flags) +void erts_lc_might_unlock_flg(erts_lc_lock_t *lck, erts_lock_options_t options) { - erts_lc_locked_locks_t *l_lcks; - erts_lc_locked_lock_t *l_lck; + lc_thread_t *thr; + lc_locked_lock_t *ll; if (lck->inited != ERTS_LC_INITITALIZED) uninitialized_lock(); @@ -1221,17 +1270,17 @@ void erts_lc_might_unlock_flg(erts_lc_lock_t *lck, Uint16 op_flags) if (lck->id < 0) return; - l_lcks = get_my_locked_locks(); + thr = get_my_locked_locks(); - if (l_lcks) { - l_lck = l_lcks->required.first; - if (find_lock(&l_lck, lck)) - unlock_of_required_lock(l_lcks, lck); + if (thr) { + ll = thr->required.first; + if (find_lock(&ll, lck)) + unlock_of_required_lock(thr, lck); } - l_lck = l_lcks->locked.first; - if (!find_lock(&l_lck, lck)) - unlock_of_not_locked(l_lcks, lck); + ll = thr->locked.first; + if (!find_lock(&ll, lck)) + unlock_of_not_locked(thr, lck); } int @@ -1274,23 +1323,29 @@ void erts_lc_unrequire_lock(erts_lc_lock_t *lck) } void -erts_lc_init_lock(erts_lc_lock_t *lck, char *name, Uint16 flags) +erts_lc_init_lock(erts_lc_lock_t *lck, char *name, erts_lock_flags_t flags) { lck->id = erts_lc_get_lock_order_id(name); - lck->extra = (UWord) &lck->extra; ASSERT(is_not_immed(lck->extra)); lck->flags = flags; + lck->taken_options = 0; lck->inited = ERTS_LC_INITITALIZED; } void -erts_lc_init_lock_x(erts_lc_lock_t *lck, char *name, Uint16 flags, Eterm extra) +erts_lc_init_lock_x(erts_lc_lock_t *lck, char *name, erts_lock_flags_t flags, Eterm extra) { lck->id = erts_lc_get_lock_order_id(name); lck->extra = extra; - ASSERT(is_immed(lck->extra)); lck->flags = flags; + if (lc_is_term_order(lck->id)) { + lck->flags |= ERTS_LOCK_FLAGS_PROPERTY_TERM_ORDER; + ASSERT(!is_header(lck->extra)); + } + else + ASSERT(is_immed(lck->extra)); + lck->taken_options = 0; lck->inited = ERTS_LC_INITITALIZED; } @@ -1304,31 +1359,13 @@ erts_lc_destroy_lock(erts_lc_lock_t *lck) lck->id = -1; lck->extra = THE_NON_VALUE; lck->flags = 0; + lck->taken_options = 0; } void erts_lc_init(void) { -#ifdef ERTS_LC_STATIC_ALLOC - int i; - static erts_lc_free_block_t fbs[ERTS_LC_FB_CHUNK_SIZE]; - for (i = 0; i < ERTS_LC_FB_CHUNK_SIZE - 1; i++) { -#ifdef DEBUG - memset((void *) &fbs[i], 0xdf, sizeof(erts_lc_free_block_t)); -#endif - fbs[i].next = &fbs[i+1]; - } -#ifdef DEBUG - memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1], - 0xdf, sizeof(erts_lc_free_block_t)); -#endif - fbs[ERTS_LC_FB_CHUNK_SIZE-1].next = NULL; - free_blocks = &fbs[0]; -#else /* #ifdef ERTS_LC_STATIC_ALLOC */ - free_blocks = NULL; -#endif /* #ifdef ERTS_LC_STATIC_ALLOC */ - - if (ethr_spinlock_init(&free_blocks_lock) != 0) + if (ethr_spinlock_init(&lc_threads_lock) != 0) ERTS_INTERNAL_ERROR("spinlock_init failed"); erts_tsd_key_create(&locks_key,"erts_lock_check_key"); @@ -1350,5 +1387,76 @@ erts_lc_pll(void) print_curr_locks(get_my_locked_locks()); } +static void collect_matrix(lc_matrix_t* matrix) +{ + int i, j; + for (i = 1; i < ERTS_LOCK_ORDER_SIZE; i++) { + for (j = 0; j <= i; j++) { + tot_lc_matrix.m[i][j] |= matrix->m[i][j]; + } +#ifdef DEBUG + for ( ; j < ERTS_LOCK_ORDER_SIZE; j++) { + ASSERT(matrix->m[i][j] == 0); + } +#endif + } +} + +Eterm +erts_lc_dump_graph(void) +{ + const char* basename = "lc_graph."; + char filename[40]; + lc_matrix_t* tot = &tot_lc_matrix; + lc_thread_t* thr; + int i, j, name_max = 0; + FILE* ff; + + lc_lock_threads(); + for (thr = lc_threads; thr; thr = thr->next) { + collect_matrix(&thr->matrix); + } + lc_unlock_threads(); + + sys_strcpy(filename, basename); + sys_get_pid(filename + strlen(basename), + sizeof(filename) - strlen(basename)); + ff = fopen(filename, "w"); + if (!ff) + return am_error; + + for (i = 1; i < ERTS_LOCK_ORDER_SIZE; i++) { + int len = strlen(erts_lock_order[i].name); + if (name_max < len) + name_max = len; + } + fputs("%This file was generated by erts_debug:lc_graph()\n\n", ff); + fputs("%{ThisLockName, ThisLockId, LockedDirectlyBeforeThis, LockedIndirectlyBeforeThis}\n", ff); + fprintf(ff, "[{%*s, %2d}", name_max, "\"NO LOCK\"", 0); + for (i = 1; i < ERTS_LOCK_ORDER_SIZE; i++) { + char* delim = ""; + fprintf(ff, ",\n {%*s, %2d, [", name_max, erts_lock_order[i].name, i); + for (j = 0; j < ERTS_LOCK_ORDER_SIZE; j++) { + if (tot->m[i][j] & 1) { + fprintf(ff, "%s%d", delim, j); + delim = ","; + } + } + fprintf(ff, "], ["); + delim = ""; + for (j = 0; j < ERTS_LOCK_ORDER_SIZE; j++) { + if (tot->m[i][j] == 2) { + fprintf(ff, "%s%d", delim, j); + delim = ","; + } + } + fputs("]}", ff); + } + fputs("].", ff); + fclose(ff); + erts_fprintf(stderr, "Created file '%s' in current working directory\n", + filename); + return am_ok; +} #endif /* #ifdef ERTS_ENABLE_LOCK_CHECK */ |