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

[wip] Allow callbacks to be registered for GVL related events #119

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
Prev Previous commit
Next Next commit
Basic unregister GVL callbacks API (thread unsafe)
  • Loading branch information
byroot committed Jan 25, 2022
commit e0c541b8c4dd6fe015df3f26f1efe494ecb93870
32 changes: 30 additions & 2 deletions ext/-test-/gvl/call_without_gvl/call_without_gvl.c
Original file line number Diff line number Diff line change
@@ -74,20 +74,46 @@ ex_callback(uint32_t e, struct gvl_hook_event_args args) {
fprintf(stderr, "calling callback\n");
}

static gvl_hook_t * single_hook = NULL;

static VALUE
thread_register_gvl_callback(VALUE thread) {
rb_gvl_event_new(*ex_callback, 0x12);
single_hook = rb_gvl_event_new(*ex_callback, 0x12);

return Qnil;
}

static VALUE
thread_unregister_gvl_callback(VALUE thread) {
if (single_hook) {
rb_gvl_event_delete(single_hook);
single_hook = NULL;
}

return Qnil;
}

static VALUE
thread_call_gvl_callback(VALUE thread) {
rb_gvl_execute_hooks(0x12);
rb_gvl_execute_hooks(0x12);
return Qnil;
}

static VALUE
thread_register_and_unregister_gvl_callback(VALUE thread) {
gvl_hook_t * hooks[5];
for (int i = 0; i < 5; i++) {
hooks[i] = rb_gvl_event_new(*ex_callback, 0x12);
}

if (!rb_gvl_event_delete(hooks[4])) return Qfalse;
if (!rb_gvl_event_delete(hooks[0])) return Qfalse;
if (!rb_gvl_event_delete(hooks[3])) return Qfalse;
if (!rb_gvl_event_delete(hooks[2])) return Qfalse;
if (!rb_gvl_event_delete(hooks[1])) return Qfalse;
return Qtrue;
}

void
Init_call_without_gvl(void)
{
@@ -96,5 +122,7 @@ Init_call_without_gvl(void)
rb_define_singleton_method(klass, "runnable_sleep", thread_runnable_sleep, 1);
rb_define_singleton_method(klass, "ubf_async_safe", thread_ubf_async_safe, 1);
rb_define_singleton_method(klass, "register_callback", thread_register_gvl_callback, 0);
rb_define_singleton_method(klass, "unregister_callback", thread_unregister_gvl_callback, 0);
rb_define_singleton_method(klass, "register_and_unregister_callbacks", thread_register_and_unregister_gvl_callback, 0);
rb_define_singleton_method(klass, "call_callbacks", thread_call_gvl_callback, 0);
}
16 changes: 15 additions & 1 deletion include/ruby/thread_native.h
Original file line number Diff line number Diff line change
@@ -205,8 +205,22 @@ void rb_native_cond_destroy(rb_nativethread_cond_t *cond);
struct gvl_hook_event_args {
//
};
#include <stdint.h>

typedef void (*rb_gvl_callback)(uint32_t event, struct gvl_hook_event_args args);
void rb_gvl_event_new(void *callback, uint32_t event);

// TODO: this is going to be the same on Windows so move it somewhere sensible
typedef struct gvl_hook {
rb_gvl_callback callback;
uint32_t event;

struct gvl_hook *next;
} gvl_hook_t;

#include "ruby/internal/memory.h"

gvl_hook_t * rb_gvl_event_new(void *callback, uint32_t event);
bool rb_gvl_event_delete(gvl_hook_t * hook);
void rb_gvl_execute_hooks(uint32_t event);
RBIMPL_SYMBOL_EXPORT_END()
#endif
11 changes: 10 additions & 1 deletion test/-ext-/gvl/test_last_thread.rb
Original file line number Diff line number Diff line change
@@ -23,7 +23,16 @@ def test_gvl_instrumentation
require '-test-/gvl/call_without_gvl'
Bug::Thread::register_callback

Bug::Thread::call_callbacks
begin
Bug::Thread::call_callbacks
ensure
Bug::Thread::unregister_callback
end
end

def test_gvl_instrumentation_unregister
require '-test-/gvl/call_without_gvl'
assert Bug::Thread::register_and_unregister_callbacks
end
end

24 changes: 23 additions & 1 deletion thread_pthread.c
Original file line number Diff line number Diff line change
@@ -103,7 +103,7 @@

static gvl_hook_t * rb_gvl_hooks = NULL;

void
gvl_hook_t *
rb_gvl_event_new(void *callback, uint32_t event) {
gvl_hook_t *hook = ALLOC_N(gvl_hook_t, 1);
hook->callback = callback;
@@ -115,6 +115,28 @@ rb_gvl_event_new(void *callback, uint32_t event) {
hook->next = rb_gvl_hooks;
rb_gvl_hooks = hook;
}
return hook;
}

bool
rb_gvl_event_delete(gvl_hook_t * hook) {
if (rb_gvl_hooks == hook) {
rb_gvl_hooks = hook->next;
ruby_xfree(hook);
return TRUE;
}

gvl_hook_t *h = rb_gvl_hooks;

do {
if (h->next == hook) {
h->next = hook->next;
ruby_xfree(hook);
return TRUE;
}
} while ((h = h->next));

return FALSE;
}

void
11 changes: 0 additions & 11 deletions thread_pthread.h
Original file line number Diff line number Diff line change
@@ -69,17 +69,6 @@ typedef struct rb_global_vm_lock_struct {
int wait_yield;
} rb_global_vm_lock_t;

#include <stdint.h>

// TODO: this is going to be the same on Windows so move it somewhere sensible
typedef struct gvl_hook {
rb_gvl_callback callback;
uint32_t event;

struct gvl_hook *next;
} gvl_hook_t;

#include "ruby/internal/memory.h"

#if __STDC_VERSION__ >= 201112
#define RB_THREAD_LOCAL_SPECIFIER _Thread_local