Skip to content

Commit

Permalink
Implement reentrant mutexes.
Browse files Browse the repository at this point in the history
We can't rely on libuv ones to be reentrant, so we need to build the
recursion support ourselves.
  • Loading branch information
jnthn committed Feb 22, 2014
1 parent 19ff5ac commit d50b471
Show file tree
Hide file tree
Showing 12 changed files with 208 additions and 7 deletions.
2 changes: 2 additions & 0 deletions build/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ OBJECTS = src/core/args@obj@ \
src/6model/reprs/MVMDLLSym@obj@ \
src/6model/reprs/MVMMultiCache@obj@ \
src/6model/reprs/MVMContinuation@obj@ \
src/6model/reprs/ReentrantMutex@obj@ \
src/6model/6model@obj@ \
src/6model/bootstrap@obj@ \
src/6model/sc@obj@ \
Expand Down Expand Up @@ -208,6 +209,7 @@ HEADERS = src/moar.h \
src/6model/reprs/MVMDLLSym.h \
src/6model/reprs/MVMMultiCache.h \
src/6model/reprs/MVMContinuation.h \
src/6model/reprs/ReentrantMutex.h \
src/6model/sc.h \
src/mast/compiler.h \
src/mast/driver.h \
Expand Down
1 change: 1 addition & 0 deletions src/6model/reprs.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ void MVM_repr_initialize_registry(MVMThreadContext *tc) {
register_core_repr(DLLSym);
register_core_repr(MultiCache);
register_core_repr(Continuation);
register_core_repr(ReentrantMutex);

tc->instance->num_reprs = MVM_REPR_CORE_COUNT;
}
Expand Down
4 changes: 3 additions & 1 deletion src/6model/reprs.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "6model/reprs/MVMDLLSym.h"
#include "6model/reprs/MVMMultiCache.h"
#include "6model/reprs/MVMContinuation.h"
#include "6model/reprs/ReentrantMutex.h"

/* REPR related functions. */
void MVM_repr_initialize_registry(MVMThreadContext *tc);
Expand Down Expand Up @@ -64,8 +65,9 @@ const MVMREPROps * MVM_repr_get_by_name(MVMThreadContext *tc, MVMString *name);
#define MVM_REPR_ID_MVMDLLSym 25
#define MVM_REPR_ID_MVMMultiCache 26
#define MVM_REPR_ID_MVMContinuation 27
#define MVM_REPR_ID_ReentrantMutex 28

#define MVM_REPR_CORE_COUNT 28
#define MVM_REPR_CORE_COUNT 29
#define MVM_REPR_MAX_COUNT 64

/* Default attribute functions for a REPR that lacks them. */
Expand Down
131 changes: 131 additions & 0 deletions src/6model/reprs/ReentrantMutex.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#include "moar.h"

/* This representation's function pointer table. */
static const MVMREPROps this_repr;

/* Creates a new type object of this representation, and associates it with
* the given HOW. */
static MVMObject * type_object_for(MVMThreadContext *tc, MVMObject *HOW) {
MVMSTable *st = MVM_gc_allocate_stable(tc, &this_repr, HOW);

MVMROOT(tc, st, {
MVMObject *obj = MVM_gc_allocate_type_object(tc, st);
MVM_ASSIGN_REF(tc, &(st->header), st->WHAT, obj);
st->size = sizeof(MVMReentrantMutex);
});

return st->WHAT;
}

/* Creates a new instance based on the type object. */
static MVMObject * allocate(MVMThreadContext *tc, MVMSTable *st) {
return MVM_gc_allocate_object(tc, st);
}

/* Initializes a new instance. */
static void initialize(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) {
MVMReentrantMutexBody *rm = (MVMReentrantMutexBody *)data;
int init_stat;
rm->mutex = malloc(sizeof(uv_mutex_t));
if ((init_stat = uv_mutex_init(rm->mutex)) < 0)
MVM_exception_throw_adhoc(tc, "Failed to initialize mutex: %s",
uv_strerror(init_stat));
}

/* Copies the body of one object to another. */
static void copy_to(MVMThreadContext *tc, MVMSTable *st, void *src, MVMObject *dest_root, void *dest) {
MVM_exception_throw_adhoc(tc, "Cannot copy object with representation ReentrantMutex");
}

