зеркало из https://github.com/github/ruby.git
introduce USE_VM_CLOCK for windows.
The timer function used on windows system set timer interrupt flag of current main ractor's executing ec and thread can detect the end of time slice. However, to set all ec->interrupt_flag for all running ractors, it is requires to synchronize with other ractors. However, timer thread can not acquire the ractor-wide lock because of some limitation. To solve this issue, this patch introduces USE_VM_CLOCK compile option to introduce rb_vm_t::clock. This clock will be incremented by the timer thread and each thread can check the incrementing by comparison with previous checked clock. At last, on windows platform this patch introduces some overhead, but I think there is no critical performance issue because of this modification.
This commit is contained in:
Родитель
dd07354f27
Коммит
1e8abe5d03
10
thread.c
10
thread.c
|
@ -341,7 +341,6 @@ rb_thread_s_debug_set(VALUE self, VALUE val)
|
|||
#endif
|
||||
|
||||
NOINLINE(static int thread_start_func_2(rb_thread_t *th, VALUE *stack_start));
|
||||
static void timer_thread_function(rb_execution_context_t *ec);
|
||||
void ruby_sigchld_handler(rb_vm_t *); /* signal.c */
|
||||
|
||||
static void
|
||||
|
@ -4574,15 +4573,6 @@ rb_threadptr_check_signal(rb_thread_t *mth)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
timer_thread_function(rb_execution_context_t *ec)
|
||||
{
|
||||
// strictly speaking, accessing gvl->owner is not thread-safe
|
||||
if (ec) {
|
||||
RUBY_VM_SET_TIMER_INTERRUPT(ec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
async_bug_fd(const char *mesg, int errno_arg, int fd)
|
||||
{
|
||||
|
|
|
@ -194,6 +194,7 @@ designate_timer_thread(rb_global_vm_lock_t *gvl)
|
|||
static void
|
||||
do_gvl_timer(rb_global_vm_lock_t *gvl, rb_thread_t *th)
|
||||
{
|
||||
rb_vm_t *vm = GET_VM();
|
||||
static rb_hrtime_t abs;
|
||||
native_thread_data_t *nd = &th->native_thread_data;
|
||||
|
||||
|
@ -208,14 +209,14 @@ do_gvl_timer(rb_global_vm_lock_t *gvl, rb_thread_t *th)
|
|||
gvl->timer_err = native_cond_timedwait(&nd->cond.gvlq, &gvl->lock, &abs);
|
||||
|
||||
ubf_wakeup_all_threads();
|
||||
ruby_sigchld_handler(GET_VM());
|
||||
ruby_sigchld_handler(vm);
|
||||
|
||||
if (UNLIKELY(rb_signal_buff_size())) {
|
||||
if (th == GET_VM()->ractor.main_thread) {
|
||||
if (th == vm->ractor.main_thread) {
|
||||
RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
|
||||
}
|
||||
else {
|
||||
threadptr_trap_interrupt(GET_VM()->ractor.main_thread);
|
||||
threadptr_trap_interrupt(vm->ractor.main_thread);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,7 +224,10 @@ do_gvl_timer(rb_global_vm_lock_t *gvl, rb_thread_t *th)
|
|||
* Timeslice. Warning: the process may fork while this
|
||||
* thread is contending for GVL:
|
||||
*/
|
||||
if (gvl->owner) timer_thread_function(gvl->owner->ec);
|
||||
if (gvl->owner) {
|
||||
// strictly speaking, accessing "gvl->owner" is not thread-safe
|
||||
RUBY_VM_SET_TIMER_INTERRUPT(gvl->owner->ec);
|
||||
}
|
||||
gvl->timer = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -710,13 +710,9 @@ timer_thread_func(void *dummy)
|
|||
rb_vm_t *vm = GET_VM();
|
||||
thread_debug("timer_thread\n");
|
||||
rb_w32_set_thread_description(GetCurrentThread(), L"ruby-timer-thread");
|
||||
while (WaitForSingleObject(timer_thread.lock, TIME_QUANTUM_USEC/1000) ==
|
||||
WAIT_TIMEOUT) {
|
||||
rb_execution_context_t *running_ec = vm->ractor.main_ractor->threads.running_ec;
|
||||
|
||||
if (running_ec) {
|
||||
timer_thread_function(running_ec);
|
||||
}
|
||||
while (WaitForSingleObject(timer_thread.lock,
|
||||
TIME_QUANTUM_USEC/1000) == WAIT_TIMEOUT) {
|
||||
vm->clock++;
|
||||
ruby_sigchld_handler(vm); /* probably no-op */
|
||||
rb_threadptr_check_signal(vm->ractor.main_thread);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
# undef _WIN32
|
||||
# endif
|
||||
|
||||
#define USE_VM_CLOCK 1
|
||||
|
||||
WINBASEAPI BOOL WINAPI
|
||||
TryEnterCriticalSection(IN OUT LPCRITICAL_SECTION lpCriticalSection);
|
||||
|
||||
|
|
22
vm_core.h
22
vm_core.h
|
@ -649,6 +649,10 @@ typedef struct rb_vm_struct {
|
|||
const struct rb_builtin_function *builtin_function_table;
|
||||
int builtin_inline_index;
|
||||
|
||||
#if USE_VM_CLOCK
|
||||
uint32_t clock;
|
||||
#endif
|
||||
|
||||
/* params */
|
||||
struct { /* size in byte */
|
||||
size_t thread_vm_stack_size;
|
||||
|
@ -845,6 +849,9 @@ struct rb_execution_context_struct {
|
|||
/* interrupt flags */
|
||||
rb_atomic_t interrupt_flag;
|
||||
rb_atomic_t interrupt_mask; /* size should match flag */
|
||||
#if USE_VM_CLOCK
|
||||
uint32_t checked_clock;
|
||||
#endif
|
||||
|
||||
rb_fiber_t *fiber_ptr;
|
||||
struct rb_thread_struct *thread_ptr;
|
||||
|
@ -1845,7 +1852,20 @@ enum {
|
|||
#define RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec) ATOMIC_OR((ec)->interrupt_flag, VM_BARRIER_INTERRUPT_MASK)
|
||||
#define RUBY_VM_INTERRUPTED(ec) ((ec)->interrupt_flag & ~(ec)->interrupt_mask & \
|
||||
(PENDING_INTERRUPT_MASK|TRAP_INTERRUPT_MASK))
|
||||
#define RUBY_VM_INTERRUPTED_ANY(ec) ((ec)->interrupt_flag & ~(ec)->interrupt_mask)
|
||||
|
||||
static inline bool
|
||||
RUBY_VM_INTERRUPTED_ANY(rb_execution_context_t *ec)
|
||||
{
|
||||
#if USE_VM_CLOCK
|
||||
uint32_t current_clock = rb_ec_vm_ptr(ec)->clock;
|
||||
|
||||
if (current_clock != ec->checked_clock) {
|
||||
ec->checked_clock = current_clock;
|
||||
RUBY_VM_SET_TIMER_INTERRUPT(ec);
|
||||
}
|
||||
#endif
|
||||
return ec->interrupt_flag & ~(ec)->interrupt_mask;
|
||||
}
|
||||
|
||||
VALUE rb_exc_set_backtrace(VALUE exc, VALUE bt);
|
||||
int rb_signal_buff_size(void);
|
||||
|
|
Загрузка…
Ссылка в новой задаче