diff options
Diffstat (limited to 'erts/emulator/beam/erl_process_lock.c')
-rw-r--r-- | erts/emulator/beam/erl_process_lock.c | 1431 |
1 files changed, 1431 insertions, 0 deletions
diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c new file mode 100644 index 0000000000..52440fb635 --- /dev/null +++ b/erts/emulator/beam/erl_process_lock.c @@ -0,0 +1,1431 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2007-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% + */ + + +/* + * Description: Impementation of Erlang process locks. + * + * Author: Rickard Green + */ + +/* + * A short explanation of the process lock implementation: + * Each process has a lock bitfield and a number of lock wait + * queues. + * The bit field contains of a number of lock flags (L1, L2, ...) + * and a number of wait flags (W1, W2, ...). Each lock flag has a + * corresponding wait flag. The bit field isn't guarranteed to be + * larger than 32-bits which sets a maximum of 16 different locks + * per process. Currently, only 4 locks per process are used. The + * bit field is operated on by use of atomic operations (custom + * made bitwise atomic operations). When a lock is locked the + * corresponding lock bit is set. When a thread is waiting on a + * lock the wait flag for the lock is set. + * The process table is protected by pix (process index) locks + * which is spinlocks that protects a number of process indices in + * the process table. The pix locks also protects the lock queues + * and modifications of wait flags. + * When acquiring a process lock we first try to set the lock + * flag. If we are able to set the lock flag and the wait flag + * isn't set we are done. If the lock flag was already set we + * have to acquire the pix lock, set the wait flag, and put + * ourselves in the wait queue. + * Process locks will always be acquired in fifo order. + * When releasing a process lock we first unset all lock flags + * whose corresponding wait flag is clear (which will succeed). + * If wait flags were set for the locks being released, we acquire + * the pix lock, and transfer the lock to the first thread + * in the wait queue. + * Note that wait flags may be read without the pix lock, but + * it is important that wait flags only are modified when the pix + * lock is held. + * This implementation assumes that erts_smp_atomic_or_retold() + * provides necessary memorybarriers for a lock operation, and that + * erts_smp_atomic_and_retold() provides necessary memorybarriers + * for an unlock operation. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "erl_process.h" + +const Process erts_proc_lock_busy; + +#ifdef ERTS_SMP + +/*#define ERTS_PROC_LOCK_SPIN_ON_GATE*/ +#define ERTS_PROC_LOCK_SPIN_COUNT_MAX 16000 +#define ERTS_PROC_LOCK_SPIN_COUNT_BASE 1000 + +#ifdef ERTS_PROC_LOCK_DEBUG +#define ERTS_PROC_LOCK_HARD_DEBUG +#endif + +#ifdef ERTS_PROC_LOCK_HARD_DEBUG +static void check_queue(erts_proc_lock_t *lck); +#endif + + +typedef struct erts_proc_lock_waiter_t_ erts_proc_lock_waiter_t; +struct erts_proc_lock_waiter_t_ { + erts_proc_lock_waiter_t *next; + erts_proc_lock_waiter_t *prev; + ErtsProcLocks wait_locks; + erts_smp_gate_t gate; + erts_proc_lock_queues_t *queues; +}; + +struct erts_proc_lock_queues_t_ { + erts_proc_lock_queues_t *next; + erts_proc_lock_waiter_t *queue[ERTS_PROC_LOCK_MAX_BIT+1]; +}; + +struct erts_proc_lock_thr_spec_data_t_ { + erts_proc_lock_queues_t *qs; + erts_proc_lock_waiter_t *wtr; +}; + +static erts_proc_lock_queues_t zeroqs = {0}; + +static erts_smp_spinlock_t wtr_lock; +static erts_proc_lock_waiter_t *waiter_free_list; +static erts_proc_lock_queues_t *queue_free_list; +static erts_tsd_key_t waiter_key; + +#ifdef ERTS_ENABLE_LOCK_CHECK +static struct { + Sint16 proc_lock_main; + Sint16 proc_lock_link; + Sint16 proc_lock_msgq; + Sint16 proc_lock_status; +} lc_id; +#endif + +erts_pix_lock_t erts_pix_locks[ERTS_NO_OF_PIX_LOCKS]; + +static int proc_lock_spin_count; +static int proc_lock_trans_spin_cost; + +static void cleanup_waiter(void); + +void +erts_init_proc_lock(void) +{ + int i; + int cpus; + erts_smp_spinlock_init(&wtr_lock, "proc_lck_wtr_alloc"); + for (i = 0; i < ERTS_NO_OF_PIX_LOCKS; i++) { +#if ERTS_PROC_LOCK_MUTEX_IMPL +#ifdef ERTS_ENABLE_LOCK_COUNT + erts_smp_mtx_init_x(&erts_pix_locks[i].u.mtx, "pix_lock", make_small(i)); +#else + erts_smp_mtx_init(&erts_pix_locks[i].u.mtx, "pix_lock"); +#endif +#else +#ifdef ERTS_ENABLE_LOCK_COUNT + erts_smp_spinlock_init_x(&erts_pix_locks[i].u.spnlck, "pix_lock", make_small(i)); +#else + erts_smp_spinlock_init(&erts_pix_locks[i].u.spnlck, "pix_lock"); +#endif +#endif + } + waiter_free_list = NULL; + queue_free_list = NULL; + erts_tsd_key_create(&waiter_key); + erts_thr_install_exit_handler(cleanup_waiter); +#ifdef ERTS_ENABLE_LOCK_CHECK + lc_id.proc_lock_main = erts_lc_get_lock_order_id("proc_main"); + lc_id.proc_lock_link = erts_lc_get_lock_order_id("proc_link"); + lc_id.proc_lock_msgq = erts_lc_get_lock_order_id("proc_msgq"); + lc_id.proc_lock_status = erts_lc_get_lock_order_id("proc_status"); +#endif + cpus = erts_get_cpu_configured(erts_cpuinfo); + if (cpus > 1) + proc_lock_spin_count = (ERTS_PROC_LOCK_SPIN_COUNT_BASE + * ((int) erts_no_schedulers)); + else if (cpus == 1) + proc_lock_spin_count = 0; + else /* No of cpus unknown. Assume multi proc, but be conservative. */ + proc_lock_spin_count = ERTS_PROC_LOCK_SPIN_COUNT_BASE; + if (proc_lock_spin_count > ERTS_PROC_LOCK_SPIN_COUNT_MAX) + proc_lock_spin_count = ERTS_PROC_LOCK_SPIN_COUNT_MAX; + proc_lock_trans_spin_cost = proc_lock_spin_count/20; +} + +static ERTS_INLINE erts_proc_lock_waiter_t * +alloc_wtr(void) +{ + erts_proc_lock_waiter_t *wtr; + erts_smp_spin_lock(&wtr_lock); + wtr = waiter_free_list; + if (wtr) { + waiter_free_list = wtr->next; + ERTS_LC_ASSERT(queue_free_list); + wtr->queues = queue_free_list; + queue_free_list = wtr->queues->next; + erts_smp_spin_unlock(&wtr_lock); + } + else { + erts_smp_spin_unlock(&wtr_lock); + wtr = erts_alloc(ERTS_ALC_T_PROC_LCK_WTR, + sizeof(erts_proc_lock_waiter_t)); + erts_smp_gate_init(&wtr->gate); + wtr->wait_locks = (ErtsProcLocks) 0; + wtr->queues = erts_alloc(ERTS_ALC_T_PROC_LCK_QS, + sizeof(erts_proc_lock_queues_t)); + sys_memcpy((void *) wtr->queues, + (void *) &zeroqs, + sizeof(erts_proc_lock_queues_t)); + } + return wtr; +} + +#ifdef ERTS_ENABLE_LOCK_CHECK +static void +check_unused_waiter(erts_proc_lock_waiter_t *wtr) +{ + int i; + ERTS_LC_ASSERT(wtr->wait_locks == 0); + for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++) + ERTS_LC_ASSERT(!wtr->queues->queue[i]); +} +#define CHECK_UNUSED_WAITER(W) check_unused_waiter((W)) +#else +#define CHECK_UNUSED_WAITER(W) +#endif + + +static ERTS_INLINE void +free_wtr(erts_proc_lock_waiter_t *wtr) +{ + CHECK_UNUSED_WAITER(wtr); + erts_smp_spin_lock(&wtr_lock); + wtr->next = waiter_free_list; + waiter_free_list = wtr; + wtr->queues->next = queue_free_list; + queue_free_list = wtr->queues; + erts_smp_spin_unlock(&wtr_lock); +} + +void +erts_proc_lock_prepare_proc_lock_waiter(void) +{ + erts_tsd_set(waiter_key, (void *) alloc_wtr()); +} + + +static void +cleanup_waiter(void) +{ + erts_proc_lock_waiter_t *wtr = erts_tsd_get(waiter_key); + if (wtr) + free_wtr(wtr); +} + + +/* + * Waiters are queued in a circular double linked list; + * where qs->queue[lock_ix] is the first waiter in queue, and + * qs->queue[lock_ix]->prev is the last waiter in queue. + */ + +static ERTS_INLINE void +enqueue_waiter(erts_proc_lock_queues_t *qs, + int ix, + erts_proc_lock_waiter_t *wtr) +{ + if (!qs->queue[ix]) { + qs->queue[ix] = wtr; + wtr->next = wtr; + wtr->prev = wtr; + } + else { + ERTS_LC_ASSERT(qs->queue[ix]->next && qs->queue[ix]->prev); + wtr->next = qs->queue[ix]; + wtr->prev = qs->queue[ix]->prev; + wtr->prev->next = wtr; + qs->queue[ix]->prev = wtr; + } +} + +static erts_proc_lock_waiter_t * +dequeue_waiter(erts_proc_lock_queues_t *qs, int ix) +{ + erts_proc_lock_waiter_t *wtr = qs->queue[ix]; + ERTS_LC_ASSERT(qs->queue[ix]); + if (wtr->next == wtr) { + ERTS_LC_ASSERT(qs->queue[ix]->prev == wtr); + qs->queue[ix] = NULL; + } + else { + ERTS_LC_ASSERT(wtr->next != wtr); + ERTS_LC_ASSERT(wtr->prev != wtr); + wtr->next->prev = wtr->prev; + wtr->prev->next = wtr->next; + qs->queue[ix] = wtr->next; + } + return wtr; +} + +/* + * Tries to aquire as many locks as possible in lock order, + * and sets the wait flag on the first lock not possible to + * aquire. + * + * Note: We need the pix lock during this operation. Wait + * flags are only allowed to be manipulated under pix + * lock. + */ +static ERTS_INLINE void +try_aquire(erts_proc_lock_t *lck, erts_proc_lock_waiter_t *wtr) +{ + ErtsProcLocks got_locks = (ErtsProcLocks) 0; + ErtsProcLocks locks = wtr->wait_locks; + int lock_no; + + ERTS_LC_ASSERT(lck->queues); + ERTS_LC_ASSERT(got_locks != locks); + + for (lock_no = 0; lock_no <= ERTS_PROC_LOCK_MAX_BIT; lock_no++) { + ErtsProcLocks lock = ((ErtsProcLocks) 1) << lock_no; + if (locks & lock) { + ErtsProcLocks wflg, old_lflgs; + if (lck->queues->queue[lock_no]) { + /* Others already waiting */ + enqueue: + ERTS_LC_ASSERT(ERTS_PROC_LOCK_FLGS_READ_(lck) + & (lock << ERTS_PROC_LOCK_WAITER_SHIFT)); + enqueue_waiter(lck->queues, lock_no, wtr); + break; + } + wflg = lock << ERTS_PROC_LOCK_WAITER_SHIFT; + old_lflgs = ERTS_PROC_LOCK_FLGS_BOR_(lck, wflg | lock); + if (old_lflgs & lock) { + /* Didn't get the lock */ + goto enqueue; + } + else { + /* Got the lock */ + got_locks |= lock; + ERTS_LC_ASSERT(!(old_lflgs & wflg)); + /* No one else can be waiting for the lock; remove wait flag */ + (void) ERTS_PROC_LOCK_FLGS_BAND_(lck, ~wflg); + if (got_locks == locks) + break; + } + } + } + + wtr->wait_locks &= ~got_locks; +} + +/* + * Transfer 'trnsfr_lcks' held by this executing thread to other + * threads waiting for the locks. When a lock has been transferred + * we also have to try to aquire as many lock as possible for the + * other thread. + */ +static int +transfer_locks(Process *p, + ErtsProcLocks trnsfr_lcks, + erts_pix_lock_t *pix_lock, + int unlock) +{ + int transferred = 0; + erts_proc_lock_waiter_t *wake = NULL; + erts_proc_lock_waiter_t *wtr; + ErtsProcLocks unset_waiter = 0; + ErtsProcLocks tlocks = trnsfr_lcks; + int lock_no; + + ERTS_LC_ASSERT(erts_lc_pix_lock_is_locked(pix_lock)); + +#ifdef ERTS_PROC_LOCK_HARD_DEBUG + check_queue(&p->lock); +#endif + + for (lock_no = 0; tlocks && lock_no <= ERTS_PROC_LOCK_MAX_BIT; lock_no++) { + ErtsProcLocks lock = ((ErtsProcLocks) 1) << lock_no; + if (tlocks & lock) { + erts_proc_lock_queues_t *qs = p->lock.queues; + /* Transfer lock */ +#ifdef ERTS_ENABLE_LOCK_CHECK + tlocks &= ~lock; +#endif + ERTS_LC_ASSERT(ERTS_PROC_LOCK_FLGS_READ_(&p->lock) + & (lock << ERTS_PROC_LOCK_WAITER_SHIFT)); + transferred++; + wtr = dequeue_waiter(qs, lock_no); + ERTS_LC_ASSERT(wtr); + if (!qs->queue[lock_no]) + unset_waiter |= lock; + ERTS_LC_ASSERT(wtr->wait_locks & lock); + wtr->wait_locks &= ~lock; + if (wtr->wait_locks) + try_aquire(&p->lock, wtr); + if (!wtr->wait_locks) { + /* + * The other thread got all locks it needs; + * need to wake it up. + */ + wtr->next = wake; + wake = wtr; + } + } + + } + + if (unset_waiter) { + unset_waiter <<= ERTS_PROC_LOCK_WAITER_SHIFT; + (void) ERTS_PROC_LOCK_FLGS_BAND_(&p->lock, ~unset_waiter); + } + +#ifdef ERTS_PROC_LOCK_HARD_DEBUG + check_queue(&p->lock); +#endif + + ERTS_LC_ASSERT(tlocks == 0); /* We should have transferred all of them */ + + if (!wake) { + if (unlock) + erts_pix_unlock(pix_lock); + } + else { + erts_pix_unlock(pix_lock); + + do { + erts_proc_lock_waiter_t *tmp = wake; + wake = wake->next; + erts_smp_gate_let_through(&tmp->gate, 1); + } while (wake); + + if (!unlock) + erts_pix_lock(pix_lock); + } + return transferred; +} + +/* + * Determine which locks in 'need_locks' are not currently locked in + * 'in_use', but do not return any locks "above" some lock we need, + * so we do not attempt to grab locks out of order. + * + * For example, if we want to lock 10111, and 00100 was already locked, this + * would return 00011, indicating we should not try for 10000 yet because + * that would be a lock-ordering violation. + */ +static ERTS_INLINE ErtsProcLocks +in_order_locks(ErtsProcLocks in_use, ErtsProcLocks need_locks) +{ + /* All locks we want that are already locked by someone else. */ + ErtsProcLocks busy = in_use & need_locks; + + /* Just the lowest numbered lock we want that's in use; 0 if none. */ + ErtsProcLocks lowest_busy = busy & -busy; + + /* All locks below the lowest one we want that's in use already. */ + return need_locks & (lowest_busy - 1); +} + +/* + * Try to grab locks one at a time in lock order and wait on the lowest + * lock we fail to grab, if any. + * + * If successful, this returns 0 and all locks in 'need_locks' are held. + * + * On entry, the pix lock is held iff !ERTS_PROC_LOCK_ATOMIC_IMPL. + * On exit it is not held. + */ +static void +wait_for_locks(Process *p, + erts_pix_lock_t *pixlck, + ErtsProcLocks locks, + ErtsProcLocks need_locks, + ErtsProcLocks olflgs) +{ + erts_pix_lock_t *pix_lock = pixlck ? pixlck : ERTS_PID2PIXLOCK(p->id); + int tsd; + erts_proc_lock_waiter_t *wtr; + + /* Acquire a waiter object on which this thread can wait. */ + wtr = erts_tsd_get(waiter_key); + if (wtr) + tsd = 1; + else { +#if ERTS_PROC_LOCK_SPINLOCK_IMPL && !ERTS_PROC_LOCK_ATOMIC_IMPL + erts_pix_unlock(pix_lock); +#endif + wtr = alloc_wtr(); + tsd = 0; +#if ERTS_PROC_LOCK_SPINLOCK_IMPL && !ERTS_PROC_LOCK_ATOMIC_IMPL + erts_pix_lock(pix_lock); +#endif + } + + /* Record which locks this waiter needs. */ + wtr->wait_locks = need_locks; + +#if ERTS_PROC_LOCK_ATOMIC_IMPL + erts_pix_lock(pix_lock); +#endif + + ERTS_LC_ASSERT(erts_lc_pix_lock_is_locked(pix_lock)); + + /* Provide the process with waiter queues, if it doesn't have one. */ + if (!p->lock.queues) { + wtr->queues->next = NULL; + p->lock.queues = wtr->queues; + } + else { + wtr->queues->next = p->lock.queues->next; + p->lock.queues->next = wtr->queues; + } + +#ifdef ERTS_PROC_LOCK_HARD_DEBUG + check_queue(&p->lock); +#endif + + /* Try to aquire locks one at a time in lock order and set wait flag */ + try_aquire(&p->lock, wtr); + +#ifdef ERTS_PROC_LOCK_HARD_DEBUG + check_queue(&p->lock); +#endif + + if (wtr->wait_locks) { /* We didn't get them all; need to wait... */ + /* Got to wait for locks... */ + erts_pix_unlock(pix_lock); + + /* + * Wait for needed locks. When we return all needed locks have + * have been acquired by other threads and transfered to us. + */ +#ifdef ERTS_PROC_LOCK_SPIN_ON_GATE + erts_smp_gate_swait(&wtr->gate, proc_lock_spin_count); +#else + erts_smp_gate_wait(&wtr->gate); +#endif + + erts_pix_lock(pix_lock); + } + + /* Recover some queues to store in the waiter. */ + ERTS_LC_ASSERT(p->lock.queues); + if (p->lock.queues->next) { + wtr->queues = p->lock.queues->next; + p->lock.queues->next = wtr->queues->next; + } + else { + wtr->queues = p->lock.queues; + p->lock.queues = NULL; + } + + erts_pix_unlock(pix_lock); + + ERTS_LC_ASSERT(locks == (ERTS_PROC_LOCK_FLGS_READ_(&p->lock) & locks)); + + if (tsd) + CHECK_UNUSED_WAITER(wtr); + else + free_wtr(wtr); +} + +/* + * erts_proc_lock_failed() is called when erts_smp_proc_lock() + * wasn't able to lock all locks. We may need to transfer locks + * to waiters and wait for our turn on locks. + * + * Iff !ERTS_PROC_LOCK_ATOMIC_IMPL, the pix lock is locked on entry. + * + * This always returns with the pix lock unlocked. + */ +void +erts_proc_lock_failed(Process *p, + erts_pix_lock_t *pixlck, + ErtsProcLocks locks, + ErtsProcLocks old_lflgs) +{ +#ifdef ERTS_PROC_LOCK_SPIN_ON_GATE + int spin_count = 0; +#else + int spin_count = proc_lock_spin_count; +#endif + + ErtsProcLocks need_locks = locks; + ErtsProcLocks olflgs = old_lflgs; + + while (need_locks != 0) + { + ErtsProcLocks can_grab = in_order_locks(olflgs, need_locks); + + if (can_grab == 0) + { + /* Someone already has the lowest-numbered lock we want. */ + + if (spin_count-- <= 0) + { + /* Too many retries, give up and sleep for the lock. */ + wait_for_locks(p, pixlck, locks, need_locks, olflgs); + return; + } + + olflgs = ERTS_PROC_LOCK_FLGS_READ_(&p->lock); + } + else + { + /* Try to grab all of the grabbable locks at once with cmpxchg. */ + ErtsProcLocks grabbed = olflgs | can_grab; + ErtsProcLocks nflgs = + ERTS_PROC_LOCK_FLGS_CMPXCHG_(&p->lock, grabbed, olflgs); + + if (nflgs == olflgs) + { + /* Success! We grabbed the 'can_grab' locks. */ + olflgs = grabbed; + need_locks &= ~can_grab; + +#ifndef ERTS_PROC_LOCK_SPIN_ON_GATE + /* Since we made progress, reset the spin count. */ + spin_count = proc_lock_spin_count; +#endif + } + else + { + /* Compare-and-exchange failed, try again. */ + olflgs = nflgs; + } + } + } + + /* Now we have all of the locks we wanted. */ + +#if !ERTS_PROC_LOCK_ATOMIC_IMPL + erts_pix_unlock(pixlck); +#endif +} + +/* + * erts_proc_unlock_failed() is called when erts_smp_proc_unlock() + * wasn't able to unlock all locks. We may need to transfer locks + * to waiters. + */ +void +erts_proc_unlock_failed(Process *p, + erts_pix_lock_t *pixlck, + ErtsProcLocks wait_locks) +{ + erts_pix_lock_t *pix_lock = pixlck ? pixlck : ERTS_PID2PIXLOCK(p->id); + +#if ERTS_PROC_LOCK_ATOMIC_IMPL + erts_pix_lock(pix_lock); +#endif + + transfer_locks(p, wait_locks, pix_lock, 1); /* unlocks pix_lock */ +} + +/* + * proc_safelock() locks process locks on two processes. In order + * to avoid a deadlock, proc_safelock() unlocks those locks that + * needs to be unlocked, and then acquires locks in lock order + * (including the previously unlocked ones). + */ + +static void +proc_safelock(Process *a_proc, + erts_pix_lock_t *a_pix_lck, + ErtsProcLocks a_have_locks, + ErtsProcLocks a_need_locks, + Process *b_proc, + erts_pix_lock_t *b_pix_lck, + ErtsProcLocks b_have_locks, + ErtsProcLocks b_need_locks) +{ + Process *p1, *p2; + Eterm pid1, pid2; + erts_pix_lock_t *pix_lck1, *pix_lck2; + ErtsProcLocks need_locks1, have_locks1, need_locks2, have_locks2; + ErtsProcLocks unlock_mask; + int lock_no, refc1 = 0, refc2 = 0; + + ERTS_LC_ASSERT(b_proc); + + + /* Determine inter process lock order... + * Locks with the same lock order should be locked on p1 before p2. + */ + if (a_proc) { + if (a_proc->id < b_proc->id) { + p1 = a_proc; + pid1 = a_proc->id; + pix_lck1 = a_pix_lck; + need_locks1 = a_need_locks; + have_locks1 = a_have_locks; + p2 = b_proc; + pid2 = b_proc->id; + pix_lck2 = b_pix_lck; + need_locks2 = b_need_locks; + have_locks2 = b_have_locks; + } + else if (a_proc->id > b_proc->id) { + p1 = b_proc; + pid1 = b_proc->id; + pix_lck1 = b_pix_lck; + need_locks1 = b_need_locks; + have_locks1 = b_have_locks; + p2 = a_proc; + pid2 = a_proc->id; + pix_lck2 = a_pix_lck; + need_locks2 = a_need_locks; + have_locks2 = a_have_locks; + } + else { + ERTS_LC_ASSERT(a_proc == b_proc); + ERTS_LC_ASSERT(a_proc->id == b_proc->id); + p1 = a_proc; + pid1 = a_proc->id; + pix_lck1 = a_pix_lck; + need_locks1 = a_need_locks | b_need_locks; + have_locks1 = a_have_locks | b_have_locks; + p2 = NULL; + pid2 = 0; + pix_lck2 = NULL; + need_locks2 = 0; + have_locks2 = 0; + } + } + else { + p1 = b_proc; + pid1 = b_proc->id; + pix_lck1 = b_pix_lck; + need_locks1 = b_need_locks; + have_locks1 = b_have_locks; + p2 = NULL; + pid2 = 0; + pix_lck2 = NULL; + need_locks2 = 0; + have_locks2 = 0; +#ifdef ERTS_ENABLE_LOCK_CHECK + a_need_locks = 0; + a_have_locks = 0; +#endif + } + +#ifdef ERTS_ENABLE_LOCK_CHECK + if (p1) + erts_proc_lc_chk_proc_locks(p1, have_locks1); + if (p2) + erts_proc_lc_chk_proc_locks(p2, have_locks2); + + if ((need_locks1 & have_locks1) != have_locks1) + erts_lc_fail("Thread tries to release process lock(s) " + "on %T via erts_proc_safelock().", pid1); + if ((need_locks2 & have_locks2) != have_locks2) + erts_lc_fail("Thread tries to release process lock(s) " + "on %T via erts_proc_safelock().", + pid2); +#endif + + + need_locks1 &= ~have_locks1; + need_locks2 &= ~have_locks2; + + /* Figure out the range of locks that needs to be unlocked... */ + unlock_mask = ERTS_PROC_LOCKS_ALL; + for (lock_no = 0; + lock_no <= ERTS_PROC_LOCK_MAX_BIT; + lock_no++) { + ErtsProcLocks lock = (1 << lock_no); + if (lock & need_locks1) + break; + unlock_mask &= ~lock; + if (lock & need_locks2) + break; + } + + /* ... and unlock locks in that range... */ + if (have_locks1 || have_locks2) { + ErtsProcLocks unlock_locks; + unlock_locks = unlock_mask & have_locks1; + if (unlock_locks) { + have_locks1 &= ~unlock_locks; + need_locks1 |= unlock_locks; + if (!have_locks1) { + refc1 = 1; + erts_smp_proc_inc_refc(p1); + } + erts_smp_proc_unlock__(p1, pix_lck1, unlock_locks); + } + unlock_locks = unlock_mask & have_locks2; + if (unlock_locks) { + have_locks2 &= ~unlock_locks; + need_locks2 |= unlock_locks; + if (!have_locks2) { + refc2 = 1; + erts_smp_proc_inc_refc(p2); + } + erts_smp_proc_unlock__(p2, pix_lck2, unlock_locks); + } + } + + /* + * lock_no equals the number of the first lock to lock on + * either p1 *or* p2. + */ + + +#ifdef ERTS_ENABLE_LOCK_CHECK + if (p1) + erts_proc_lc_chk_proc_locks(p1, have_locks1); + if (p2) + erts_proc_lc_chk_proc_locks(p2, have_locks2); +#endif + + /* Lock locks in lock order... */ + while (lock_no <= ERTS_PROC_LOCK_MAX_BIT) { + ErtsProcLocks locks; + ErtsProcLocks lock = (1 << lock_no); + ErtsProcLocks lock_mask = 0; + if (need_locks1 & lock) { + do { + lock = (1 << lock_no++); + lock_mask |= lock; + } while (lock_no <= ERTS_PROC_LOCK_MAX_BIT + && !(need_locks2 & lock)); + if (need_locks2 & lock) + lock_no--; + locks = need_locks1 & lock_mask; + erts_smp_proc_lock__(p1, pix_lck1, locks); + have_locks1 |= locks; + need_locks1 &= ~locks; + } + else if (need_locks2 & lock) { + while (lock_no <= ERTS_PROC_LOCK_MAX_BIT + && !(need_locks1 & lock)) { + lock_mask |= lock; + lock = (1 << ++lock_no); + } + locks = need_locks2 & lock_mask; + erts_smp_proc_lock__(p2, pix_lck2, locks); + have_locks2 |= locks; + need_locks2 &= ~locks; + } + else + lock_no++; + } + +#ifdef ERTS_ENABLE_LOCK_CHECK + if (p1) + erts_proc_lc_chk_proc_locks(p1, have_locks1); + if (p2) + erts_proc_lc_chk_proc_locks(p2, have_locks2); + + if (p1 && p2) { + if (p1 == a_proc) { + ERTS_LC_ASSERT(a_need_locks == have_locks1); + ERTS_LC_ASSERT(b_need_locks == have_locks2); + } + else { + ERTS_LC_ASSERT(a_need_locks == have_locks2); + ERTS_LC_ASSERT(b_need_locks == have_locks1); + } + } + else { + ERTS_LC_ASSERT(p1); + if (a_proc) { + ERTS_LC_ASSERT(have_locks1 == (a_need_locks | b_need_locks)); + } + else { + ERTS_LC_ASSERT(have_locks1 == b_need_locks); + } + } +#endif + + if (refc1) + erts_smp_proc_dec_refc(p1); + if (refc2) + erts_smp_proc_dec_refc(p2); +} + +void +erts_proc_safelock(Process *a_proc, + ErtsProcLocks a_have_locks, + ErtsProcLocks a_need_locks, + Process *b_proc, + ErtsProcLocks b_have_locks, + ErtsProcLocks b_need_locks) +{ + proc_safelock(a_proc, + a_proc ? ERTS_PID2PIXLOCK(a_proc->id) : NULL, + a_have_locks, + a_need_locks, + b_proc, + b_proc ? ERTS_PID2PIXLOCK(b_proc->id) : NULL, + b_have_locks, + b_need_locks); +} + +/* + * erts_pid2proc_safelock() is called from erts_pid2proc_opt() when + * it wasn't possible to trylock all locks needed. + * c_p - current process + * c_p_have_locks - locks held on c_p + * pid - process id of process we are looking up + * proc - process struct of process we are looking + * up (both in and out argument) + * need_locks - all locks we need (including have_locks) + * pix_lock - pix lock for process we are looking up + * flags - option flags + */ +void +erts_pid2proc_safelock(Process *c_p, + ErtsProcLocks c_p_have_locks, + Process **proc, + ErtsProcLocks need_locks, + erts_pix_lock_t *pix_lock, + int flags) +{ + Process *p = *proc; + ERTS_LC_ASSERT(p->lock.refc > 0); + ERTS_LC_ASSERT(process_tab[internal_pid_index(p->id)] == p); + p->lock.refc++; + erts_pix_unlock(pix_lock); + + proc_safelock(c_p, + c_p ? ERTS_PID2PIXLOCK(c_p->id) : NULL, + c_p_have_locks, + c_p_have_locks, + p, + pix_lock, + 0, + need_locks); + + erts_pix_lock(pix_lock); + + if (!p->is_exiting + || ((flags & ERTS_P2P_FLG_ALLOW_OTHER_X) + && process_tab[internal_pid_index(p->id)] == p)) { + ERTS_LC_ASSERT(p->lock.refc > 1); + p->lock.refc--; + } + else { + /* No proc. Note, we need to keep refc until after process unlock */ + erts_pix_unlock(pix_lock); + erts_smp_proc_unlock__(p, pix_lock, need_locks); + *proc = NULL; + erts_pix_lock(pix_lock); + ERTS_LC_ASSERT(p->lock.refc > 0); + if (--p->lock.refc == 0) { + erts_pix_unlock(pix_lock); + erts_free_proc(p); + erts_pix_lock(pix_lock); + } + } +} + +void +erts_proc_lock_init(Process *p) +{ + /* We always start with all locks locked */ +#if ERTS_PROC_LOCK_ATOMIC_IMPL + erts_smp_atomic_init(&p->lock.flags, (long) ERTS_PROC_LOCKS_ALL); +#else + p->lock.flags = ERTS_PROC_LOCKS_ALL; +#endif + p->lock.queues = NULL; + p->lock.refc = 1; +#ifdef ERTS_ENABLE_LOCK_COUNT + erts_lcnt_proc_lock_init(p); + erts_lcnt_proc_lock(&(p->lock), ERTS_PROC_LOCKS_ALL); + erts_lcnt_proc_lock_post_x(&(p->lock), ERTS_PROC_LOCKS_ALL, __FILE__, __LINE__); +#endif + +#ifdef ERTS_ENABLE_LOCK_CHECK + erts_proc_lc_trylock(p, ERTS_PROC_LOCKS_ALL, 1); +#endif +#ifdef ERTS_PROC_LOCK_DEBUG + { + int i; + for (i = 0; i <= ERTS_PROC_LOCK_MAX_BIT; i++) + erts_smp_atomic_init(&p->lock.locked[i], (long) 1); + } +#endif +} + +/* --- Process lock counting ----------------------------------------------- */ + +#ifdef ERTS_ENABLE_LOCK_COUNT +void erts_lcnt_proc_lock_init(Process *p) { + + if (p->id != ERTS_INVALID_PID) { + erts_lcnt_init_lock_x(&(p->lock.lcnt_main), "proc_main", ERTS_LCNT_LT_PROCLOCK, p->id); + erts_lcnt_init_lock_x(&(p->lock.lcnt_msgq), "proc_msgq", ERTS_LCNT_LT_PROCLOCK, p->id); + erts_lcnt_init_lock_x(&(p->lock.lcnt_link), "proc_link", ERTS_LCNT_LT_PROCLOCK, p->id); + erts_lcnt_init_lock_x(&(p->lock.lcnt_status), "proc_status", ERTS_LCNT_LT_PROCLOCK, p->id); + } else { + erts_lcnt_init_lock(&(p->lock.lcnt_main), "proc_main", ERTS_LCNT_LT_PROCLOCK); + erts_lcnt_init_lock(&(p->lock.lcnt_msgq), "proc_msgq", ERTS_LCNT_LT_PROCLOCK); + erts_lcnt_init_lock(&(p->lock.lcnt_link), "proc_link", ERTS_LCNT_LT_PROCLOCK); + erts_lcnt_init_lock(&(p->lock.lcnt_status), "proc_status", ERTS_LCNT_LT_PROCLOCK); + } +} + + +void erts_lcnt_proc_lock_destroy(Process *p) { + erts_lcnt_destroy_lock(&(p->lock.lcnt_main)); + erts_lcnt_destroy_lock(&(p->lock.lcnt_msgq)); + erts_lcnt_destroy_lock(&(p->lock.lcnt_link)); + erts_lcnt_destroy_lock(&(p->lock.lcnt_status)); +} + +void erts_lcnt_proc_lock(erts_proc_lock_t *lock, ErtsProcLocks locks) { + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) { + if (locks & ERTS_PROC_LOCK_MAIN) { + erts_lcnt_lock(&(lock->lcnt_main)); + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + erts_lcnt_lock(&(lock->lcnt_msgq)); + } + if (locks & ERTS_PROC_LOCK_LINK) { + erts_lcnt_lock(&(lock->lcnt_link)); + } + if (locks & ERTS_PROC_LOCK_STATUS) { + erts_lcnt_lock(&(lock->lcnt_status)); + } + } +} + +void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks, char *file, unsigned int line) { + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) { + if (locks & ERTS_PROC_LOCK_MAIN) { + erts_lcnt_lock_post_x(&(lock->lcnt_main), file, line); + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + erts_lcnt_lock_post_x(&(lock->lcnt_msgq), file, line); + } + if (locks & ERTS_PROC_LOCK_LINK) { + erts_lcnt_lock_post_x(&(lock->lcnt_link), file, line); + } + if (locks & ERTS_PROC_LOCK_STATUS) { + erts_lcnt_lock_post_x(&(lock->lcnt_status), file, line); + } + } +} + +void erts_lcnt_proc_lock_unaquire(erts_proc_lock_t *lock, ErtsProcLocks locks) { + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) { + if (locks & ERTS_PROC_LOCK_MAIN) { + erts_lcnt_lock_unaquire(&(lock->lcnt_main)); + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + erts_lcnt_lock_unaquire(&(lock->lcnt_msgq)); + } + if (locks & ERTS_PROC_LOCK_LINK) { + erts_lcnt_lock_unaquire(&(lock->lcnt_link)); + } + if (locks & ERTS_PROC_LOCK_STATUS) { + erts_lcnt_lock_unaquire(&(lock->lcnt_status)); + } + } +} + +void erts_lcnt_proc_unlock(erts_proc_lock_t *lock, ErtsProcLocks locks) { + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) { + if (locks & ERTS_PROC_LOCK_MAIN) { + erts_lcnt_unlock(&(lock->lcnt_main)); + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + erts_lcnt_unlock(&(lock->lcnt_msgq)); + } + if (locks & ERTS_PROC_LOCK_LINK) { + erts_lcnt_unlock(&(lock->lcnt_link)); + } + if (locks & ERTS_PROC_LOCK_STATUS) { + erts_lcnt_unlock(&(lock->lcnt_status)); + } + } +} +void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res) { + if (erts_lcnt_rt_options & ERTS_LCNT_OPT_PROCLOCK) { + if (locks & ERTS_PROC_LOCK_MAIN) { + erts_lcnt_trylock(&(lock->lcnt_main), res); + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + erts_lcnt_trylock(&(lock->lcnt_msgq), res); + } + if (locks & ERTS_PROC_LOCK_LINK) { + erts_lcnt_trylock(&(lock->lcnt_link), res); + } + if (locks & ERTS_PROC_LOCK_STATUS) { + erts_lcnt_trylock(&(lock->lcnt_status), res); + } + } +} + +#endif /* ifdef ERTS_ENABLE_LOCK_COUNT */ + + +/* --- Process lock checking ----------------------------------------------- */ + +#ifdef ERTS_ENABLE_LOCK_CHECK + +void +erts_proc_lc_lock(Process *p, ErtsProcLocks locks) +{ + erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1, + p->id, + ERTS_LC_FLG_LT_PROCLOCK); + if (locks & ERTS_PROC_LOCK_MAIN) { + lck.id = lc_id.proc_lock_main; + erts_lc_lock(&lck); + } + if (locks & ERTS_PROC_LOCK_LINK) { + lck.id = lc_id.proc_lock_link; + erts_lc_lock(&lck); + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + lck.id = lc_id.proc_lock_msgq; + erts_lc_lock(&lck); + } + if (locks & ERTS_PROC_LOCK_STATUS) { + lck.id = lc_id.proc_lock_status; + erts_lc_lock(&lck); + } +} + +void +erts_proc_lc_trylock(Process *p, ErtsProcLocks locks, int locked) +{ + erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1, + p->id, + ERTS_LC_FLG_LT_PROCLOCK); + if (locks & ERTS_PROC_LOCK_MAIN) { + lck.id = lc_id.proc_lock_main; + erts_lc_trylock(locked, &lck); + } + if (locks & ERTS_PROC_LOCK_LINK) { + lck.id = lc_id.proc_lock_link; + erts_lc_trylock(locked, &lck); + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + lck.id = lc_id.proc_lock_msgq; + erts_lc_trylock(locked, &lck); + } + if (locks & ERTS_PROC_LOCK_STATUS) { + lck.id = lc_id.proc_lock_status; + erts_lc_trylock(locked, &lck); + } +} + +void +erts_proc_lc_unlock(Process *p, ErtsProcLocks locks) +{ + erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1, + p->id, + ERTS_LC_FLG_LT_PROCLOCK); + if (locks & ERTS_PROC_LOCK_STATUS) { + lck.id = lc_id.proc_lock_status; + erts_lc_unlock(&lck); + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + lck.id = lc_id.proc_lock_msgq; + erts_lc_unlock(&lck); + } + if (locks & ERTS_PROC_LOCK_LINK) { + lck.id = lc_id.proc_lock_link; + erts_lc_unlock(&lck); + } + if (locks & ERTS_PROC_LOCK_MAIN) { + lck.id = lc_id.proc_lock_main; + erts_lc_unlock(&lck); + } +} + +void +erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks) +{ + erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1, + p->id, + ERTS_LC_FLG_LT_PROCLOCK); + if (locks & ERTS_PROC_LOCK_STATUS) { + lck.id = lc_id.proc_lock_status; + erts_lc_might_unlock(&lck); + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + lck.id = lc_id.proc_lock_msgq; + erts_lc_might_unlock(&lck); + } + if (locks & ERTS_PROC_LOCK_LINK) { + lck.id = lc_id.proc_lock_link; + erts_lc_might_unlock(&lck); + } + if (locks & ERTS_PROC_LOCK_MAIN) { + lck.id = lc_id.proc_lock_main; + erts_lc_might_unlock(&lck); + } +} + +void +erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks) +{ + erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1, + p->id, + ERTS_LC_FLG_LT_PROCLOCK); + if (locks & ERTS_PROC_LOCK_MAIN) { + lck.id = lc_id.proc_lock_main; + erts_lc_require_lock(&lck); + } + if (locks & ERTS_PROC_LOCK_LINK) { + lck.id = lc_id.proc_lock_link; + erts_lc_require_lock(&lck); + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + lck.id = lc_id.proc_lock_msgq; + erts_lc_require_lock(&lck); + } + if (locks & ERTS_PROC_LOCK_STATUS) { + lck.id = lc_id.proc_lock_status; + erts_lc_require_lock(&lck); + } +} + +void +erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks) +{ + erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1, + p->id, + ERTS_LC_FLG_LT_PROCLOCK); + if (locks & ERTS_PROC_LOCK_STATUS) { + lck.id = lc_id.proc_lock_status; + erts_lc_unrequire_lock(&lck); + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + lck.id = lc_id.proc_lock_msgq; + erts_lc_unrequire_lock(&lck); + } + if (locks & ERTS_PROC_LOCK_LINK) { + lck.id = lc_id.proc_lock_link; + erts_lc_unrequire_lock(&lck); + } + if (locks & ERTS_PROC_LOCK_MAIN) { + lck.id = lc_id.proc_lock_main; + erts_lc_unrequire_lock(&lck); + } +} + + +int +erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks) +{ + if (locks & ERTS_PROC_LOCKS_ALL) { + erts_lc_lock_t lck = ERTS_LC_LOCK_INIT(-1, + p->id, + ERTS_LC_FLG_LT_PROCLOCK); + + if (locks & ERTS_PROC_LOCK_MAIN) + lck.id = lc_id.proc_lock_main; + else if (locks & ERTS_PROC_LOCK_LINK) + lck.id = lc_id.proc_lock_link; + else if (locks & ERTS_PROC_LOCK_MSGQ) + lck.id = lc_id.proc_lock_msgq; + else if (locks & ERTS_PROC_LOCK_STATUS) + lck.id = lc_id.proc_lock_status; + else + erts_lc_fail("Unknown proc lock found"); + + return erts_lc_trylock_force_busy(&lck); + } + return 0; +} + +void erts_proc_lc_chk_only_proc_main(Process *p) +{ + erts_lc_lock_t proc_main = ERTS_LC_LOCK_INIT(lc_id.proc_lock_main, + p->id, + ERTS_LC_FLG_LT_PROCLOCK); + erts_lc_check_exact(&proc_main, 1); +} + +#define ERTS_PROC_LC_EMPTY_LOCK_INIT \ + ERTS_LC_LOCK_INIT(-1, THE_NON_VALUE, ERTS_LC_FLG_LT_PROCLOCK) + +void +erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks) +{ + int have_locks_len = 0; + erts_lc_lock_t have_locks[4] = {ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT}; + if (locks & ERTS_PROC_LOCK_MAIN) { + have_locks[have_locks_len].id = lc_id.proc_lock_main; + have_locks[have_locks_len++].extra = p->id; + } + if (locks & ERTS_PROC_LOCK_LINK) { + have_locks[have_locks_len].id = lc_id.proc_lock_link; + have_locks[have_locks_len++].extra = p->id; + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + have_locks[have_locks_len].id = lc_id.proc_lock_msgq; + have_locks[have_locks_len++].extra = p->id; + } + if (locks & ERTS_PROC_LOCK_STATUS) { + have_locks[have_locks_len].id = lc_id.proc_lock_status; + have_locks[have_locks_len++].extra = p->id; + } + + erts_lc_check(have_locks, have_locks_len, NULL, 0); +} + +void +erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks) +{ + int have_locks_len = 0; + int have_not_locks_len = 0; + erts_lc_lock_t have_locks[4] = {ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT}; + erts_lc_lock_t have_not_locks[4] = {ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT, + ERTS_PROC_LC_EMPTY_LOCK_INIT}; + + if (locks & ERTS_PROC_LOCK_MAIN) { + have_locks[have_locks_len].id = lc_id.proc_lock_main; + have_locks[have_locks_len++].extra = p->id; + } + else { + have_not_locks[have_not_locks_len].id = lc_id.proc_lock_main; + have_not_locks[have_not_locks_len++].extra = p->id; + } + if (locks & ERTS_PROC_LOCK_LINK) { + have_locks[have_locks_len].id = lc_id.proc_lock_link; + have_locks[have_locks_len++].extra = p->id; + } + else { + have_not_locks[have_not_locks_len].id = lc_id.proc_lock_link; + have_not_locks[have_not_locks_len++].extra = p->id; + } + if (locks & ERTS_PROC_LOCK_MSGQ) { + have_locks[have_locks_len].id = lc_id.proc_lock_msgq; + have_locks[have_locks_len++].extra = p->id; + } + else { + have_not_locks[have_not_locks_len].id = lc_id.proc_lock_msgq; + have_not_locks[have_not_locks_len++].extra = p->id; + } + if (locks & ERTS_PROC_LOCK_STATUS) { + have_locks[have_locks_len].id = lc_id.proc_lock_status; + have_locks[have_locks_len++].extra = p->id; + } + else { + have_not_locks[have_not_locks_len].id = lc_id.proc_lock_status; + have_not_locks[have_not_locks_len++].extra = p->id; + } + + erts_lc_check(have_locks, have_locks_len, + have_not_locks, have_not_locks_len); +} + +ErtsProcLocks +erts_proc_lc_my_proc_locks(Process *p) +{ + int resv[4]; + erts_lc_lock_t locks[4] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main, + p->id, + ERTS_LC_FLG_LT_PROCLOCK), + ERTS_LC_LOCK_INIT(lc_id.proc_lock_link, + p->id, + ERTS_LC_FLG_LT_PROCLOCK), + ERTS_LC_LOCK_INIT(lc_id.proc_lock_msgq, + p->id, + ERTS_LC_FLG_LT_PROCLOCK), + ERTS_LC_LOCK_INIT(lc_id.proc_lock_status, + p->id, + ERTS_LC_FLG_LT_PROCLOCK)}; + + ErtsProcLocks res = 0; + + erts_lc_have_locks(resv, locks, 4); + if (resv[0]) + res |= ERTS_PROC_LOCK_MAIN; + if (resv[1]) + res |= ERTS_PROC_LOCK_LINK; + if (resv[2]) + res |= ERTS_PROC_LOCK_MSGQ; + if (resv[3]) + res |= ERTS_PROC_LOCK_STATUS; + + return res; +} + +void +erts_proc_lc_chk_no_proc_locks(char *file, int line) +{ + int resv[4]; + int ids[4] = {lc_id.proc_lock_main, + lc_id.proc_lock_link, + lc_id.proc_lock_msgq, + lc_id.proc_lock_status}; + erts_lc_have_lock_ids(resv, ids, 4); + if (resv[0] || resv[1] || resv[2] || resv[3]) { + erts_lc_fail("%s:%d: Thread has process locks locked when expected " + "not to have any process locks locked", + file, line); + } +} + +#endif /* #ifdef ERTS_ENABLE_LOCK_CHECK */ + +#ifdef ERTS_PROC_LOCK_HARD_DEBUG +void +check_queue(erts_proc_lock_t *lck) +{ + int lock_no; + ErtsProcLocks lflgs = ERTS_PROC_LOCK_FLGS_READ_(lck); + + for (lock_no = 0; lock_no <= ERTS_PROC_LOCK_MAX_BIT; lock_no++) { + ErtsProcLocks wtr; + wtr = (((ErtsProcLocks) 1) << lock_no) << ERTS_PROC_LOCK_WAITER_SHIFT; + if (lflgs & wtr) { + int n; + erts_proc_lock_waiter_t *wtr; + ERTS_LC_ASSERT(lck->queues && lck->queues->queue[lock_no]); + wtr = lck->queues->queue[lock_no]; + n = 0; + do { + wtr = wtr->next; + n++; + } while (wtr != lck->queues->queue[lock_no]); + do { + wtr = wtr->prev; + n--; + } while (wtr != lck->queues->queue[lock_no]); + ERTS_LC_ASSERT(n == 0); + } + else { + ERTS_LC_ASSERT(!lck->queues || !lck->queues->queue[lock_no]); + } + } +} +#endif + +#endif /* ERTS_SMP (the whole file) */ |