/* Called by the VM in order to free memory associated with this object. */
static void gc_free(MVMThreadContext *tc, MVMObject *obj) {
/* The ThreadContext has already been destroyed by the GC. */
MVMReentrantMutex *rm = (MVMReentrantMutex *)obj;
uv_mutex_destroy(rm->body.mutex);
free(rm->body.mutex);
}

/* Gets the storage specification for this representation. */
static MVMStorageSpec get_storage_spec(MVMThreadContext *tc, MVMSTable *st) {
MVMStorageSpec spec;
spec.inlineable = MVM_STORAGE_SPEC_REFERENCE;
spec.boxed_primitive = MVM_STORAGE_SPEC_BP_NONE;
spec.can_box = 0;
return spec;
}

/* Compose the representation. */
static void compose(MVMThreadContext *tc, MVMSTable *st, MVMObject *info) {
/* Nothing to do for this REPR. */
}

/* Set the size of the STable. */
static void deserialize_stable_size(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) {
st->size = sizeof(MVMReentrantMutex);
}

/* Initializes the representation. */
const MVMREPROps * MVMReentrantMutex_initialize(MVMThreadContext *tc) {
return &this_repr;
}

static const MVMREPROps this_repr = {
type_object_for,
allocate,
initialize,
copy_to,
MVM_REPR_DEFAULT_ATTR_FUNCS,
MVM_REPR_DEFAULT_BOX_FUNCS,
MVM_REPR_DEFAULT_POS_FUNCS,
MVM_REPR_DEFAULT_ASS_FUNCS,
MVM_REPR_DEFAULT_ELEMS,
get_storage_spec,
NULL, /* change_type */
NULL, /* serialize */
NULL, /* deserialize */
NULL, /* serialize_repr_data */
NULL, /* deserialize_repr_data */
deserialize_stable_size,
NULL, /* gc_mark */
gc_free,
NULL, /* gc_cleanup */
NULL, /* gc_mark_repr_data */
NULL, /* gc_free_repr_data */
compose,
"ReentrantMutex", /* name */
MVM_REPR_ID_ReentrantMutex,
0, /* refs_frames */
};

/* Locks the mutex. */
void MVM_reentrantmutex_lock(MVMThreadContext *tc, MVMReentrantMutex *rm) {
if (MVM_load(&rm->body.holder_id) == tc->thread_id) {
/* We already hold the lock; bump the count. */
MVM_incr(&rm->body.lock_count);
}
else {
/* Not holding the lock; obtain it. */
MVMROOT(tc, rm, {
MVM_gc_mark_thread_blocked(tc);
uv_mutex_lock(rm->body.mutex);
MVM_gc_mark_thread_unblocked(tc);
});
MVM_store(&rm->body.holder_id, tc->thread_id);
MVM_store(&rm->body.lock_count, 1);
}
}

/* Unlocks the mutex. */
void MVM_reentrantmutex_unlock(MVMThreadContext *tc, MVMReentrantMutex *rm) {
/* Ensure we hold the lock. */
if (MVM_load(&rm->body.holder_id) == tc->thread_id) {
if (MVM_decr(&rm->body.lock_count) == 1) {
/* Decremented the last recursion count; really unlock. */
MVM_store(&rm->body.holder_id, 0);
uv_mutex_unlock(rm->body.mutex);
}
}
else {
MVM_exception_throw_adhoc(tc, "Attempt to unlock mutex by thread not holding it");
}
}
24 changes: 24 additions & 0 deletions src/6model/reprs/ReentrantMutex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* Representation used for VM thread handles. */
struct MVMReentrantMutexBody {
/* The (non-reentrant) mutex supplied by libuv. Sadly, we have to hold it
* at a level of indirection - at least on Windows - because if the object
* is moved it causes confusion. */
uv_mutex_t *mutex;

/* Who currently holds the mutex, if anyone. */
AO_t holder_id;

/* How many times we've taken the lock. */
AO_t lock_count;
};
struct MVMReentrantMutex {
MVMObject common;
MVMReentrantMutexBody body;
};

/* Function for REPR setup. */
const MVMREPROps * MVMReentrantMutex_initialize(MVMThreadContext *tc);

