Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend caml_locking_scheme with callbacks for thread start/stop #1411

Merged
merged 1 commit into from
May 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions ocaml/otherlibs/systhreads/st_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ struct caml_locking_scheme caml_default_locking_scheme =
{ &default_master_lock,
(void (*)(void*))&st_masterlock_acquire,
(void (*)(void*))&st_masterlock_release,
NULL,
NULL,
(void (*)(void*))&st_masterlock_init,
default_can_skip_yield,
(void (*)(void*))&st_thread_yield };
Expand Down Expand Up @@ -649,6 +651,7 @@ static ST_THREAD_FUNCTION caml_thread_start(void * arg)
caml_thread_t th = (caml_thread_t) arg;
value clos;
void * signal_stack;
struct caml_locking_scheme* sch;
#ifdef NATIVE_CODE
struct longjmp_buffer termination_buf;
char tos;
Expand All @@ -659,6 +662,9 @@ static ST_THREAD_FUNCTION caml_thread_start(void * arg)
/* Associate the thread descriptor with the thread */
st_tls_set(thread_descriptor_key, (void *) th);
st_thread_set_id(Ident(th->descr));
sch = atomic_load(&caml_locking_scheme);
if (sch->thread_start != NULL)
sch->thread_start(sch->context, Thread_type_caml);
/* Acquire the global mutex */
caml_leave_blocking_section();
st_thread_set_id(Ident(th->descr));
Expand All @@ -673,6 +679,9 @@ static ST_THREAD_FUNCTION caml_thread_start(void * arg)
caml_modify(&(Start_closure(th->descr)), Val_unit);
caml_callback_exn(clos, Val_unit);
caml_thread_stop();
sch = atomic_load(&caml_locking_scheme);
if (sch->thread_stop != NULL)
sch->thread_stop(sch->context, Thread_type_caml);
#ifdef NATIVE_CODE
}
#endif
Expand Down Expand Up @@ -722,10 +731,15 @@ CAMLprim value caml_thread_new(value clos) /* ML */
CAMLexport int caml_c_thread_register(void)
{
caml_thread_t th;
struct caml_locking_scheme* sch;
#ifdef NATIVE_CODE
st_retcode err;
#endif

sch = atomic_load(&caml_locking_scheme);
if (sch->thread_start != NULL)
sch->thread_start(sch->context, Thread_type_c_registered);

/* Already registered? */
if (st_tls_get(thread_descriptor_key) != NULL) return 0;
/* Create a thread info block */
Expand Down Expand Up @@ -767,6 +781,7 @@ CAMLexport int caml_c_thread_register(void)

CAMLexport int caml_c_thread_unregister(void)
{
struct caml_locking_scheme* sch;
caml_thread_t th = st_tls_get(thread_descriptor_key);
/* Not registered? */
if (th == NULL) return 0;
Expand All @@ -781,6 +796,9 @@ CAMLexport int caml_c_thread_unregister(void)
if (all_threads == NULL) caml_thread_cleanup(Val_unit);
/* Release the runtime */
release_runtime_lock();
sch = atomic_load(&caml_locking_scheme);
if (sch->thread_stop != NULL)
sch->thread_stop(sch->context, Thread_type_c_registered);
return 1;
}

Expand Down
8 changes: 8 additions & 0 deletions ocaml/otherlibs/systhreads/threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,19 @@ CAMLextern_libthreads int caml_c_thread_unregister(void);
Both functions return 1 on success, 0 on error.
*/

enum caml_thread_type { Thread_type_caml, Thread_type_c_registered };
struct caml_locking_scheme {
void* context;
void (*lock)(void*);
void (*unlock)(void*);

/* If non-NULL, these functions are called when threads start and stop.
For threads created by OCaml, that's at creation and termination.
For threads created by C, that's at caml_c_thread_register/unregister.
The lock is not held when these functions are called. */
void (*thread_start)(void*, enum caml_thread_type);
void (*thread_stop)(void*, enum caml_thread_type);
stedolan marked this conversation as resolved.
Show resolved Hide resolved

/* Called after fork().
The lock should be held after this function returns. */
void (*reinitialize_after_fork)(void*);
Expand Down
3 changes: 3 additions & 0 deletions ocaml/testsuite/tests/lib-systhreads/swapgil.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ include systhreads
*** native
*)

external setup : unit -> unit = "swap_gil_setup"
let () = setup ()

let counter = ref 0

external blocking_section : unit -> unit = "blocking_section"
Expand Down
29 changes: 29 additions & 0 deletions ocaml/testsuite/tests/lib-systhreads/swapgil_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ struct c_thread {
pthread_t thread;
};

static __thread enum caml_thread_type thread_ty = Thread_type_caml;
static __thread int started = 0;
void* threadfn(struct c_thread* th)
{
thread_ty = Thread_type_c_registered;
caml_c_thread_register();
caml_leave_blocking_section();
caml_callback(th->callback, Val_unit);
caml_enter_blocking_section();
if (!started) abort();
caml_c_thread_unregister();
if (started) abort();
return NULL;
}

Expand Down Expand Up @@ -60,16 +65,19 @@ static void runtime_lock(void* m)
timeout.tv_sec = 0;
timeout.tv_usec = 1;
select(0, NULL, NULL, NULL, &timeout);
if (!started) abort();
if (pthread_mutex_lock(m) != 0) abort();
}

static void runtime_unlock(void* m)
{
if (!started) abort();
if (pthread_mutex_unlock(m) != 0) abort();
}

static void runtime_yield(void* m)
{
if (!started) abort();
if (pthread_mutex_unlock(m) != 0) abort();
#ifdef __linux__
/* sched_yield() doesn't do what we want in Linux 2.6 and up (PR#2663) */
Expand All @@ -84,12 +92,31 @@ static void runtime_yield(void* m)
if (pthread_mutex_lock(m) != 0) abort();
}

static void runtime_thread_start(void* m, enum caml_thread_type ty)
{
if (ty != thread_ty) abort();
started = 1;
}

static void runtime_thread_stop(void* m, enum caml_thread_type ty)
{
if (ty != thread_ty) abort();
started = 0;
}

static void runtime_reinitialize(void* m)
{
/* This test doesn't fork, so this never runs. */
abort();
}

value swap_gil_setup(value unused)
{
caml_default_locking_scheme.thread_start = runtime_thread_start;
caml_default_locking_scheme.thread_stop = runtime_thread_stop;
started = 1;
}

value swap_gil(value unused)
{
struct caml_locking_scheme* s;
Expand All @@ -104,6 +131,8 @@ value swap_gil(value unused)
s->context = m;
s->lock = runtime_lock;
s->unlock = runtime_unlock;
s->thread_start = runtime_thread_start;
s->thread_stop = runtime_thread_stop;
s->reinitialize_after_fork = runtime_reinitialize;
s->can_skip_yield = NULL;
s->yield = runtime_yield;
Expand Down