klisp

an open source interpreter for the Kernel Programming Language.
git clone http://git.hanabi.in/repos/klisp.git
Log | Files | Refs | README

commit f1ed54d9fa77c9378fc035dda595646e43f0535d
parent 44ee75643ede8c2cc39b70bc2cf7e03254bb0e11
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Wed, 29 Aug 2012 17:49:20 -0300

Added mutex lock, unlock, and trylock.

Diffstat:
Msrc/kgthreads.c | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/kmutex.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/kmutex.h | 19+++++++++++++------
3 files changed, 156 insertions(+), 6 deletions(-)

diff --git a/src/kgthreads.c b/src/kgthreads.c @@ -39,6 +39,9 @@ static void *thread_run(void *data) routine somewhere */ bool errorp = false; /* may be set to true in error handler */ bool rootp = true; /* may be set to false in continuation */ + + /* ???/TODO should the fact that the thread thrown an exception + be reported to the error output??? */ /* We have already the appropriate environment, operative and arguments in place, but we still need the @@ -165,6 +168,52 @@ static void make_mutex(klisp_State *K) kapply_cc(K, new_mutex); } +/* mutex-lock */ +static void mutex_lock(klisp_State *K) +{ + TValue *xparams = K->next_xparams; + TValue ptree = K->next_value; + TValue denv = K->next_env; + klisp_assert(ttisenvironment(K->next_env)); + UNUSED(xparams); + UNUSED(denv); + + bind_1tp(K, ptree, "mutex", ttismutex, mutex); + kmutex_lock(K, mutex); + kapply_cc(K, KINERT); +} + +/* mutex-unlock */ +static void mutex_unlock(klisp_State *K) +{ + TValue *xparams = K->next_xparams; + TValue ptree = K->next_value; + TValue denv = K->next_env; + klisp_assert(ttisenvironment(K->next_env)); + UNUSED(xparams); + UNUSED(denv); + + bind_1tp(K, ptree, "mutex", ttismutex, mutex); + kmutex_unlock(K, mutex); + kapply_cc(K, KINERT); +} + +/* mutex-trylock */ +static void mutex_trylock(klisp_State *K) +{ + TValue *xparams = K->next_xparams; + TValue ptree = K->next_value; + TValue denv = K->next_env; + klisp_assert(ttisenvironment(K->next_env)); + UNUSED(xparams); + UNUSED(denv); + + bind_1tp(K, ptree, "mutex", ttismutex, mutex); + bool res = kmutex_trylock(K, mutex); + kapply_cc(K, b2tv(res)); +} + + /* init ground */ void kinit_threads_ground_env(klisp_State *K) { @@ -193,4 +242,12 @@ void kinit_threads_ground_env(klisp_State *K) /* make-mutex */ add_applicative(K, ground_env, "make-mutex", make_mutex, 0); + /* REFACTOR: should lock and unlock have an '!'? + What about try lock?? '!', '?', '!?', neither? + /* mutex-lock */ + add_applicative(K, ground_env, "mutex-lock", mutex_lock, 0); + /* mutex-unlock */ + add_applicative(K, ground_env, "mutex-unlock", mutex_unlock, 0); + /* mutex-trylock */ + add_applicative(K, ground_env, "mutex-trylock", mutex_trylock, 0); } diff --git a/src/kmutex.c b/src/kmutex.c @@ -35,6 +35,92 @@ TValue kmake_mutex(klisp_State *K) return gc2mutex(new_mutex); } +/* LOCK: GIL should be acquired exactly once */ +void kmutex_lock(klisp_State *K, TValue mutex) +{ + TValue thread = gc2th(K); + if (tv_equal(thread, kmutex_owner(mutex))) { + if (kmutex_count(mutex) == KMUTEX_MAX_COUNT) { + klispE_throw_simple(K, "Mutex count overflow"); + return; + } + ++kmutex_count(mutex); + } else { + /* we need to release GIL to avoid deadlocks */ + klisp_unlock(K); + int res = pthread_mutex_lock(&kmutex_mutex(mutex)); + klisp_lock(K); + + if (res != 0) { + klispE_throw_simple_with_irritants(K, "Can't lock mutex", + 1, i2tv(res)); + return; + } + + klisp_assert(!kmutex_is_owned(mutex)); + kmutex_owner(mutex) = thread; + kmutex_count(mutex) = 1; + } +} + +/* LOCK: GIL should be acquired exactly once */ +void kmutex_unlock(klisp_State *K, TValue mutex) +{ + TValue thread = gc2th(K); + if (!kmutex_is_owned(mutex)) { + klispE_throw_simple(K, "The mutex isn't locked"); + return; + } else if (tv_equal(thread, kmutex_owner(mutex))) { + if (kmutex_count(mutex) == 1) { + int res = pthread_mutex_unlock(&kmutex_mutex(mutex)); + + if (res != 0) { + klispE_throw_simple_with_irritants(K, "Can't unlock mutex", + 1, i2tv(res)); + return; + } + + kmutex_owner(mutex) = KMUTEX_NO_OWNER; + kmutex_count(mutex) = 0; + } else { + --kmutex_count(mutex); + } + } else { + klispE_throw_simple(K, "The mutex is locked by a different thread"); + return; + } +} + +/* LOCK: GIL should be acquired exactly once */ +bool kmutex_trylock(klisp_State *K, TValue mutex) +{ + TValue thread = gc2th(K); + if (tv_equal(thread, kmutex_owner(mutex))) { + kmutex_lock(K, mutex); /* this will check max_count */ + return true; + } else if (kmutex_is_owned(mutex)) { + return false; + } else { + /* we need to release GIL to avoid deadlocks */ + klisp_unlock(K); + int res = pthread_mutex_trylock(&kmutex_mutex(mutex)); + klisp_lock(K); + + if (res == 0) { + klisp_assert(!kmutex_is_owned(mutex)); + kmutex_owner(mutex) = thread; + kmutex_count(mutex) = 1; + return true; + } else if (res == EBUSY) { + return false; + } else { + klispE_throw_simple_with_irritants(K, "Error on trylock mutex", + 1, i2tv(res)); + return false; + } + } +} + void klispX_free(klisp_State *K, Mutex *m) { /* XXX/??? Is it okay if the mutex wasn't correctly created?, diff --git a/src/kmutex.h b/src/kmutex.h @@ -13,12 +13,19 @@ TValue kmake_mutex(klisp_State *K); void klispX_free(klisp_State *K, Mutex *mutex); -/* XXX The functions for locking and unlocking are in kgthreads, for now, - mainly because they use locking... */ +/* LOCK: these functions require that the calling code has + acquired the GIL exactly once previous to the call and + they may temporarily release it to avoid deadlocks */ +void kmutex_lock(klisp_State *K, TValue mutex); +void kmutex_unlock(klisp_State *K, TValue mutex); +bool kmutex_trylock(klisp_State *K, TValue mutex); -#define kmutex_is_owned(m_) (ttisthread(tv2mutex(p_)->owner)) -#define kmutex_get_owner(m_) (tv2mutex(p_)->owner) -#define kmutex_get_mutex(m_) (tv2mutex(p_)->mutex) -#define kmutex_get_count(m_) (tv2mutex(p_)->count) +#define kmutex_is_owned(m_) (ttisthread(tv2mutex(m_)->owner)) +#define kmutex_owner(m_) (tv2mutex(m_)->owner) +#define kmutex_mutex(m_) (tv2mutex(m_)->mutex) +#define kmutex_count(m_) (tv2mutex(m_)->count) + +// #define KMUTEX_MAX_COUNT UINT32_MAX +#define KMUTEX_MAX_COUNT 255 #endif