summaryrefslogtreecommitdiff
path: root/vm_trace.c
diff options
context:
space:
mode:
authornormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-11-30 03:56:29 +0000
committernormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-11-30 03:56:29 +0000
commiteb38fb670bb74522f253cca440bccfd8d8d63c3c (patch)
treedce0e338bae7264c9fbeeb610b7a484f14326be9 /vm_trace.c
parentd7e4e50bdb9478c3e60ae855abcf2596991c976b (diff)
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.c58
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());
+ }
}