Skip to content

Commit

Permalink
simple dynamic race detector for jl_array_to_string (#173)
Browse files Browse the repository at this point in the history
  • Loading branch information
d-netto authored Aug 22, 2024
1 parent ec21f11 commit 53e658c
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 1 deletion.
32 changes: 32 additions & 0 deletions src/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -457,13 +457,43 @@ JL_DLLEXPORT jl_array_t *jl_pchar_to_array(const char *str, size_t len)
return a;
}

uv_mutex_t array_to_string_print_lock;

void jl_set_in_flight_bit_for_array_to_string(jl_array_t *a)
{
uintptr_t msk = (1 << ARRAY_TO_STRING_IN_FLIGHT_BIT_OFFSET);
uintptr_t header = jl_atomic_fetch_or((_Atomic(uintptr_t) *)jl_astaggedvalue(a), msk);
if (header & msk) {
uv_mutex_lock(&array_to_string_print_lock);
// Race detected... Someone already set the in-flight bit.
jl_safe_printf("Race detected... Someone already set the in-flight bit.\n");
jlbacktracet(jl_current_task);
uv_mutex_unlock(&array_to_string_print_lock);
}
}

void jl_reset_in_flight_bit_for_array_to_string(jl_array_t *a)
{
uintptr_t msk = (1 << ARRAY_TO_STRING_IN_FLIGHT_BIT_OFFSET);
uintptr_t header = jl_atomic_fetch_and((_Atomic(uintptr_t) *)jl_astaggedvalue(a), ~msk);
if (!(header & msk)) {
uv_mutex_lock(&array_to_string_print_lock);
// Race detected... Someone reset the in-flight bit before we could.
jl_safe_printf("Race detected... Someone reset the in-flight bit before we could.\n");
jlbacktracet(jl_current_task);
uv_mutex_unlock(&array_to_string_print_lock);
}
}

JL_DLLEXPORT jl_value_t *jl_array_to_string(jl_array_t *a)
{
jl_set_in_flight_bit_for_array_to_string(a);
size_t len = jl_array_len(a);
if (len == 0) {
// this may seem like purely an optimization (which it also is), but it
// also ensures that calling `String(a)` doesn't corrupt a previous
// string also created the same way, where `a = StringVector(_)`.
jl_reset_in_flight_bit_for_array_to_string(a);
return jl_an_empty_string;
}
if (a->flags.how == 3 && a->offset == 0 && a->elsize == 1 &&
Expand All @@ -476,11 +506,13 @@ JL_DLLEXPORT jl_value_t *jl_array_to_string(jl_array_t *a)
a->nrows = 0;
a->length = 0;
a->maxsize = 0;
jl_reset_in_flight_bit_for_array_to_string(a);
return o;
}
}
a->nrows = 0;
a->length = 0;
jl_reset_in_flight_bit_for_array_to_string(a);
return jl_pchar_to_string((const char*)jl_array_data(a), len);
}

Expand Down
4 changes: 4 additions & 0 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,8 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel)

void jl_init_heartbeat(void);

extern uv_mutex_t array_to_string_print_lock;

static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_task_t *ct)
{
JL_TIMING(JULIA_INIT, JULIA_INIT);
Expand Down Expand Up @@ -902,6 +904,8 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_
jl_start_gc_threads();
uv_barrier_wait(&thread_init_done);

uv_mutex_init(&array_to_string_print_lock);

jl_init_heartbeat();

jl_gc_enable(1);
Expand Down
7 changes: 6 additions & 1 deletion src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@ typedef struct _jl_value_t jl_value_t;
struct _jl_taggedvalue_bits {
uintptr_t gc:2;
uintptr_t in_image:1;
uintptr_t unused:1;
// Bit to indicate whether a call to `jl_array_to_string` is in-flight
// Mostly used to implement a poor-man's dynamic race detector.
// See usage in `jl_array_to_string`.
#define ARRAY_TO_STRING_IN_FLIGHT_BIT_OFFSET (3)
uintptr_t array_to_string_in_flight:1;
#ifdef _P64
uintptr_t tag:60;
#else
Expand Down Expand Up @@ -2228,6 +2232,7 @@ JL_DLLEXPORT size_t jl_static_show(JL_STREAM *out, jl_value_t *v) JL_NOTSAFEPOIN
JL_DLLEXPORT size_t jl_static_show_func_sig(JL_STREAM *s, jl_value_t *type) JL_NOTSAFEPOINT;
JL_DLLEXPORT void jl_print_backtrace(void) JL_NOTSAFEPOINT;
JL_DLLEXPORT void jlbacktrace(void) JL_NOTSAFEPOINT; // deprecated
JL_DLLEXPORT void jlbacktracet(jl_task_t *t) JL_NOTSAFEPOINT;
// Mainly for debugging, use `void*` so that no type cast is needed in C++.
JL_DLLEXPORT void jl_(void *jl_value) JL_NOTSAFEPOINT;

Expand Down

0 comments on commit 53e658c

Please sign in to comment.