Feature #3176 » thread-priorities-try2.diff
bignum.c | ||
---|---|---|
return Qtrue;
|
||
}
|
||
extern int ffs(int);
|
||
/*
|
||
* call-seq:
|
||
* big.ffs -> integer
|
||
*
|
||
* Returns the index (starting at 1) of the least significant bit which
|
||
* is set in <i>big</i>. Returns 0 if <i>big</i> is 0.
|
||
*
|
||
* 0xFFFF_FFFF_FFFF_FFFF.ffs #=> 1
|
||
* 0x8000_0000_0000_0000.ffs #=> 64
|
||
* -0x8000_0000_0000_0000.ffs #=> 64
|
||
*/
|
||
static VALUE
|
||
rb_big_ffs(VALUE x)
|
||
{
|
||
int i;
|
||
int len=RBIGNUM_LEN(x);
|
||
int result;
|
||
BDIGIT *bdigits=BDIGITS(x);
|
||
for(i=0;i<len;i++){
|
||
result=ffs(bdigits[i]);
|
||
if (result)
|
||
return UINT2NUM(result + i*sizeof(BDIGIT)*CHAR_BIT);
|
||
}
|
||
return INT2NUM(0);
|
||
}
|
||
/*
|
||
* Bignum objects hold integers outside the range of
|
||
* Fixnum. Bignum objects are created
|
||
... | ... | |
rb_define_method(rb_cBignum, "odd?", rb_big_odd_p, 0);
|
||
rb_define_method(rb_cBignum, "even?", rb_big_even_p, 0);
|
||
rb_define_method(rb_cBignum, "ffs", rb_big_ffs, 0);
|
||
power_cache_init();
|
||
}
|
configure.in | ||
---|---|---|
setsid telldir seekdir fchmod cosh sinh tanh log2 round\
|
||
setuid setgid daemon select_large_fdset setenv unsetenv\
|
||
mktime timegm gmtime_r clock_gettime gettimeofday\
|
||
pread sendfile shutdown sigaltstack)
|
||
pread sendfile shutdown sigaltstack ffs)
|
||
AC_CACHE_CHECK(for unsetenv returns a value, rb_cv_unsetenv_return_value,
|
||
[AC_TRY_COMPILE([
|
eval.c | ||
---|---|---|
void rb_clear_trace_func(void);
|
||
void rb_thread_stop_timer_thread(void);
|
||
extern void rb_threadptr_interrupt(rb_thread_t *th);
|
||
void rb_call_inits(void);
|
||
void Init_heap(void);
|
||
... | ... | |
ruby_finalize_1();
|
||
}
|
||
void rb_thread_stop_timer_thread(void);
|
||
int
|
||
ruby_cleanup(volatile int ex)
|
||
{
|
numeric.c | ||
---|---|---|
return Qtrue;
|
||
}
|
||
#ifndef HAVE_FFS
|
||
static const char ffs_table[] = [0, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1];
|
||
#define FFS_BITS 4
|
||
#define FFS_MASK ((1<<FFS_BITS)-1)
|
||
int
|
||
ffs(int i)
|
||
{
|
||
int j,count=0;
|
||
if (i==0) return 0;
|
||
while(1){
|
||
j=i&FFS_MASK;
|
||
if (j) return ffs_table[j] + count;
|
||
i>>=FFS_BITS;
|
||
count+=FFS_BITS;
|
||
}
|
||
}
|
||
#endif
|
||
/*
|
||
* call-seq:
|
||
* fix.ffs -> integer
|
||
*
|
||
* Returns the index (starting at 1) of the least significant bit which
|
||
* is set in <i>fix</i>. Returns 0 if <i>fix</i> is 0.
|
||
*
|
||
* 1.ffs #=> 1
|
||
* 2.ffs #=> 2
|
||
* 3.ffs #=> 1
|
||
* 4.ffs #=> 3
|
||
* 0.ffs #=> 0
|
||
* -1.ffs #=> 1
|
||
* -2.ffs #=> 2
|
||
*/
|
||
static VALUE
|
||
fix_ffs(VALUE num)
|
||
{
|
||
return INT2FIX(ffs(FIX2INT(num)));
|
||
}
|
||
/*
|
||
* Document-class: ZeroDivisionError
|
||
*
|
||
... | ... | |
rb_define_method(rb_cFixnum, "even?", fix_even_p, 0);
|
||
rb_define_method(rb_cFixnum, "succ", fix_succ, 0);
|
||
rb_define_method(rb_cFixnum, "ffs", fix_ffs, 0);
|
||
rb_cFloat = rb_define_class("Float", rb_cNumeric);
|
||
rb_undef_alloc_func(rb_cFloat);
|
signal.c | ||
---|---|---|
{
|
||
int i, sig = 0;
|
||
/*this function could be made much faster by use of a bitmask and ffs() */
|
||
for (i=1; i<RUBY_NSIG; i++) {
|
||
if (signal_buff.cnt[i] > 0) {
|
||
rb_disable_interrupt();
|
thread.c | ||
---|---|---|
#include "eval_intern.h"
|
||
#include "gc.h"
|
||
#ifndef USE_NATIVE_THREAD_PRIORITY
|
||
#define USE_NATIVE_THREAD_PRIORITY 0
|
||
#define RUBY_THREAD_PRIORITY_MAX 3
|
||
#define RUBY_THREAD_PRIORITY_MIN -3
|
||
#endif
|
||
#ifndef THREAD_DEBUG
|
||
#define THREAD_DEBUG 0
|
||
#endif
|
||
... | ... | |
static inline void blocking_region_end(rb_thread_t *th, struct rb_blocking_region_buffer *region);
|
||
static void pqueue_enqueue(pqueue_t *pqueue, rb_thread_t *th, unsigned priority);
|
||
static rb_thread_t *pqueue_dequeue(pqueue_t *pqueue);
|
||
static rb_thread_t *pqueue_dequeue_starting_at(pqueue_t *pqueue, unsigned start_from, unsigned *found_at);
|
||
void rb_threadptr_interrupt(rb_thread_t *th);
|
||
#define RB_GC_SAVE_MACHINE_CONTEXT(th) \
|
||
do { \
|
||
rb_gc_save_machine_context(th); \
|
||
SET_MACHINE_STACK_END(&(th)->machine_stack_end); \
|
||
} while (0)
|
||
#define GVL_TAKE(th) \
|
||
while (0!=native_mutex_trylock(&(th)->vm->global_vm_lock)) { \
|
||
thread_debug("waiting for gvl\n"); \
|
||
/*might be good to check RUBY_VM_INTERRUPTED here*/ \
|
||
pqueue_enqueue(&(th)->vm->ready_to_run_list, \
|
||
(th), \
|
||
RUBY_THREAD_PRIORITY_MAX-(th)->priority \
|
||
); \
|
||
doze((th)); \
|
||
}
|
||
#define GVL_GIVE(th) \
|
||
do { \
|
||
rb_thread_t *th2; \
|
||
native_mutex_unlock(&(th)->vm->global_vm_lock); \
|
||
th2=pqueue_dequeue(&(th)->vm->ready_to_run_list); \
|
||
thread_debug("giving up gvl to %p\n", th2); \
|
||
if (th2) undoze(th2); \
|
||
} while(0)
|
||
#define GVL_UNLOCK_BEGIN() do { \
|
||
rb_thread_t *_th_stored = GET_THREAD(); \
|
||
RB_GC_SAVE_MACHINE_CONTEXT(_th_stored); \
|
||
native_mutex_unlock(&_th_stored->vm->global_vm_lock)
|
||
GVL_GIVE(_th_stored)
|
||
#define GVL_UNLOCK_END() \
|
||
native_mutex_lock(&_th_stored->vm->global_vm_lock); \
|
||
GVL_TAKE(_th_stored); \
|
||
rb_thread_set_current(_th_stored); \
|
||
} while(0)
|
||
... | ... | |
(th)->status = THREAD_STOPPED; \
|
||
thread_debug("enter blocking region (%p)\n", (void *)(th)); \
|
||
RB_GC_SAVE_MACHINE_CONTEXT(th); \
|
||
native_mutex_unlock(&(th)->vm->global_vm_lock); \
|
||
GVL_GIVE(th); \
|
||
} while (0)
|
||
#define BLOCKING_REGION(exec, ubf, ubfarg) do { \
|
||
... | ... | |
}
|
||
static void
|
||
pqueue_flush(pqueue_t *pqueue)
|
||
{
|
||
memset(pqueue,0,sizeof(pqueue));
|
||
native_mutex_initialize(&pqueue->lock);
|
||
}
|
||
static void
|
||
pqueue_initialize(pqueue_t *pqueue)
|
||
{
|
||
pqueue_flush(pqueue);
|
||
if (sizeof(pqueue->mask)*CHAR_BIT<RUBY_NUM_PRIORITIES)
|
||
rb_fatal("pqueue_t.mask smaller than %d bits!", RUBY_NUM_PRIORITIES);
|
||
if (!getenv("THREAD_PRIOS_WARN")) {
|
||
rb_warn("need benchmarks");
|
||
rb_warn("need to test ffs");
|
||
rb_warn("need to test thread priorities more");
|
||
ruby_setenv("THREAD_PRIOS_WARN","1");
|
||
}
|
||
}
|
||
void
|
||
pqueue_destroy(pqueue_t *pqueue)
|
||
{
|
||
native_mutex_destroy(&pqueue->lock);
|
||
memset(pqueue,0,sizeof(pqueue));
|
||
}
|
||
static void
|
||
pqueue_enqueue(pqueue_t *pqueue, rb_thread_t *th, unsigned priority)
|
||
{
|
||
rb_thread_t *queue;
|
||
if (priority>=RUBY_NUM_PRIORITIES) priority=RUBY_NUM_PRIORITIES-1;
|
||
/*th->next should be NULL here*/
|
||
native_mutex_lock(&pqueue->lock);
|
||
pqueue->mask |= 1<<priority;
|
||
queue=pqueue->queues[priority];
|
||
if (queue==NULL) {
|
||
th->next=th;
|
||
} else {
|
||
th->next=queue->next;
|
||
queue->next=th;
|
||
}
|
||
pqueue->queues[priority]=th;
|
||
native_mutex_unlock(&pqueue->lock);
|
||
}
|
||
static rb_thread_t *
|
||
pqueue_dequeue(pqueue_t *pqueue)
|
||
{
|
||
int i;
|
||
rb_thread_t *result;
|
||
unsigned mask;
|
||
native_mutex_lock(&pqueue->lock);
|
||
mask = pqueue->mask;
|
||
i=ffs(mask)-1;
|
||
if (i==-1) {
|
||
result=NULL;
|
||
} else {
|
||
rb_thread_t *queue=pqueue->queues[i];
|
||
/*queue should be non-NULL here*/
|
||
result=queue->next;
|
||
if (result==queue) { /*last item in this queue?*/
|
||
pqueue->queues[i]=NULL;
|
||
pqueue->mask &= ~(1<<i);
|
||
} else {
|
||
queue->next=result->next;
|
||
}
|
||
result->next=NULL;
|
||
}
|
||
native_mutex_unlock(&pqueue->lock);
|
||
return result;
|
||
}
|
||
static rb_thread_t *
|
||
pqueue_dequeue_starting_at(pqueue_t *pqueue, unsigned start_from, unsigned *found_at)
|
||
{
|
||
int i;
|
||
rb_thread_t *result;
|
||
unsigned mask;
|
||
mask=(1<<start_from)-1;
|
||
mask=~mask;
|
||
native_mutex_lock(&pqueue->lock);
|
||
mask &= pqueue->mask;
|
||
i=ffs(mask)-1;
|
||
if (i==-1) {
|
||
result=NULL;
|
||
*found_at=-1;
|
||
} else {
|
||
rb_thread_t *queue=pqueue->queues[i];
|
||
/*queue should be non-NULL here*/
|
||
*found_at=i;
|
||
result=queue->next;
|
||
if (result==queue) { /*last item in this queue?*/
|
||
pqueue->queues[i]=NULL;
|
||
pqueue->mask &= ~(1<<i);
|
||
} else {
|
||
queue->next=result->next;
|
||
}
|
||
result->next=NULL;
|
||
}
|
||
native_mutex_unlock(&pqueue->lock);
|
||
return result;
|
||
}
|
||
/*returns the priority of the highest priority item in the queue.
|
||
returns -1 if the queue is empty.
|
||
note: this returns a queue-relative priority (0..31, with 0==highest prio),
|
||
rather than a ruby-level priority (-16..15, with 15==highest prio).
|
||
*/
|
||
static int
|
||
pqueue_highest_priority(pqueue_t *pqueue)
|
||
{
|
||
return ffs(pqueue->mask)-1;
|
||
}
|
||
static void
|
||
pqueue_rotate(pqueue_t *pqueue)
|
||
{
|
||
unsigned i=pqueue->next_promote_index;
|
||
if (i){
|
||
rb_thread_t *promoting;
|
||
unsigned found_at;
|
||
promoting=pqueue_dequeue_starting_at(pqueue,i,&found_at);
|
||
if (!promoting) promoting=pqueue_dequeue_starting_at(pqueue,0,&found_at);
|
||
if (promoting) pqueue_enqueue(pqueue,promoting,found_at-1);
|
||
}
|
||
if (++pqueue->next_promote_index>=RUBY_NUM_PRIORITIES) pqueue->next_promote_index=0;
|
||
}
|
||
static void
|
||
set_unblock_function(rb_thread_t *th, rb_unblock_function_t *func, void *arg,
|
||
struct rb_unblock_callback *old)
|
||
{
|
||
... | ... | |
native_mutex_unlock(&th->interrupt_lock);
|
||
}
|
||
/*notify a thread that it should stop waiting and call the thread's
|
||
unblocking function. see rb_thread_blocking_region for a
|
||
description of blocking regions and unblocking functions. Typically,
|
||
th->unblock.func is set to one of these:
|
||
ubf_handle (win32)
|
||
ubf_pthread_cond_signal (pthreads)
|
||
ubf_select
|
||
lock_interrupt
|
||
rb_big_stop
|
||
and th->unblock.arg is set to th. However, they might be different if
|
||
an extention used rb_thread_blocking_region or rb_thread_call_without_gvl
|
||
to define a custom blocking region.
|
||
*/
|
||
void
|
||
rb_threadptr_interrupt(rb_thread_t *th)
|
||
{
|
||
... | ... | |
#endif
|
||
thread_debug("thread start: %p\n", (void *)th);
|
||
native_mutex_lock(&th->vm->global_vm_lock);
|
||
GVL_TAKE(th);
|
||
{
|
||
thread_debug("thread start (get lock): %p\n", (void *)th);
|
||
rb_thread_set_current(th);
|
||
... | ... | |
thread_unlock_all_locking_mutexes(th);
|
||
if (th != main_th) rb_check_deadlock(th->vm);
|
||
if (th->vm->main_thread == th) {
|
||
/*ending main thread; interpreter will exit*/
|
||
ruby_cleanup(state);
|
||
}
|
||
else {
|
||
thread_cleanup_func(th);
|
||
native_mutex_unlock(&th->vm->global_vm_lock);
|
||
GVL_GIVE(th);
|
||
}
|
||
return 0;
|
||
... | ... | |
rb_thread_wait_for(rb_time_timeval(INT2FIX(sec)));
|
||
}
|
||
static int
|
||
there_are_equal_or_higher_priority_threads(rb_thread_t *th)
|
||
{
|
||
int highest_waiting=pqueue_highest_priority(&th->vm->ready_to_run_list);
|
||
if (highest_waiting==-1) return 0;
|
||
highest_waiting=RUBY_THREAD_PRIORITY_MAX-highest_waiting;
|
||
return(highest_waiting>=th->priority);
|
||
}
|
||
static void rb_threadptr_execute_interrupts_rec(rb_thread_t *, int);
|
||
static void
|
||
rb_thread_schedule_rec(int sched_depth)
|
||
{
|
||
static int ticks_til_rotate=0;
|
||
thread_debug("rb_thread_schedule\n");
|
||
if (!rb_thread_alone()) {
|
||
rb_thread_t *th = GET_THREAD();
|
||
if (!sched_depth || there_are_equal_or_higher_priority_threads(th)) {
|
||
thread_debug("rb_thread_schedule/switch start\n");
|
||
thread_debug("rb_thread_schedule/switch start\n");
|
||
RB_GC_SAVE_MACHINE_CONTEXT(th);
|
||
GVL_GIVE(th);
|
||
GVL_TAKE(th);
|
||
RB_GC_SAVE_MACHINE_CONTEXT(th);
|
||
native_mutex_unlock(&th->vm->global_vm_lock);
|
||
{
|
||
native_thread_yield();
|
||
rb_thread_set_current(th);
|
||
thread_debug("rb_thread_schedule/switch done\n");
|
||
}
|
||
native_mutex_lock(&th->vm->global_vm_lock);
|
||
rb_thread_set_current(th);
|
||
thread_debug("rb_thread_schedule/switch done\n");
|
||
if (sched_depth){
|
||
if (ticks_til_rotate) {
|
||
--ticks_til_rotate;
|
||
} else {
|
||
ticks_til_rotate=10;
|
||
pqueue_rotate(&th->vm->ready_to_run_list);
|
||
}
|
||
}
|
||
if (!sched_depth && UNLIKELY(GET_THREAD()->interrupt_flag)) {
|
||
rb_threadptr_execute_interrupts_rec(GET_THREAD(), sched_depth+1);
|
||
}
|
||
if (!sched_depth && UNLIKELY(GET_THREAD()->interrupt_flag)) {
|
||
rb_threadptr_execute_interrupts_rec(GET_THREAD(), sched_depth+1);
|
||
}
|
||
}
|
||
}
|
||
... | ... | |
static inline void
|
||
blocking_region_end(rb_thread_t *th, struct rb_blocking_region_buffer *region)
|
||
{
|
||
native_mutex_lock(&th->vm->global_vm_lock);
|
||
GVL_TAKE(th);
|
||
rb_thread_set_current(th);
|
||
thread_debug("leave blocking region (%p)\n", (void *)th);
|
||
remove_signal_thread_list(th);
|
||
... | ... | |
return Qnil;
|
||
}
|
||
/*
|
||
/* check the current thread for 'interrupts', (asynchronous events sent by other
|
||
* threads or the system) and handle them if present. Here are the types of
|
||
* 'interrupt':
|
||
* a signal
|
||
* an exception sent asynchonously (via Thread#raise)
|
||
* c-level finalizers which are run as a result of garbage collection
|
||
* the thread's time slice has expired so it must give up time to other threads
|
||
*
|
||
* this method and rb_thread_schedule_rec are mutually recursive; however,
|
||
* the sched_depth counter prevents re-entry into the time slice expiry logic.
|
||
* (so this method should never be recursed into more than twice, and never
|
||
* more than once in the time slice expiry logic.)
|
||
*/
|
||
static void
|
||
... | ... | |
sched_depth++;
|
||
EXEC_EVENT_HOOK(th, RUBY_EVENT_SWITCH, th->cfp->self, 0, 0);
|
||
/*I think all this mucking with slice is unnecessary now*/
|
||
if (th->slice > 0) {
|
||
th->slice--;
|
||
}
|
||
... | ... | |
/*****************************************************/
|
||
/*just an alias for rb_threadptr_interrupt, appearently... so why is it needed?*/
|
||
static void
|
||
rb_threadptr_ready(rb_thread_t *th)
|
||
{
|
||
... | ... | |
* will run more frequently than lower-priority threads (but lower-priority
|
||
* threads can also run).
|
||
*
|
||
* This is just hint for Ruby thread scheduler. It may be ignored on some
|
||
* platform.
|
||
*
|
||
* count1 = count2 = 0
|
||
* a = Thread.new do
|
||
* loop { count1 += 1 }
|
||
... | ... | |
vm->main_thread = th;
|
||
native_mutex_reinitialize_atfork(&th->vm->global_vm_lock);
|
||
pqueue_flush(&vm->ready_to_run_list);
|
||
st_foreach(vm->living_threads, atfork, (st_data_t)th);
|
||
st_clear(vm->living_threads);
|
||
st_insert(vm->living_threads, thval, (st_data_t)th->thread_id);
|
||
... | ... | |
rb_thread_lock_t *lp = &GET_THREAD()->vm->global_vm_lock;
|
||
native_mutex_initialize(lp);
|
||
native_mutex_lock(lp);
|
||
pqueue_initialize(&GET_THREAD()->vm->ready_to_run_list);
|
||
native_mutex_initialize(&GET_THREAD()->interrupt_lock);
|
||
}
|
||
}
|
thread_pthread.c | ||
---|---|---|
return pthread_setspecific(ruby_native_thread_key, th) == 0;
|
||
}
|
||
/*called once to initialize the main thread*/
|
||
static void
|
||
Init_native_thread(void)
|
||
{
|
||
... | ... | |
#endif
|
||
CHECK_ERR(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
|
||
pthread_cond_init(&th->native_thread_data.sleep_cond, 0);
|
||
err = pthread_create(&th->thread_id, &attr, thread_start_func_1, th);
|
||
thread_debug("create: %p (%d)", (void *)th, err);
|
||
thread_debug("create: %p (%d)\n", (void *)th, err);
|
||
CHECK_ERR(pthread_attr_destroy(&attr));
|
||
if (!err) {
|
||
pthread_cond_init(&th->native_thread_data.sleep_cond, 0);
|
||
if (err) {
|
||
pthread_cond_destroy(&th->native_thread_data.sleep_cond);
|
||
}
|
||
}
|
||
return err;
|
||
... | ... | |
#define PER_NANO 1000000000
|
||
/*go into a 'light sleep', while waiting for the GVL
|
||
to become available. To be called by ready threads
|
||
that are waiting to run.
|
||
*/
|
||
static void
|
||
doze(rb_thread_t *th)
|
||
{
|
||
int r;
|
||
pthread_mutex_lock(&th->interrupt_lock);
|
||
thread_debug("doze: pthread_cond_wait start\n");
|
||
r = pthread_cond_wait(&th->native_thread_data.sleep_cond,
|
||
&th->interrupt_lock);
|
||
thread_debug("doze: pthread_cond_wait end\n");
|
||
if (r) rb_bug_errno("pthread_cond_wait", r);
|
||
pthread_mutex_unlock(&th->interrupt_lock);
|
||
}
|
||
static void undoze(rb_thread_t *th)
|
||
{
|
||
pthread_cond_signal(&th->native_thread_data.sleep_cond);
|
||
}
|
||
static void
|
||
native_sleep(rb_thread_t *th, struct timeval *tv)
|
||
{
|
thread_win32.c | ||
---|---|---|
return TlsSetValue(ruby_native_thread_key, th);
|
||
}
|
||
/*called once to initialize the main thread*/
|
||
static void
|
||
Init_native_thread(void)
|
||
{
|
||
... | ... | |
thread_debug(" w32_wait_events events:%p, count:%d, timeout:%ld, th:%p\n",
|
||
events, count, timeout, th);
|
||
if (th && (intr = th->native_thread_data.interrupt_event)) {
|
||
native_mutex_lock(&th->vm->global_vm_lock);
|
||
GVL_TAKE(th);
|
||
if (intr == th->native_thread_data.interrupt_event) {
|
||
w32_reset_event(intr);
|
||
if (RUBY_VM_INTERRUPTED(th)) {
|
||
... | ... | |
targets[count++] = intr;
|
||
thread_debug(" * handle: %p (count: %d, intr)\n", intr, count);
|
||
}
|
||
native_mutex_unlock(&th->vm->global_vm_lock);
|
||
GVL_GIVE(th);
|
||
}
|
||
thread_debug(" WaitForMultipleObjects start (count: %d)\n", count);
|
||
... | ... | |
return ret;
|
||
}
|
||
/*go into a 'light sleep', while waiting for the GVL
|
||
to become available. To be called by ready threads
|
||
that are waiting to run.
|
||
*/
|
||
static void
|
||
doze(rb_thread_t *th)
|
||
{
|
||
DWORD ret;
|
||
thread_debug("doze start\n");
|
||
ret=WaitForSingleObject(th->interrupt_event, INFINITE);
|
||
thread_debug("doze done (%lu)\n", ret);
|
||
if (WAIT_OBJECT_0 != ret) w32_error("WaitForSingleObject in doze");
|
||
}
|
||
static void
|
||
undoze(rb_thread_t *th)
|
||
{
|
||
w32_set_event(th->native_thread_data.interrupt_event);
|
||
}
|
||
static void
|
||
native_sleep(rb_thread_t *th, struct timeval *tv)
|
||
{
|
||
... | ... | |
thread_debug("native_sleep start (%lu)\n", msec);
|
||
ret = w32_wait_events(0, 0, msec, th);
|
||
thread_debug("native_sleep done (%lu)\n", ret);
|
||
/*should check for error and rb_bug if there was one here*/
|
||
}
|
||
native_mutex_lock(&th->interrupt_lock);
|
vm.c | ||
---|---|---|
void vm_analysis_register(int reg, int isset);
|
||
void vm_analysis_insn(int insn);
|
||
extern void pqueue_destroy(pqueue_t *pqueue);
|
||
void
|
||
rb_vm_change_state(void)
|
||
{
|
||
... | ... | |
}
|
||
rb_thread_lock_unlock(&vm->global_vm_lock);
|
||
rb_thread_lock_destroy(&vm->global_vm_lock);
|
||
pqueue_destroy(&vm->ready_to_run_list);
|
||
ruby_xfree(vm);
|
||
ruby_current_vm = 0;
|
||
#if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
|
vm_core.h | ||
---|---|---|
#include <setjmp.h>
|
||
#include <signal.h>
|
||
#ifndef USE_NATIVE_THREAD_PRIORITY
|
||
#define USE_NATIVE_THREAD_PRIORITY 0
|
||
#define RUBY_THREAD_PRIORITY_MAX 15
|
||
#define RUBY_THREAD_PRIORITY_MIN -16
|
||
#define RUBY_NUM_PRIORITIES (1+RUBY_THREAD_PRIORITY_MAX-RUBY_THREAD_PRIORITY_MIN)
|
||
#endif
|
||
#ifndef NSIG
|
||
# define NSIG (_SIGMAX + 1) /* For QNX */
|
||
#endif
|
||
... | ... | |
void rb_objspace_free(struct rb_objspace *);
|
||
#endif
|
||
struct rb_thread_struct;
|
||
typedef struct priority_queue {
|
||
/*elements in queues are circularly linked lists of rb_thread_t,
|
||
and queues[i] points to the _tail_ of the queue. in this way,
|
||
both the head and tail of the queue are easily accessible (O(1))
|
||
but only one word is required to hold a pointer to the queue.
|
||
*/
|
||
struct rb_thread_struct *queues[RUBY_NUM_PRIORITIES];
|
||
/*queues[0]==highest prio, queues[RUBY_NUM_PRIORITIES-1]==lowest prio*/
|
||
/*mask holds a index of which elements in queues are nonempty.
|
||
if queues[i]!=NULL, then mask&(1<<i) is set.
|
||
*/
|
||
unsigned mask; /*must be at least RUBY_NUM_PRIORITIES bits*/
|
||
unsigned next_promote_index; /*makes this into a fair priority queue*/
|
||
rb_thread_lock_t lock;
|
||
} pqueue_t;
|
||
typedef struct rb_vm_struct {
|
||
VALUE self;
|
||
rb_thread_lock_t global_vm_lock;
|
||
pqueue_t ready_to_run_list;
|
||
struct rb_thread_struct *main_thread;
|
||
struct rb_thread_struct *running_thread;
|
||
... | ... | |
/* misc */
|
||
int method_missing_reason;
|
||
int abort_on_exception;
|
||
struct rb_thread_struct *next;
|
||
} rb_thread_t;
|
||
/* iseq.c */
|