Skip to content

Commit

Permalink
Add MVM_get_running_threads_context to get the running thread's con…
Browse files Browse the repository at this point in the history
…text

Start with a fallback implementation using `uv_key_create`, `uv_key_set` and
`uv_key_get`, to eliminate the static variable and make the code thread
safe.

Use this in `qsort`'s callback, which needs to call functions that take the
thread context pointer, but has no way to pass this in.

The standard C library function `qsort` takes a callback function to compare
two elements, which is only passed two arguments - pointers to the two
elements to compare. As our elements are MVMString pointers, we *also* need
a thread context pointer to pass to `MVM_string_compare`.

This had been implemented by passing the thread context pointer "via" a
static variable. This is obviously not thread safe, but we've got away with
it so far, probably because we rarely throw exceptions in this code.
  • Loading branch information
nwc10 committed Oct 31, 2020
1 parent 004e4bc commit 4cfde6e
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 3 deletions.
5 changes: 2 additions & 3 deletions src/6model/serialization.c
Original file line number Diff line number Diff line change
Expand Up @@ -508,9 +508,9 @@ static void write_array_str(MVMThreadContext *tc, MVMSerializationWriter *writer
}

/* Writes a hash where each key is a MVMString and each value a variant reference. */
static MVMThreadContext *cmp_tc;
static int cmp_strings(const void *s1, const void *s2) {
return MVM_string_compare(cmp_tc, *(MVMString **)s1, *(MVMString **)s2);
MVMThreadContext *tc = MVM_get_running_threads_context();
return MVM_string_compare(tc, *(MVMString **)s1, *(MVMString **)s2);
}
static void write_hash_str_var(MVMThreadContext *tc, MVMSerializationWriter *writer, MVMObject *hash) {
MVMuint32 elems = (MVMuint32)MVM_repr_elems(tc, hash);
Expand All @@ -526,7 +526,6 @@ static void write_hash_str_var(MVMThreadContext *tc, MVMSerializationWriter *wri
MVM_repr_shift_o(tc, iter);
keys[i++] = MVM_iterkey_s(tc, (MVMIter *)iter);
}
cmp_tc = tc;
qsort(keys, elems, sizeof(MVMString*), cmp_strings);
for (i = 0; i < elems; i++) {
MVM_serialization_write_str(tc, writer, keys[i]);
Expand Down
7 changes: 7 additions & 0 deletions src/core/threads.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ static void start_thread(void *data) {
/* Stash thread ID. */
tc->thread_obj->body.native_thread_id = MVM_platform_thread_id();

#ifndef MVM_THREAD_LOCAL
/* Store this thread's thread context pointer, so that we can retrieve it
* in places where we can't pass it in to, such as the callback function
* used by qsort. */
uv_key_set(&MVM_running_threads_context_key, tc);
#endif

/* Create a spesh log for this thread, unless it's just going to run C
* code (and thus it's a VM internal worker). */
if (REPR(tc->thread_obj->body.invokee)->ID != MVM_REPR_ID_MVMCFunction)
Expand Down
16 changes: 16 additions & 0 deletions src/moar.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ MVM_STATIC_INLINE MVMuint64 ptr_hash_64_to_64(MVMuint64 u) {
return (MVMuint64)u;
}

#ifndef MVM_THREAD_LOCAL
uv_key_t MVM_running_threads_context_key;

static void
make_uv_key() {
int result = uv_key_create(&MVM_running_threads_context_key);
if (result)
MVM_panic(1, "uv_key_create failed with code %u", result);
}
#endif

/* Create a new instance of the VM. */
MVMInstance * MVM_vm_create_instance(void) {
MVMInstance *instance;
Expand All @@ -98,6 +109,11 @@ MVMInstance * MVM_vm_create_instance(void) {
char *dynvar_log;
int init_stat;

#ifndef MVM_THREAD_LOCAL
static uv_once_t key_once = UV_ONCE_INIT;
uv_once(&key_once, make_uv_key);
#endif

/* Set up instance data structure. */
instance = MVM_calloc(1, sizeof(MVMInstance));

Expand Down
11 changes: 11 additions & 0 deletions src/moar.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,14 @@ AO_t AO_fetch_compare_and_swap_emulation(volatile AO_t *addr, AO_t old_val, AO_t
* which the other atomic operation macros are used... */
#define MVM_store(addr, new) AO_store_full((volatile AO_t *)(addr), (AO_t)(new))
#define MVM_load(addr) AO_load_full((volatile AO_t *)(addr))

#ifndef MVM_THREAD_LOCAL

/* Fallback to an implememtation using UV's APIs (pretty much pthreads) */
extern uv_key_t MVM_running_threads_context_key;

MVM_STATIC_INLINE MVMThreadContext *MVM_get_running_threads_context(void) {
return uv_key_get(&MVM_running_threads_context_key);
}

#endif

0 comments on commit 4cfde6e

Please sign in to comment.