Skip to content

Commit

Permalink
[wasm] add no thread variant for freestanding environment
Browse files Browse the repository at this point in the history
This implementation does nothing around preemptive context switching
because there is no native thread.
  • Loading branch information
kateinoigakukun committed Jan 19, 2022
1 parent d6d52a7 commit 420622b
Show file tree
Hide file tree
Showing 5 changed files with 306 additions and 1 deletion.
6 changes: 6 additions & 0 deletions include/ruby/thread_native.h
Expand Up @@ -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. */
Expand Down
2 changes: 1 addition & 1 deletion thread.c
Expand Up @@ -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
Expand Down
278 changes: 278 additions & 0 deletions 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 <time.h>

#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 */
17 changes: 17 additions & 0 deletions 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 */
4 changes: 4 additions & 0 deletions tool/m4/ruby_thread.m4
Expand Up @@ -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
Expand All @@ -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)])
Expand Down

0 comments on commit 420622b

Please sign in to comment.