diff --git a/include/ruby/thread_native.h b/include/ruby/thread_native.h index 2945ff1e4d..c23b15e133 100644 --- a/include/ruby/thread_native.h +++ b/include/ruby/thread_native.h @@ -37,6 +37,12 @@ typedef pthread_t rb_nativethread_id_t; typedef pthread_mutex_t rb_nativethread_lock_t; typedef pthread_cond_t rb_nativethread_cond_t; +#elif defined(__wasi__) // no-thread platforms + +typedef struct rb_nativethread_id_t *rb_nativethread_id_t; +typedef struct rb_nativethread_lock_t *rb_nativethread_lock_t; +typedef struct rb_nativethread_cond_t *rb_nativethread_cond_t; + #elif defined(__DOXYGEN__) /** Opaque type that holds an ID of a native thread. */ diff --git a/thread.c b/thread.c index 52ecf124bd..95daed6668 100644 --- a/thread.c +++ b/thread.c @@ -340,7 +340,7 @@ rb_thread_s_debug_set(VALUE self, VALUE val) # define PRI_THREAD_ID "p" #endif -NOINLINE(static int thread_start_func_2(rb_thread_t *th, VALUE *stack_start)); +MAYBE_UNUSED(NOINLINE(static int thread_start_func_2(rb_thread_t *th, VALUE *stack_start))); void ruby_sigchld_handler(rb_vm_t *); /* signal.c */ static void diff --git a/thread_none.c b/thread_none.c new file mode 100644 index 0000000000..24e8e45e10 --- /dev/null +++ b/thread_none.c @@ -0,0 +1,278 @@ +/* + A thread interface implementation without any system thread. + + Assumption: + * There is a only single thread in the ruby process + * No signal happens targeting the ruby process + + Note: + * No thread switching in the VM + * No timer thread because thread switching won't happen + * No mutex guard because the VM won't be racy +*/ + +#ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION + +#include + +#define DEBUG_OUT() (void)(0); + +#define TIME_QUANTUM_MSEC (100) +#define TIME_QUANTUM_USEC (TIME_QUANTUM_MSEC * 1000) +#define TIME_QUANTUM_NSEC (TIME_QUANTUM_USEC * 1000) + +// Do nothing for GVL +static void +gvl_acquire(rb_global_vm_lock_t *gvl, rb_thread_t *th) +{ +} + +static void +gvl_release(rb_global_vm_lock_t *gvl) +{ +} + +static void +gvl_yield(rb_global_vm_lock_t *gvl, rb_thread_t *th) +{ +} + +void +rb_gvl_init(rb_global_vm_lock_t *gvl) +{ +} + +static void +gvl_destroy(rb_global_vm_lock_t *gvl) +{ +} + +// Do nothing for mutex guard +void +rb_native_mutex_lock(rb_nativethread_lock_t *lock) +{ +} + +void +rb_native_mutex_unlock(rb_nativethread_lock_t *lock) +{ +} + +int +rb_native_mutex_trylock(rb_nativethread_lock_t *lock) +{ + return 0; +} + +void +rb_native_mutex_initialize(rb_nativethread_lock_t *lock) +{ +} + +void +rb_native_mutex_destroy(rb_nativethread_lock_t *lock) +{ +} + +void +rb_native_cond_initialize(rb_nativethread_cond_t *cond) +{ +} + +void +rb_native_cond_destroy(rb_nativethread_cond_t *cond) +{ +} + +void +rb_native_cond_signal(rb_nativethread_cond_t *cond) +{ +} + +void +rb_native_cond_broadcast(rb_nativethread_cond_t *cond) +{ +} + +void +rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex) +{ +} + +void +rb_native_cond_timedwait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex, unsigned long msec) +{ +} + +// The only one thread in process +static rb_thread_t *ruby_native_thread; + +rb_thread_t * +ruby_thread_from_native(void) +{ + return ruby_native_thread; +} + +int +ruby_thread_set_native(rb_thread_t *th) +{ + if (th && th->ec) { + rb_ractor_set_current_ec(th->ractor, th->ec); + } + ruby_native_thread = th; + return 1; // always succeed +} + +void +Init_native_thread(rb_thread_t *th) +{ + // no TLS setup and no thread id setup + ruby_thread_set_native(th); + fill_thread_id_str(th); +} + +static void +native_thread_destroy(rb_thread_t *th) +{ +} + +void +ruby_init_stack(volatile VALUE *addr) +{ +} + +static int +native_thread_init_stack(rb_thread_t *th) +{ + return 0; // success +} + +static int +native_thread_create(rb_thread_t *th) +{ + th->status = THREAD_KILLED; + rb_ractor_living_threads_remove(th->ractor, th); + rb_notimplement(); +} + +// Do nothing for handling ubf because no other thread doesn't exist and unblock anything +#define register_ubf_list(th) (void)(th) +#define unregister_ubf_list(th) (void)(th) +#define ubf_select 0 + +inline static void +ubf_wakeup_all_threads(void) +{ + return; +} + +inline static int +ubf_threads_empty(void) +{ + return 1; // true +} + +inline static void +ubf_list_atfork() +{ +} + +inline static void +ubf_timer_disarm(void) +{ +} + + +// No timer thread because thread switching won't happen +#define TIMER_THREAD_CREATED_P() (1) +inline static void +rb_thread_create_timer_thread(void) +{ +} + +void +rb_thread_wakeup_timer_thread(int sig) +{ +} + +inline static int +native_stop_timer_thread(void) +{ + return 1; // success +} + +inline static void +native_reset_timer_thread(void) +{ +} + +// Do nothing for thread naming +inline static void +native_set_thread_name(rb_thread_t *th) +{ +} + +inline static void +native_set_another_thread_name(rb_nativethread_id_t thread_id, VALUE name) +{ +} + +// Don't expose native thread id for now to keep system's thread API agnostic +#define USE_NATIVE_THREAD_NATIVE_THREAD_ID 0 + +// No reserved fd for piping threads +int +rb_reserved_fd_p(int fd) +{ + return 0; // not reserved +} + +// Don't expose native thread info for now to keep system's thread API agnostic +rb_nativethread_id_t +rb_nativethread_self(void) +{ + return NULL; +} + +// Do nothing for sigwait things because of no signal assumption +// Q(katei): is this correct description? +int +rb_sigwait_fd_get(const rb_thread_t *th) +{ + return -1; +} + +NORETURN(void rb_sigwait_fd_put(rb_thread_t *, int)); +void +rb_sigwait_fd_put(rb_thread_t *th, int fd) +{ + rb_bug("not implemented, should not be called rb_sigwait_fd_put"); +} + +NORETURN(void rb_sigwait_sleep(const rb_thread_t *, int, const rb_hrtime_t *)); +void +rb_sigwait_sleep(const rb_thread_t *th, int sigwait_fd, const rb_hrtime_t *rel) +{ + rb_bug("not implemented, should not be called rb_sigwait_sleep"); +} + +static void +native_sleep(rb_thread_t *th, rb_hrtime_t *rel) +{ + // No signal assumption allows the use of uninterruptible sleep + struct timespec ts; + (void)clock_nanosleep(CLOCK_REALTIME, 0, rb_hrtime2timespec(&ts, rel), NULL); +} + +static int +native_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout, rb_thread_t *th) +{ + return rb_fd_select(n, readfds, writefds, exceptfds, timeout); +} + +static VALUE +rb_thread_start_unblock_thread(void) +{ + return Qfalse; +} +#endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */ diff --git a/thread_none.h b/thread_none.h new file mode 100644 index 0000000000..eac2635ca7 --- /dev/null +++ b/thread_none.h @@ -0,0 +1,17 @@ +#ifndef RUBY_THREAD_NONE_H +#define RUBY_THREAD_NONE_H + +#define RB_NATIVETHREAD_LOCK_INIT (void)(0) +#define RB_NATIVETHREAD_COND_INIT (void)(0) + +// no-thread impl doesn't use TLS but define this to avoid using tls key +// based implementation in vm.c +#define RB_THREAD_LOCAL_SPECIFIER + +typedef struct native_thread_data_struct {} native_thread_data_t; + +typedef struct rb_global_vm_lock_struct {} rb_global_vm_lock_t; + +RUBY_EXTERN struct rb_execution_context_struct *ruby_current_ec; + +#endif /* RUBY_THREAD_NONE_H */ diff --git a/tool/m4/ruby_thread.m4 b/tool/m4/ruby_thread.m4 index 3831bc4c06..2e58eddb98 100644 --- a/tool/m4/ruby_thread.m4 +++ b/tool/m4/ruby_thread.m4 @@ -8,6 +8,9 @@ AC_ARG_WITH(thread, [mingw*], [ THREAD_MODEL=win32 ], + [wasi*], [ + THREAD_MODEL=none + ], [ AS_IF([test "$rb_with_pthread" = "yes"], [ THREAD_MODEL=pthread @@ -19,6 +22,7 @@ AC_ARG_WITH(thread, AS_CASE(["$THREAD_MODEL"], [pthread], [AC_CHECK_HEADERS(pthread.h)], [win32], [], +[none], [], [""], [AC_MSG_ERROR(thread model is missing)], [AC_MSG_ERROR(unknown thread model $THREAD_MODEL)])