diff options
author | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-11-30 03:56:29 +0000 |
---|---|---|
committer | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-11-30 03:56:29 +0000 |
commit | eb38fb670bb74522f253cca440bccfd8d8d63c3c (patch) | |
tree | dce0e338bae7264c9fbeeb610b7a484f14326be9 /vm_trace.c | |
parent | d7e4e50bdb9478c3e60ae855abcf2596991c976b (diff) | |
download | ruby-eb38fb670bb74522f253cca440bccfd8d8d63c3c.tar.gz |
vm_trace.c: workqueue as thread-safe version of postponed_job
postponed_job is safe to use in signal handlers, but is not
thread-safe for MJIT. Implement a workqueue for MJIT
thread-safety.
[Bug #15316]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66100 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'vm_trace.c')
-rw-r--r-- | vm_trace.c | 58 |
1 files changed, 54 insertions, 4 deletions
diff --git a/vm_trace.c b/vm_trace.c index d6b60372b3..23bba86d29 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -1752,12 +1752,18 @@ typedef struct rb_postponed_job_struct { #define MAX_POSTPONED_JOB 1000 #define MAX_POSTPONED_JOB_SPECIAL_ADDITION 24 +struct rb_workqueue_job { + struct list_node jnode; /* <=> vm->workqueue */ + rb_postponed_job_t job; +}; + void Init_vm_postponed_job(void) { rb_vm_t *vm = GET_VM(); vm->postponed_job_buffer = ALLOC_N(rb_postponed_job_t, MAX_POSTPONED_JOB); vm->postponed_job_index = 0; + /* workqueue is initialized when VM locks are initialized */ } enum postponed_job_register_result { @@ -1766,7 +1772,7 @@ enum postponed_job_register_result { PJRR_INTERRUPTED = 2 }; -/* Async-signal-safe, thread-safe against MJIT worker thread */ +/* Async-signal-safe */ static enum postponed_job_register_result postponed_job_register(rb_execution_context_t *ec, rb_vm_t *vm, unsigned int flags, rb_postponed_job_func_t func, void *data, int max, int expected_index) @@ -1774,13 +1780,11 @@ postponed_job_register(rb_execution_context_t *ec, rb_vm_t *vm, rb_postponed_job_t *pjob; if (expected_index >= max) return PJRR_FULL; /* failed */ - if (mjit_enabled) mjit_postponed_job_register_start_hook(); if (ATOMIC_CAS(vm->postponed_job_index, expected_index, expected_index+1) == expected_index) { pjob = &vm->postponed_job_buffer[expected_index]; } else { - if (mjit_enabled) mjit_postponed_job_register_finish_hook(); return PJRR_INTERRUPTED; } @@ -1789,7 +1793,6 @@ postponed_job_register(rb_execution_context_t *ec, rb_vm_t *vm, pjob->data = data; RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(ec); - if (mjit_enabled) mjit_postponed_job_register_finish_hook(); return PJRR_SUCCESS; } @@ -1842,6 +1845,29 @@ rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, } } +/* + * thread-safe and called from non-Ruby thread + * returns FALSE on failure (ENOMEM), TRUE otherwise + */ +int +rb_workqueue_register(unsigned flags, rb_postponed_job_func_t func, void *data) +{ + struct rb_workqueue_job *wq_job = malloc(sizeof(*wq_job)); + rb_vm_t *vm = GET_VM(); + + if (!wq_job) return FALSE; + wq_job->job.func = func; + wq_job->job.data = data; + + rb_nativethread_lock_lock(&vm->workqueue_lock); + list_add_tail(&vm->workqueue, &wq_job->jnode); + rb_nativethread_lock_unlock(&vm->workqueue_lock); + + RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(GET_EC()); + + return TRUE; +} + void rb_postponed_job_flush(rb_vm_t *vm) { @@ -1849,6 +1875,13 @@ rb_postponed_job_flush(rb_vm_t *vm) const rb_atomic_t block_mask = POSTPONED_JOB_INTERRUPT_MASK|TRAP_INTERRUPT_MASK; volatile rb_atomic_t saved_mask = ec->interrupt_mask & block_mask; VALUE volatile saved_errno = ec->errinfo; + struct list_head tmp; + + list_head_init(&tmp); + + rb_nativethread_lock_lock(&vm->workqueue_lock); + list_append_list(&tmp, &vm->workqueue); + rb_nativethread_lock_unlock(&vm->workqueue_lock); ec->errinfo = Qnil; /* mask POSTPONED_JOB dispatch */ @@ -1857,16 +1890,33 @@ rb_postponed_job_flush(rb_vm_t *vm) EC_PUSH_TAG(ec); if (EC_EXEC_TAG() == TAG_NONE) { int index; + struct rb_workqueue_job *wq_job; + while ((index = vm->postponed_job_index) > 0) { if (ATOMIC_CAS(vm->postponed_job_index, index, index-1) == index) { rb_postponed_job_t *pjob = &vm->postponed_job_buffer[index-1]; (*pjob->func)(pjob->data); } } + while ((wq_job = list_pop(&tmp, struct rb_workqueue_job, jnode))) { + rb_postponed_job_t pjob = wq_job->job; + + free(wq_job); + (pjob.func)(pjob.data); + } } EC_POP_TAG(); } /* restore POSTPONED_JOB mask */ ec->interrupt_mask &= ~(saved_mask ^ block_mask); ec->errinfo = saved_errno; + + /* don't leak memory if a job threw an exception */ + if (!list_empty(&tmp)) { + rb_nativethread_lock_lock(&vm->workqueue_lock); + list_prepend_list(&vm->workqueue, &tmp); + rb_nativethread_lock_unlock(&vm->workqueue_lock); + + RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(GET_EC()); + } } |