diff --git a/src/c/threads/mutex.d b/src/c/threads/mutex.d index 3452bc894..cdd91a148 100755 --- a/src/c/threads/mutex.d +++ b/src/c/threads/mutex.d @@ -113,12 +113,15 @@ mp_giveup_lock(cl_object lock) FEerror_not_owned(lock); } if (--lock->lock.counter == 0) { - lock->lock.owner = ECL_NIL; - print_lock("releasing %p\t", lock, lock); - ecl_wakeup_waiters(env, lock, ECL_WAKEUP_ONE); - } else { - print_lock("released %p\t", lock, lock); - } + cl_object first = ecl_waiter_pop(env, lock);; + if (first == ECL_NIL) { + lock->lock.owner = ECL_NIL; + } else { + lock->lock.counter = 1; + lock->lock.owner = first; + ecl_wakeup_process(first); + } + } ecl_return1(env, ECL_T); } @@ -158,6 +161,26 @@ mp_get_lock_nowait(cl_object lock) ecl_return1(env, get_lock_inner(env, lock)); } +static cl_object +own_or_get_lock(cl_env_ptr env, cl_object lock) +{ + cl_object output; + cl_object own_process = env->own_process; + ecl_disable_interrupts_env(env); + if (AO_compare_and_swap_full((AO_t*)&(lock->lock.owner), + (AO_t)ECL_NIL, (AO_t)own_process)) { + lock->lock.counter = 1; + output = ECL_T; + print_lock("acquired %p\t", lock, lock); + } else if (lock->lock.owner == own_process) { + output = ECL_T; + } else { + output = ECL_NIL; + } + ecl_enable_interrupts_env(env); + return output; +} + cl_object mp_get_lock_wait(cl_object lock) { @@ -166,7 +189,7 @@ mp_get_lock_wait(cl_object lock) FEerror_not_a_lock(lock); } if (get_lock_inner(env, lock) == ECL_NIL) { - ecl_wait_on(env, get_lock_inner, lock); + ecl_wait_on(env, own_or_get_lock, lock); } @(return ECL_T) } diff --git a/src/c/threads/queue.d b/src/c/threads/queue.d index bb282f539..7aad83b00 100755 --- a/src/c/threads/queue.d +++ b/src/c/threads/queue.d @@ -302,6 +302,29 @@ ecl_wait_on(cl_env_ptr env, cl_object (*condition)(cl_env_ptr, cl_object), cl_ob #endif } +cl_object +ecl_waiter_pop(cl_env_ptr the_env, cl_object q) +{ + cl_object output; + ecl_disable_interrupts_env(the_env); + ecl_get_spinlock(the_env, &q->queue.spinlock); + { + cl_object l; + output = ECL_NIL; + for (l = q->queue.list; l != ECL_NIL; l = ECL_CONS_CDR(l)) { + cl_object p = ECL_CONS_CAR(l); + if (p->process.phase != ECL_PROCESS_INACTIVE && + p->process.phase != ECL_PROCESS_EXITING) { + output = p; + break; + } + } + } + ecl_giveup_spinlock(&q->queue.spinlock); + ecl_enable_interrupts_env(the_env); + return output; +} + void ecl_wakeup_waiters(cl_env_ptr the_env, cl_object q, int flags) { diff --git a/src/h/internal.h b/src/h/internal.h index e5ecdcdb6..191becde5 100755 --- a/src/h/internal.h +++ b/src/h/internal.h @@ -497,6 +497,7 @@ extern void ecl_giveup_spinlock(cl_object *lock); extern cl_object ecl_wait_on(cl_env_ptr env, cl_object (*condition)(cl_env_ptr, cl_object), cl_object o); extern void ecl_wakeup_waiters(cl_env_ptr the_env, cl_object o, int flags); extern void ecl_wakeup_process(cl_object process); +extern cl_object ecl_waiter_pop(cl_env_ptr the_env, cl_object q); #endif /* threads/rwlock.d */