/* Lock and unlock functions. */
void MVM_reentrantmutex_lock(MVMThreadContext *tc, MVMReentrantMutex *rm);
void MVM_reentrantmutex_unlock(MVMThreadContext *tc, MVMReentrantMutex *rm);
20 changes: 20 additions & 0 deletions src/core/interp.c
Original file line number Diff line number Diff line change
Expand Up @@ -3958,6 +3958,26 @@ void MVM_interp_run(MVMThreadContext *tc, void (*initial_invoke)(MVMThreadContex
GET_REG(cur_op, 0).o = MVM_thread_current(tc);
cur_op += 2;
goto NEXT;
OP(lock): {
MVMObject *lock = GET_REG(cur_op, 0).o;
if (REPR(lock)->ID == MVM_REPR_ID_ReentrantMutex)
MVM_reentrantmutex_lock(tc, (MVMReentrantMutex *)lock);
else
MVM_exception_throw_adhoc(tc,
"lock requires an object with REPR ReentrantMutex");
cur_op += 2;
goto NEXT;
}
OP(unlock): {
MVMObject *lock = GET_REG(cur_op, 0).o;
if (REPR(lock)->ID == MVM_REPR_ID_ReentrantMutex)
MVM_reentrantmutex_unlock(tc, (MVMReentrantMutex *)lock);
else
MVM_exception_throw_adhoc(tc,
"lock requires an object with REPR ReentrantMutex");
cur_op += 2;
goto NEXT;
}
#if MVM_CGOTO
OP_CALL_EXTOP: {
/* Bounds checking? Never heard of that. */
Expand Down
4 changes: 2 additions & 2 deletions src/core/oplabels.h
Original file line number Diff line number Diff line change
Expand Up @@ -564,8 +564,8 @@ static const void * const LABELS[] = {
&&OP_threadid,
&&OP_threadyield,
&&OP_currentthread,
NULL,
NULL,
&&OP_lock,
&&OP_unlock,
NULL,
NULL,
NULL,
Expand Down
2 changes: 2 additions & 0 deletions src/core/oplist
Original file line number Diff line number Diff line change
Expand Up @@ -592,3 +592,5 @@ threadrun r(obj)
threadid w(int64) r(obj)
threadyield
currentthread w(obj)
lock r(obj)
unlock r(obj)
16 changes: 15 additions & 1 deletion src/core/ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -3943,9 +3943,23 @@ static MVMOpInfo MVM_op_infos[] = {
1,
{ MVM_operand_write_reg | MVM_operand_obj }
},
{
MVM_OP_lock,
"lock",
" ",
1,
{ MVM_operand_read_reg | MVM_operand_obj }
},
{
MVM_OP_unlock,
"unlock",
" ",
1,
{ MVM_operand_read_reg | MVM_operand_obj }
},
};

static unsigned short MVM_op_counts = 563;
static unsigned short MVM_op_counts = 565;

MVMOpInfo * MVM_op_get_op(unsigned short op) {
if (op >= MVM_op_counts)
Expand Down
2 changes: 2 additions & 0 deletions src/core/ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,8 @@
#define MVM_OP_threadid 560
#define MVM_OP_threadyield 561
#define MVM_OP_currentthread 562
#define MVM_OP_lock 563
#define MVM_OP_unlock 564

#define MVM_OP_EXT_BASE 1024
#define MVM_OP_EXT_CU_LIMIT 1024
Expand Down
7 changes: 4 additions & 3 deletions src/moar.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ MVMInstance * MVM_vm_create_instance(void) {

/* Create the main thread's ThreadContext and stash it. */
instance->main_thread = MVM_tc_create(instance);
instance->main_thread->thread_id = 1;

/* No user threads when we start, and next thread to be created gets ID 1
* (the main thread got ID 0). */
/* No user threads when we start, and next thread to be created gets ID 2
* (the main thread got ID 1). */
instance->num_user_threads = 0;
MVM_store(&instance->next_user_thread_id, 1);
MVM_store(&instance->next_user_thread_id, 2);

/* Set up the permanent roots storage. */
instance->num_permroots = 0;
Expand Down
2 changes: 2 additions & 0 deletions src/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ typedef struct MVMMultiCacheBody MVMMultiCacheBody;
typedef struct MVMMultiArityCache MVMMultiArityCache;
typedef struct MVMContinuation MVMContinuation;
typedef struct MVMContinuationBody MVMContinuationBody;
typedef struct MVMReentrantMutex MVMReentrantMutex;
typedef struct MVMReentrantMutexBody MVMReentrantMutexBody;
typedef struct MVMObject MVMObject;
typedef struct MVMObjectStooge MVMObjectStooge;
typedef struct MVMOpInfo MVMOpInfo;
Expand Down

0 comments on commit d50b471

Please sign in to comment.