зеркало из https://github.com/github/ruby.git
Change the semantics of rb_postponed_job_register
Our current implementation of rb_postponed_job_register suffers from some safety issues that can lead to interpreter crashes (see bug #1991). Essentially, the issue is that jobs can be called with the wrong arguments. We made two attempts to fix this whilst keeping the promised semantics, but: * The first one involved masking/unmasking when flushing jobs, which was believed to be too expensive * The second one involved a lock-free, multi-producer, single-consumer ringbuffer, which was too complex The critical insight behind this third solution is that essentially the only user of these APIs are a) internal, or b) profiling gems. For a), none of the usages actually require variable data; they will work just fine with the preregistration interface. For b), generally profiling gems only call a single callback with a single piece of data (which is actually usually just zero) for the life of the program. The ringbuffer is complex because it needs to support multi-word inserts of job & data (which can't be atomic); but nobody actually even needs that functionality, really. So, this comit: * Introduces a pre-registration API for jobs, with a GVL-requiring rb_postponed_job_prereigster, which returns a handle which can be used with an async-signal-safe rb_postponed_job_trigger. * Deprecates rb_postponed_job_register (and re-implements it on top of the preregister function for compatability) * Moves all the internal usages of postponed job register pre-registration
This commit is contained in:
Родитель
aecbd66742
Коммит
f8effa209a
51
common.mk
51
common.mk
|
@ -3002,6 +3002,7 @@ class.$(OBJEXT): {$(VPATH)}vm_debug.h
|
|||
class.$(OBJEXT): {$(VPATH)}vm_opts.h
|
||||
class.$(OBJEXT): {$(VPATH)}vm_sync.h
|
||||
compar.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
compar.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
compar.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
compar.$(OBJEXT): $(top_srcdir)/internal/compar.h
|
||||
compar.$(OBJEXT): $(top_srcdir)/internal/compilers.h
|
||||
|
@ -3185,6 +3186,7 @@ compile.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
compile.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
compile.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
compile.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
compile.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
compile.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
compile.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
compile.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
|
@ -3653,6 +3655,7 @@ cont.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
|||
cont.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
cont.$(OBJEXT): $(hdrdir)/ruby.h
|
||||
cont.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
cont.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
cont.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
cont.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
cont.$(OBJEXT): $(top_srcdir)/internal/compilers.h
|
||||
|
@ -4238,6 +4241,7 @@ dir.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
dir.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
dir.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
dir.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
dir.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
dir.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
dir.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
dir.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
|
@ -5739,14 +5743,22 @@ enc/utf_8.$(OBJEXT): {$(VPATH)}oniguruma.h
|
|||
enc/utf_8.$(OBJEXT): {$(VPATH)}regenc.h
|
||||
enc/utf_8.$(OBJEXT): {$(VPATH)}st.h
|
||||
enc/utf_8.$(OBJEXT): {$(VPATH)}subst.h
|
||||
encoding.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
|
||||
encoding.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
||||
encoding.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
encoding.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
encoding.$(OBJEXT): $(hdrdir)/ruby.h
|
||||
encoding.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
encoding.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
encoding.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
encoding.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
encoding.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
encoding.$(OBJEXT): $(top_srcdir)/internal/compilers.h
|
||||
encoding.$(OBJEXT): $(top_srcdir)/internal/enc.h
|
||||
encoding.$(OBJEXT): $(top_srcdir)/internal/encoding.h
|
||||
encoding.$(OBJEXT): $(top_srcdir)/internal/error.h
|
||||
encoding.$(OBJEXT): $(top_srcdir)/internal/gc.h
|
||||
encoding.$(OBJEXT): $(top_srcdir)/internal/imemo.h
|
||||
encoding.$(OBJEXT): $(top_srcdir)/internal/inits.h
|
||||
encoding.$(OBJEXT): $(top_srcdir)/internal/load.h
|
||||
encoding.$(OBJEXT): $(top_srcdir)/internal/object.h
|
||||
|
@ -5757,6 +5769,7 @@ encoding.$(OBJEXT): $(top_srcdir)/internal/variable.h
|
|||
encoding.$(OBJEXT): $(top_srcdir)/internal/vm.h
|
||||
encoding.$(OBJEXT): $(top_srcdir)/internal/warnings.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}assert.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}atomic.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}backward/2/assume.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}backward/2/bool.h
|
||||
|
@ -5773,6 +5786,7 @@ encoding.$(OBJEXT): {$(VPATH)}defines.h
|
|||
encoding.$(OBJEXT): {$(VPATH)}encindex.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}encoding.c
|
||||
encoding.$(OBJEXT): {$(VPATH)}encoding.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}id.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}id_table.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}intern.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}internal.h
|
||||
|
@ -5924,16 +5938,24 @@ encoding.$(OBJEXT): {$(VPATH)}internal/value_type.h
|
|||
encoding.$(OBJEXT): {$(VPATH)}internal/variable.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}internal/warning_push.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}method.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}missing.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}node.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}onigmo.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}oniguruma.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}regenc.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}ruby_assert.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}ruby_atomic.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}rubyparser.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}shape.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}st.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}subst.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
|
||||
encoding.$(OBJEXT): {$(VPATH)}thread_native.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}util.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}vm_core.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}vm_debug.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}vm_opts.h
|
||||
encoding.$(OBJEXT): {$(VPATH)}vm_sync.h
|
||||
enum.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
enum.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
|
@ -6139,6 +6161,7 @@ enumerator.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
enumerator.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
enumerator.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
enumerator.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
enumerator.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
enumerator.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
enumerator.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
enumerator.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
|
@ -6352,6 +6375,7 @@ error.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
error.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
error.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
error.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
error.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
error.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
error.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
error.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
|
@ -6567,6 +6591,7 @@ eval.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
|||
eval.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
eval.$(OBJEXT): $(hdrdir)/ruby.h
|
||||
eval.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
eval.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
eval.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
eval.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
eval.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
|
@ -6806,6 +6831,7 @@ file.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
file.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
file.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
file.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
file.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
file.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
file.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
file.$(OBJEXT): $(top_srcdir)/internal/compilers.h
|
||||
|
@ -7010,6 +7036,7 @@ gc.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
|||
gc.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
gc.$(OBJEXT): $(hdrdir)/ruby.h
|
||||
gc.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
gc.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
gc.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
gc.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
gc.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
|
@ -7457,6 +7484,7 @@ hash.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
hash.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
hash.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
hash.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
hash.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
hash.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
hash.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
hash.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
|
@ -7839,6 +7867,7 @@ io.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
io.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
io.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
io.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
io.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
io.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
io.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
io.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
|
@ -8061,6 +8090,7 @@ io_buffer.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
io_buffer.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
io_buffer.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
io_buffer.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
io_buffer.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
io_buffer.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
io_buffer.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
io_buffer.$(OBJEXT): $(top_srcdir)/internal/bits.h
|
||||
|
@ -8252,6 +8282,7 @@ iseq.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
|||
iseq.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
iseq.$(OBJEXT): $(hdrdir)/ruby.h
|
||||
iseq.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
iseq.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
iseq.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
iseq.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
iseq.$(OBJEXT): $(top_srcdir)/internal/bits.h
|
||||
|
@ -8501,6 +8532,7 @@ load.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
load.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
load.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
load.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
load.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
load.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
load.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
load.$(OBJEXT): $(top_srcdir)/internal/bits.h
|
||||
|
@ -9203,6 +9235,7 @@ marshal.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
marshal.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
marshal.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
marshal.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
marshal.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
marshal.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
marshal.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
marshal.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
|
@ -10636,6 +10669,7 @@ object.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
object.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
object.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
object.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
object.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
object.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
object.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
object.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
|
@ -11061,6 +11095,7 @@ parse.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
|||
parse.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
parse.$(OBJEXT): $(hdrdir)/ruby.h
|
||||
parse.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
parse.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
parse.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
parse.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
parse.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
|
@ -12318,6 +12353,7 @@ proc.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
proc.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
proc.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
proc.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
proc.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
proc.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
proc.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
proc.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
|
@ -12530,6 +12566,7 @@ process.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
|||
process.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
process.$(OBJEXT): $(hdrdir)/ruby.h
|
||||
process.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
process.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
|
@ -12755,6 +12792,7 @@ ractor.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
|||
ractor.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
ractor.$(OBJEXT): $(hdrdir)/ruby.h
|
||||
ractor.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
ractor.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
ractor.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
ractor.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
ractor.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
|
@ -13181,6 +13219,7 @@ random.$(OBJEXT): {$(VPATH)}thread_native.h
|
|||
random.$(OBJEXT): {$(VPATH)}vm_core.h
|
||||
random.$(OBJEXT): {$(VPATH)}vm_opts.h
|
||||
range.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
range.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
range.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
range.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
range.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
|
@ -15842,6 +15881,7 @@ shape.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
shape.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
shape.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
shape.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
shape.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
shape.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
shape.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
shape.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
|
@ -16050,6 +16090,7 @@ signal.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
signal.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
signal.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
signal.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
signal.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
signal.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
signal.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
signal.$(OBJEXT): $(top_srcdir)/internal/compilers.h
|
||||
|
@ -16255,6 +16296,7 @@ signal.$(OBJEXT): {$(VPATH)}vm_core.h
|
|||
signal.$(OBJEXT): {$(VPATH)}vm_debug.h
|
||||
signal.$(OBJEXT): {$(VPATH)}vm_opts.h
|
||||
sprintf.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
sprintf.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
sprintf.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
sprintf.$(OBJEXT): $(top_srcdir)/internal/bits.h
|
||||
sprintf.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
|
@ -16798,6 +16840,7 @@ string.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
string.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
string.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
string.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
string.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
string.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
string.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
string.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
|
@ -17050,6 +17093,7 @@ struct.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
struct.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
struct.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
struct.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
struct.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
struct.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
struct.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
struct.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
|
@ -17260,6 +17304,7 @@ symbol.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
symbol.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
symbol.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
symbol.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
symbol.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
symbol.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
symbol.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
symbol.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
|
@ -17476,6 +17521,7 @@ thread.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
|||
thread.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
thread.$(OBJEXT): $(hdrdir)/ruby.h
|
||||
thread.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
thread.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
thread.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
thread.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
thread.$(OBJEXT): $(top_srcdir)/internal/bits.h
|
||||
|
@ -18269,6 +18315,7 @@ variable.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
variable.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
variable.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
variable.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
variable.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
variable.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
variable.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
variable.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
|
@ -18691,6 +18738,7 @@ vm.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
|||
vm.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
vm.$(OBJEXT): $(hdrdir)/ruby.h
|
||||
vm.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
vm.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
vm.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
|
@ -18944,6 +18992,7 @@ vm_backtrace.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
|||
vm_backtrace.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
vm_backtrace.$(OBJEXT): $(CCAN_DIR)/str/str.h
|
||||
vm_backtrace.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
vm_backtrace.$(OBJEXT): $(hdrdir)/ruby/version.h
|
||||
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
vm_backtrace.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
|
@ -19562,6 +19611,7 @@ vm_trace.$(OBJEXT): $(hdrdir)/ruby.h
|
|||
vm_trace.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
vm_trace.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
vm_trace.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
|
||||
vm_trace.$(OBJEXT): $(top_srcdir)/internal/bits.h
|
||||
vm_trace.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
vm_trace.$(OBJEXT): $(top_srcdir)/internal/compilers.h
|
||||
vm_trace.$(OBJEXT): $(top_srcdir)/internal/gc.h
|
||||
|
@ -19570,6 +19620,7 @@ vm_trace.$(OBJEXT): $(top_srcdir)/internal/imemo.h
|
|||
vm_trace.$(OBJEXT): $(top_srcdir)/internal/serial.h
|
||||
vm_trace.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
|
||||
vm_trace.$(OBJEXT): $(top_srcdir)/internal/symbol.h
|
||||
vm_trace.$(OBJEXT): $(top_srcdir)/internal/thread.h
|
||||
vm_trace.$(OBJEXT): $(top_srcdir)/internal/variable.h
|
||||
vm_trace.$(OBJEXT): $(top_srcdir)/internal/vm.h
|
||||
vm_trace.$(OBJEXT): $(top_srcdir)/internal/warnings.h
|
||||
|
|
|
@ -1,6 +1,29 @@
|
|||
#include "ruby.h"
|
||||
#include "ruby/debug.h"
|
||||
|
||||
// We're testing deprecated things, don't print the compiler warnings
|
||||
#if 0
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(disable : 4996)
|
||||
|
||||
#elif defined(__INTEL_COMPILER)
|
||||
#pragma warning(disable : 1786)
|
||||
|
||||
#elif defined(__clang__)
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
#elif defined(__SUNPRO_CC)
|
||||
#pragma error_messages (off,symdeprecated)
|
||||
|
||||
#else
|
||||
// :FIXME: improve here for your compiler.
|
||||
|
||||
#endif
|
||||
|
||||
static int counter;
|
||||
|
||||
static void
|
||||
|
@ -58,6 +81,22 @@ pjob_call_direct(VALUE self, VALUE obj)
|
|||
return self;
|
||||
}
|
||||
|
||||
static void pjob_noop_callback(void *data) { }
|
||||
|
||||
static VALUE
|
||||
pjob_register_one_same(VALUE self)
|
||||
{
|
||||
rb_gc_start();
|
||||
int r1 = rb_postponed_job_register_one(0, pjob_noop_callback, NULL);
|
||||
int r2 = rb_postponed_job_register_one(0, pjob_noop_callback, NULL);
|
||||
int r3 = rb_postponed_job_register_one(0, pjob_noop_callback, NULL);
|
||||
VALUE ary = rb_ary_new();
|
||||
rb_ary_push(ary, INT2FIX(r1));
|
||||
rb_ary_push(ary, INT2FIX(r2));
|
||||
rb_ary_push(ary, INT2FIX(r3));
|
||||
return ary;
|
||||
}
|
||||
|
||||
#ifdef HAVE_PTHREAD_H
|
||||
#include <pthread.h>
|
||||
|
||||
|
@ -86,6 +125,57 @@ pjob_register_in_c_thread(VALUE self, VALUE obj)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
pjob_preregistered_callback(void *data)
|
||||
{
|
||||
VALUE ary = (VALUE)data;
|
||||
Check_Type(ary, T_ARRAY);
|
||||
rb_ary_push(ary, INT2FIX(counter));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
pjob_preregister_and_call_with_sleep(VALUE self, VALUE obj)
|
||||
{
|
||||
counter = 0;
|
||||
rb_postponed_job_handle_t h = rb_postponed_job_preregister(pjob_preregistered_callback, (void *)obj);
|
||||
counter++;
|
||||
rb_postponed_job_trigger(h);
|
||||
rb_thread_sleep(0);
|
||||
counter++;
|
||||
rb_postponed_job_trigger(h);
|
||||
rb_thread_sleep(0);
|
||||
counter++;
|
||||
rb_postponed_job_trigger(h);
|
||||
rb_thread_sleep(0);
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
pjob_preregister_and_call_without_sleep(VALUE self, VALUE obj)
|
||||
{
|
||||
counter = 0;
|
||||
rb_postponed_job_handle_t h = rb_postponed_job_preregister(pjob_preregistered_callback, (void *)obj);
|
||||
counter = 3;
|
||||
rb_postponed_job_trigger(h);
|
||||
rb_postponed_job_trigger(h);
|
||||
rb_postponed_job_trigger(h);
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
pjob_preregister_multiple_times(VALUE self)
|
||||
{
|
||||
int r1 = rb_postponed_job_preregister(pjob_noop_callback, NULL);
|
||||
int r2 = rb_postponed_job_preregister(pjob_noop_callback, NULL);
|
||||
int r3 = rb_postponed_job_preregister(pjob_noop_callback, NULL);
|
||||
VALUE ary = rb_ary_new();
|
||||
rb_ary_push(ary, INT2FIX(r1));
|
||||
rb_ary_push(ary, INT2FIX(r2));
|
||||
rb_ary_push(ary, INT2FIX(r3));
|
||||
return ary;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
Init_postponed_job(VALUE self)
|
||||
{
|
||||
|
@ -93,8 +183,12 @@ Init_postponed_job(VALUE self)
|
|||
rb_define_module_function(mBug, "postponed_job_register", pjob_register, 1);
|
||||
rb_define_module_function(mBug, "postponed_job_register_one", pjob_register_one, 1);
|
||||
rb_define_module_function(mBug, "postponed_job_call_direct", pjob_call_direct, 1);
|
||||
rb_define_module_function(mBug, "postponed_job_register_one_same", pjob_register_one_same, 0);
|
||||
#ifdef HAVE_PTHREAD_H
|
||||
rb_define_module_function(mBug, "postponed_job_register_in_c_thread", pjob_register_in_c_thread, 1);
|
||||
#endif
|
||||
rb_define_module_function(mBug, "postponed_job_preregister_and_call_with_sleep", pjob_preregister_and_call_with_sleep, 1);
|
||||
rb_define_module_function(mBug, "postponed_job_preregister_and_call_without_sleep", pjob_preregister_and_call_without_sleep, 1);
|
||||
rb_define_module_function(mBug, "postponed_job_preregister_multiple_times", pjob_preregister_multiple_times, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
#include "ruby/debug.h"
|
||||
|
||||
static int invoking; /* TODO: should not be global variable */
|
||||
static VALUE gc_start_proc;
|
||||
static VALUE gc_end_proc;
|
||||
static rb_postponed_job_handle_t invoking_proc_pjob;
|
||||
static bool pjob_execute_gc_start_proc_p;
|
||||
static bool pjob_execute_gc_end_proc_p;
|
||||
|
||||
static VALUE
|
||||
invoke_proc_ensure(VALUE _)
|
||||
|
@ -17,23 +22,35 @@ invoke_proc_begin(VALUE proc)
|
|||
}
|
||||
|
||||
static void
|
||||
invoke_proc(void *data)
|
||||
invoke_proc(void *unused)
|
||||
{
|
||||
VALUE proc = (VALUE)data;
|
||||
invoking += 1;
|
||||
rb_ensure(invoke_proc_begin, proc, invoke_proc_ensure, 0);
|
||||
if (pjob_execute_gc_start_proc_p) {
|
||||
pjob_execute_gc_start_proc_p = false;
|
||||
invoking += 1;
|
||||
rb_ensure(invoke_proc_begin, gc_start_proc, invoke_proc_ensure, 0);
|
||||
}
|
||||
if (pjob_execute_gc_end_proc_p) {
|
||||
pjob_execute_gc_end_proc_p = false;
|
||||
invoking += 1;
|
||||
rb_ensure(invoke_proc_begin, gc_end_proc, invoke_proc_ensure, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gc_start_end_i(VALUE tpval, void *data)
|
||||
{
|
||||
rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
|
||||
if (0) {
|
||||
rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
|
||||
fprintf(stderr, "trace: %s\n", rb_tracearg_event_flag(tparg) == RUBY_INTERNAL_EVENT_GC_START ? "gc_start" : "gc_end");
|
||||
}
|
||||
|
||||
if (invoking == 0) {
|
||||
rb_postponed_job_register(0, invoke_proc, data);
|
||||
if (rb_tracearg_event_flag(tparg) == RUBY_INTERNAL_EVENT_GC_START) {
|
||||
pjob_execute_gc_start_proc_p = true;
|
||||
} else {
|
||||
pjob_execute_gc_end_proc_p = true;
|
||||
}
|
||||
rb_postponed_job_trigger(invoking_proc_pjob);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,8 +71,13 @@ set_gc_hook(VALUE module, VALUE proc, rb_event_flag_t event, const char *tp_str,
|
|||
if (!rb_obj_is_proc(proc)) {
|
||||
rb_raise(rb_eTypeError, "trace_func needs to be Proc");
|
||||
}
|
||||
if (event == RUBY_INTERNAL_EVENT_GC_START) {
|
||||
gc_start_proc = proc;
|
||||
} else {
|
||||
gc_end_proc = proc;
|
||||
}
|
||||
|
||||
tpval = rb_tracepoint_new(0, event, gc_start_end_i, (void *)proc);
|
||||
tpval = rb_tracepoint_new(0, event, gc_start_end_i, 0);
|
||||
rb_ivar_set(module, tp_key, tpval);
|
||||
rb_tracepoint_enable(tpval);
|
||||
}
|
||||
|
@ -82,4 +104,10 @@ Init_gc_hook(VALUE module)
|
|||
{
|
||||
rb_define_module_function(module, "after_gc_start_hook=", set_after_gc_start, 1);
|
||||
rb_define_module_function(module, "after_gc_exit_hook=", start_after_gc_exit, 1);
|
||||
rb_gc_register_address(&gc_start_proc);
|
||||
rb_gc_register_address(&gc_end_proc);
|
||||
invoking_proc_pjob = rb_postponed_job_preregister(invoke_proc, NULL);
|
||||
if (invoking_proc_pjob == POSTPONED_JOB_HANDLE_INVALID) {
|
||||
rb_raise(rb_eStandardError, "could not preregister invoke_proc");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -566,6 +566,7 @@ ripper.o: $(hdrdir)/ruby/st.h
|
|||
ripper.o: $(hdrdir)/ruby/subst.h
|
||||
ripper.o: $(hdrdir)/ruby/thread_native.h
|
||||
ripper.o: $(hdrdir)/ruby/util.h
|
||||
ripper.o: $(hdrdir)/ruby/version.h
|
||||
ripper.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
ripper.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
ripper.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
|
|
@ -186,6 +186,7 @@ ancdata.o: $(hdrdir)/ruby/subst.h
|
|||
ancdata.o: $(hdrdir)/ruby/thread.h
|
||||
ancdata.o: $(hdrdir)/ruby/thread_native.h
|
||||
ancdata.o: $(hdrdir)/ruby/util.h
|
||||
ancdata.o: $(hdrdir)/ruby/version.h
|
||||
ancdata.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
ancdata.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
ancdata.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -394,6 +395,7 @@ basicsocket.o: $(hdrdir)/ruby/subst.h
|
|||
basicsocket.o: $(hdrdir)/ruby/thread.h
|
||||
basicsocket.o: $(hdrdir)/ruby/thread_native.h
|
||||
basicsocket.o: $(hdrdir)/ruby/util.h
|
||||
basicsocket.o: $(hdrdir)/ruby/version.h
|
||||
basicsocket.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
basicsocket.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
basicsocket.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -602,6 +604,7 @@ constants.o: $(hdrdir)/ruby/subst.h
|
|||
constants.o: $(hdrdir)/ruby/thread.h
|
||||
constants.o: $(hdrdir)/ruby/thread_native.h
|
||||
constants.o: $(hdrdir)/ruby/util.h
|
||||
constants.o: $(hdrdir)/ruby/version.h
|
||||
constants.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
constants.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
constants.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -811,6 +814,7 @@ ifaddr.o: $(hdrdir)/ruby/subst.h
|
|||
ifaddr.o: $(hdrdir)/ruby/thread.h
|
||||
ifaddr.o: $(hdrdir)/ruby/thread_native.h
|
||||
ifaddr.o: $(hdrdir)/ruby/util.h
|
||||
ifaddr.o: $(hdrdir)/ruby/version.h
|
||||
ifaddr.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
ifaddr.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
ifaddr.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -1019,6 +1023,7 @@ init.o: $(hdrdir)/ruby/subst.h
|
|||
init.o: $(hdrdir)/ruby/thread.h
|
||||
init.o: $(hdrdir)/ruby/thread_native.h
|
||||
init.o: $(hdrdir)/ruby/util.h
|
||||
init.o: $(hdrdir)/ruby/version.h
|
||||
init.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
init.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
init.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -1227,6 +1232,7 @@ ipsocket.o: $(hdrdir)/ruby/subst.h
|
|||
ipsocket.o: $(hdrdir)/ruby/thread.h
|
||||
ipsocket.o: $(hdrdir)/ruby/thread_native.h
|
||||
ipsocket.o: $(hdrdir)/ruby/util.h
|
||||
ipsocket.o: $(hdrdir)/ruby/version.h
|
||||
ipsocket.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
ipsocket.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
ipsocket.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -1435,6 +1441,7 @@ option.o: $(hdrdir)/ruby/subst.h
|
|||
option.o: $(hdrdir)/ruby/thread.h
|
||||
option.o: $(hdrdir)/ruby/thread_native.h
|
||||
option.o: $(hdrdir)/ruby/util.h
|
||||
option.o: $(hdrdir)/ruby/version.h
|
||||
option.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
option.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
option.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -1643,6 +1650,7 @@ raddrinfo.o: $(hdrdir)/ruby/subst.h
|
|||
raddrinfo.o: $(hdrdir)/ruby/thread.h
|
||||
raddrinfo.o: $(hdrdir)/ruby/thread_native.h
|
||||
raddrinfo.o: $(hdrdir)/ruby/util.h
|
||||
raddrinfo.o: $(hdrdir)/ruby/version.h
|
||||
raddrinfo.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
raddrinfo.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
raddrinfo.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -1851,6 +1859,7 @@ socket.o: $(hdrdir)/ruby/subst.h
|
|||
socket.o: $(hdrdir)/ruby/thread.h
|
||||
socket.o: $(hdrdir)/ruby/thread_native.h
|
||||
socket.o: $(hdrdir)/ruby/util.h
|
||||
socket.o: $(hdrdir)/ruby/version.h
|
||||
socket.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
socket.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
socket.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -2059,6 +2068,7 @@ sockssocket.o: $(hdrdir)/ruby/subst.h
|
|||
sockssocket.o: $(hdrdir)/ruby/thread.h
|
||||
sockssocket.o: $(hdrdir)/ruby/thread_native.h
|
||||
sockssocket.o: $(hdrdir)/ruby/util.h
|
||||
sockssocket.o: $(hdrdir)/ruby/version.h
|
||||
sockssocket.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
sockssocket.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
sockssocket.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -2267,6 +2277,7 @@ tcpserver.o: $(hdrdir)/ruby/subst.h
|
|||
tcpserver.o: $(hdrdir)/ruby/thread.h
|
||||
tcpserver.o: $(hdrdir)/ruby/thread_native.h
|
||||
tcpserver.o: $(hdrdir)/ruby/util.h
|
||||
tcpserver.o: $(hdrdir)/ruby/version.h
|
||||
tcpserver.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
tcpserver.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
tcpserver.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -2475,6 +2486,7 @@ tcpsocket.o: $(hdrdir)/ruby/subst.h
|
|||
tcpsocket.o: $(hdrdir)/ruby/thread.h
|
||||
tcpsocket.o: $(hdrdir)/ruby/thread_native.h
|
||||
tcpsocket.o: $(hdrdir)/ruby/util.h
|
||||
tcpsocket.o: $(hdrdir)/ruby/version.h
|
||||
tcpsocket.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
tcpsocket.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
tcpsocket.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -2683,6 +2695,7 @@ udpsocket.o: $(hdrdir)/ruby/subst.h
|
|||
udpsocket.o: $(hdrdir)/ruby/thread.h
|
||||
udpsocket.o: $(hdrdir)/ruby/thread_native.h
|
||||
udpsocket.o: $(hdrdir)/ruby/util.h
|
||||
udpsocket.o: $(hdrdir)/ruby/version.h
|
||||
udpsocket.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
udpsocket.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
udpsocket.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -2891,6 +2904,7 @@ unixserver.o: $(hdrdir)/ruby/subst.h
|
|||
unixserver.o: $(hdrdir)/ruby/thread.h
|
||||
unixserver.o: $(hdrdir)/ruby/thread_native.h
|
||||
unixserver.o: $(hdrdir)/ruby/util.h
|
||||
unixserver.o: $(hdrdir)/ruby/version.h
|
||||
unixserver.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
unixserver.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
unixserver.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
@ -3099,6 +3113,7 @@ unixsocket.o: $(hdrdir)/ruby/subst.h
|
|||
unixsocket.o: $(hdrdir)/ruby/thread.h
|
||||
unixsocket.o: $(hdrdir)/ruby/thread_native.h
|
||||
unixsocket.o: $(hdrdir)/ruby/util.h
|
||||
unixsocket.o: $(hdrdir)/ruby/version.h
|
||||
unixsocket.o: $(top_srcdir)/ccan/check_type/check_type.h
|
||||
unixsocket.o: $(top_srcdir)/ccan/container_of/container_of.h
|
||||
unixsocket.o: $(top_srcdir)/ccan/list/list.h
|
||||
|
|
12
gc.c
12
gc.c
|
@ -952,6 +952,7 @@ typedef struct rb_objspace {
|
|||
#endif
|
||||
|
||||
rb_darray(VALUE *) weak_references;
|
||||
rb_postponed_job_handle_t finalize_deferred_pjob;
|
||||
} rb_objspace_t;
|
||||
|
||||
|
||||
|
@ -1425,6 +1426,8 @@ PRINTF_ARGS(static void gc_report_body(int level, rb_objspace_t *objspace, const
|
|||
static const char *obj_info(VALUE obj);
|
||||
static const char *obj_type_name(VALUE obj);
|
||||
|
||||
static void gc_finalize_deferred(void *dmy);
|
||||
|
||||
/*
|
||||
* 1 - TSC (H/W Time Stamp Counter)
|
||||
* 2 - getrusage
|
||||
|
@ -1906,6 +1909,10 @@ rb_objspace_alloc(void)
|
|||
rb_objspace_t *objspace = calloc1(sizeof(rb_objspace_t));
|
||||
objspace->flags.measure_gc = 1;
|
||||
malloc_limit = gc_params.malloc_limit_min;
|
||||
objspace->finalize_deferred_pjob = rb_postponed_job_preregister(gc_finalize_deferred, objspace);
|
||||
if (objspace->finalize_deferred_pjob == POSTPONED_JOB_HANDLE_INVALID) {
|
||||
rb_bug("Could not preregister postponed job for GC");
|
||||
}
|
||||
|
||||
for (int i = 0; i < SIZE_POOL_COUNT; i++) {
|
||||
rb_size_pool_t *size_pool = &size_pools[i];
|
||||
|
@ -4527,9 +4534,8 @@ gc_finalize_deferred(void *dmy)
|
|||
static void
|
||||
gc_finalize_deferred_register(rb_objspace_t *objspace)
|
||||
{
|
||||
if (rb_postponed_job_register_one(0, gc_finalize_deferred, objspace) == 0) {
|
||||
rb_bug("gc_finalize_deferred_register: can't register finalizer.");
|
||||
}
|
||||
/* will enqueue a call to gc_finalize_deferred */
|
||||
rb_postponed_job_trigger(objspace->finalize_deferred_pjob);
|
||||
}
|
||||
|
||||
static int pop_mark_stack(mark_stack_t *stack, VALUE *data);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* modify this file, provided that the conditions mentioned in the
|
||||
* file COPYING are met. Consult the file for details.
|
||||
*/
|
||||
#include "ruby/internal/attr/deprecated.h"
|
||||
#include "ruby/internal/attr/nonnull.h"
|
||||
#include "ruby/internal/attr/returns_nonnull.h"
|
||||
#include "ruby/internal/dllexport.h"
|
||||
|
@ -615,48 +616,151 @@ VALUE rb_tracearg_object(rb_trace_arg_t *trace_arg);
|
|||
|
||||
/*
|
||||
* Postponed Job API
|
||||
* rb_postponed_job_register and rb_postponed_job_register_one are
|
||||
* async-signal-safe and used via SIGPROF by the "stackprof" RubyGem
|
||||
*
|
||||
* This API is designed to be called from contexts where it is not safe to run Ruby
|
||||
* code (e.g. because they do not hold the GVL or because GC is in progress), and
|
||||
* defer a callback to run in a context where it _is_ safe. The primary intended
|
||||
* users of this API is for sampling profilers like the "stackprof" gem; these work
|
||||
* by scheduling the periodic delivery of a SIGPROF signal, and inside the C-level
|
||||
* signal handler, deferring a job to collect a Ruby backtrace when it is next safe
|
||||
* to do so.
|
||||
*
|
||||
* Historically, this API provided two functions `rb_postponed_job_register` and
|
||||
* `rb_postponed_job_register_one`, which claimed to be fully async-signal-safe and
|
||||
* would call back the provided `func` and `data` at an appropriate time. However,
|
||||
* these functions were subject to race conditions which could cause crashes when
|
||||
* racing with Ruby's internal use of them.
|
||||
*
|
||||
* Therefore, this API has now been changed, and now requires that jobs scheduled
|
||||
* from a signal handler context are pre-registered in advance into a fixed-size
|
||||
* table. This table is quite small (it only has 32 entries on most systems)
|
||||
* and so gems should generally only preregister one or two funcs. This process is
|
||||
* managed by the `rb_postponed_job_preregister` and `rb_postponed_job_trigger`
|
||||
* functions.
|
||||
*
|
||||
* We also provide the old `rb_postponed_job_register` and
|
||||
* `rb_postponed_job_register_one` functions for backwards compatability, but with
|
||||
* changed semantics; `rb_postponed_job_register` now behaves the same as
|
||||
* `rb_postponed_job_register_once`. These changes should remain compatible with all
|
||||
* of the observed in-the-wild usages of the postponed job APIs, which almost all
|
||||
* use the _one API and pass `0` for data anyway.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Type of postponed jobs.
|
||||
*
|
||||
* @param[in,out] arg What was passed to rb_postponed_job_register().
|
||||
* @param[in,out] arg What was passed to `rb_postponed_job_preregister`
|
||||
*/
|
||||
typedef void (*rb_postponed_job_func_t)(void *arg);
|
||||
|
||||
/**
|
||||
* Registers a postponed job.
|
||||
* The type of a handle returned from `rb_postponed_job_preregister` and
|
||||
* passed to `rb_postponed_job_trigger`
|
||||
*/
|
||||
typedef unsigned int rb_postponed_job_handle_t;
|
||||
#define POSTPONED_JOB_HANDLE_INVALID ((rb_postponed_job_handle_t)UINT_MAX)
|
||||
|
||||
/**
|
||||
* Pre-registers a func in Ruby's postponed job preregistration table,
|
||||
* returning an opaque handle which can be used to trigger the job later. Generally,
|
||||
* this function will be called during the initialization routine of an extension.
|
||||
*
|
||||
* There are situations when running a ruby program is not possible. For
|
||||
* instance when a program is in a signal handler; for another instance when
|
||||
* the GC is busy. On such situations however, there might be needs to do
|
||||
* something. We cannot but defer such operations until we are 100% sure it is
|
||||
* safe to execute them. This mechanism is called postponed jobs. This
|
||||
* function registers a new one. The registered job would eventually gets
|
||||
* executed.
|
||||
* The returned handle can be used later to call `rb_postponed_job_trigger`. This will
|
||||
* cause Ruby to call back into the registered `func` with `data` at a later time, in
|
||||
* a context where the GVL is held and it is safe to perform Ruby allocations.
|
||||
*
|
||||
* @param[in] flags (Unused) reserved for future extensions.
|
||||
* If the given func was already pre-registered, this method will overwrite the
|
||||
* stored data with the newly passed data, and return the same handle instance as
|
||||
* was previously returned.
|
||||
*
|
||||
* If this function is called concurrently with the same `func`, then the stored data
|
||||
* could be the value from either call (but will definitely be one of them).
|
||||
*
|
||||
* If this function is called to update the data concurrently with a call to
|
||||
* `rb_postponed_job_trigger` on the same handle, it's undefined whether `func` will
|
||||
* be called with the old data or the new data.
|
||||
*
|
||||
* Although the current implementation of this method is in fact async-signal-safe and
|
||||
* has defined semantics when called concurrently on the same `func`, a future Ruby
|
||||
* version might require that this method be called under the GVL; thus, programs which
|
||||
* aim to be forward-compatible should call this method whilst holding the GVL.
|
||||
*
|
||||
* @param[in] func The function to be pre-registered
|
||||
* @param[in] data The data to be pre-registered
|
||||
* @retval POSTPONED_JOB_HANDLE_INVALID The job table is full; this registration
|
||||
* did not succeed and no further registration will do so for
|
||||
* the lifetime of the program.
|
||||
* @retval otherwise A handle which can be passed to `rb_postponed_job_trigger`
|
||||
*/
|
||||
rb_postponed_job_handle_t rb_postponed_job_preregister(rb_postponed_job_func_t func, void *data);
|
||||
|
||||
/**
|
||||
* Triggers a pre-registered job registered with rb_postponed_job_preregister,
|
||||
* scheduling it for execution the next time the Ruby VM checks for interrupts.
|
||||
* The context in which the job is called in holds the GVL and is safe to perform
|
||||
* Ruby allocations within (i.e. it is not during GC).
|
||||
*
|
||||
* This method is async-signal-safe and can be called from any thread, at any
|
||||
* time, including in signal handlers.
|
||||
*
|
||||
* If this method is called multiple times, Ruby will coalesce this into only
|
||||
* one call to the job the next time it checks for interrupts.
|
||||
*
|
||||
* @params[in] h A handle returned from rb_postponed_job_preregister
|
||||
*/
|
||||
void rb_postponed_job_trigger(rb_postponed_job_handle_t h);
|
||||
|
||||
/**
|
||||
* Schedules the given `func` to be called with `data` when Ruby next checks for
|
||||
* interupts. If this function is called multiple times in between Ruby checking
|
||||
* for interrupts, then `func` will be called only once with the `data` vlaue from
|
||||
* the first call to this function.
|
||||
*
|
||||
* Like `rb_postponed_job_trigger`, the context in which the job is called
|
||||
* holds the GVL and can allocate Ruby objects.
|
||||
*
|
||||
* This method essentially has the same semantics as:
|
||||
*
|
||||
* ```
|
||||
* rb_postponed_job_trigger(rb_postponed_job_preregister(func, data));
|
||||
* ```
|
||||
*
|
||||
* @note Prevoius versions of Ruby promised that the (`func`, `data`) pairs would
|
||||
* be executed as many times as they were registered with this function; in
|
||||
* reality this was always subject to race conditions and this function no
|
||||
* longer provides this guarantee. Instead, we only promise that `func` will
|
||||
* be called once.
|
||||
*
|
||||
* @deprecated This interface implies that arbitrarily many `func`'s can be enqueued
|
||||
* over the lifetime of the program, whilst in reality the registration
|
||||
* slots for postponed jobs are a finite resource. This is made clearer
|
||||
* by the `rb_postponed_job_preregister` and `rb_postponed_job_trigger`
|
||||
* functions, and a future version of Ruby might delete this function.
|
||||
*
|
||||
* @param[in] flags Unused and ignored.
|
||||
* @param[in] func Job body.
|
||||
* @param[in,out] data Passed as-is to `func`.
|
||||
* @retval 0 Postponed job buffer is full. Failed.
|
||||
* @retval otherwise Opaque return value.
|
||||
* @post The passed job is postponed.
|
||||
* @retval 0 Postponed job registration table is full. Failed.
|
||||
* @retval 1 Registration succeeded.
|
||||
* @post The passed job will run on the next interrupt check.
|
||||
*/
|
||||
RBIMPL_ATTR_DEPRECATED(("use rb_postponed_job_preregister and rb_postponed_job_trigger"))
|
||||
int rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void *data);
|
||||
|
||||
/**
|
||||
* Identical to rb_postponed_job_register(), except it additionally checks for
|
||||
* duplicated registration. In case the passed job is already in the postponed
|
||||
* job buffer this function does nothing.
|
||||
* Identical to `rb_postponed_job_register`
|
||||
*
|
||||
* @param[in] flags (Unused) reserved for future extensions.
|
||||
* @deprecated This is deprecated for the same reason as `rb_postponed_job_register`
|
||||
*
|
||||
* @param[in] flags Unused and ignored.
|
||||
* @param[in] func Job body.
|
||||
* @param[in,out] data Passed as-is to `func`.
|
||||
* @retval 0 Postponed job buffer is full. Failed.
|
||||
* @retval otherwise Opaque return value.
|
||||
* @retval 0 Postponed job registration table is full. Failed.
|
||||
* @retval 1 Registration succeeded.
|
||||
* @post The passed job will run on the next interrupt check.
|
||||
*/
|
||||
RBIMPL_ATTR_DEPRECATED(("use rb_postponed_job_preregister and rb_postponed_job_trigger"))
|
||||
int rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data);
|
||||
|
||||
/** @} */
|
||||
|
|
1
inits.c
1
inits.c
|
@ -22,7 +22,6 @@ rb_call_inits(void)
|
|||
{
|
||||
CALL(default_shapes);
|
||||
CALL(Thread_Mutex);
|
||||
CALL(vm_postponed_job);
|
||||
CALL(RandomSeedCore);
|
||||
CALL(encodings);
|
||||
CALL(sym);
|
||||
|
|
9
rjit.c
9
rjit.c
|
@ -101,6 +101,9 @@ VALUE rb_rjit_raw_samples = 0;
|
|||
// Line numbers for --rjit-trace-exits
|
||||
VALUE rb_rjit_line_samples = 0;
|
||||
|
||||
// Postponed job handle for triggering rjit_iseq_update_references
|
||||
static rb_postponed_job_handle_t rjit_iseq_update_references_pjob;
|
||||
|
||||
// A default threshold used to add iseq to JIT.
|
||||
#define DEFAULT_CALL_THRESHOLD 10
|
||||
// Size of executable memory block in MiB.
|
||||
|
@ -301,7 +304,7 @@ rb_rjit_iseq_update_references(struct rb_iseq_constant_body *const body)
|
|||
// Asynchronously hook the Ruby code to avoid allocation during GC.compact.
|
||||
// Using _one because it's too slow to invalidate all for each ISEQ. Thus
|
||||
// not giving an ISEQ pointer.
|
||||
rb_postponed_job_register_one(0, rjit_iseq_update_references, NULL);
|
||||
rb_postponed_job_trigger(rjit_iseq_update_references_pjob);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -429,6 +432,10 @@ rb_rjit_init(const struct rb_rjit_options *opts)
|
|||
rb_rjit_enabled = false;
|
||||
return;
|
||||
}
|
||||
rjit_iseq_update_references_pjob = rb_postponed_job_preregister(rjit_iseq_update_references, NULL);
|
||||
if (rjit_iseq_update_references_pjob == POSTPONED_JOB_HANDLE_INVALID) {
|
||||
rb_bug("Could not preregister postponed job for RJIT");
|
||||
}
|
||||
rb_mRJITC = rb_const_get(rb_mRJIT, rb_intern("C"));
|
||||
VALUE rb_cRJITCompiler = rb_const_get(rb_mRJIT, rb_intern("Compiler"));
|
||||
rb_RJITCompiler = rb_funcall(rb_cRJITCompiler, rb_intern("new"), 0);
|
||||
|
|
|
@ -2,34 +2,62 @@
|
|||
require 'test/unit'
|
||||
require '-test-/postponed_job'
|
||||
|
||||
module Bug
|
||||
def self.postponed_job_call_direct_wrapper(*args)
|
||||
postponed_job_call_direct(*args)
|
||||
end
|
||||
|
||||
def self.postponed_job_register_wrapper(*args)
|
||||
postponed_job_register(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class TestPostponed_job < Test::Unit::TestCase
|
||||
def test_register
|
||||
direct, registered = [], []
|
||||
def test_preregister_and_trigger
|
||||
assert_separately([], __FILE__, __LINE__, <<-'RUBY')
|
||||
require '-test-/postponed_job'
|
||||
Bug.postponed_job_preregister_and_call_without_sleep(counters = [])
|
||||
# i.e. rb_postponed_job_trigger performs coalescing
|
||||
assert_equal([3], counters)
|
||||
|
||||
Bug.postponed_job_call_direct_wrapper(direct)
|
||||
Bug.postponed_job_register_wrapper(registered)
|
||||
# i.e. rb_postponed_job_trigger resets after interrupts are checked
|
||||
Bug.postponed_job_preregister_and_call_with_sleep(counters = [])
|
||||
assert_equal([1, 2, 3], counters)
|
||||
RUBY
|
||||
end
|
||||
|
||||
assert_equal([0], direct)
|
||||
assert_equal([3], registered)
|
||||
def test_multiple_preregistration
|
||||
assert_separately([], __FILE__, __LINE__, <<-'RUBY')
|
||||
require '-test-/postponed_job'
|
||||
handles = Bug.postponed_job_preregister_multiple_times
|
||||
# i.e. rb_postponed_job_preregister returns the same handle if preregistered multiple times
|
||||
assert_equal [handles[0]], handles.uniq
|
||||
RUBY
|
||||
end
|
||||
|
||||
Bug.postponed_job_register_one(ary = [])
|
||||
assert_equal [1], ary
|
||||
|
||||
def test_legacy_register
|
||||
assert_separately([], __FILE__, __LINE__, <<-'RUBY')
|
||||
require '-test-/postponed_job'
|
||||
direct, registered = [], []
|
||||
|
||||
Bug.postponed_job_call_direct(direct)
|
||||
Bug.postponed_job_register(registered)
|
||||
|
||||
assert_equal([0], direct)
|
||||
assert_equal([3], registered)
|
||||
|
||||
Bug.postponed_job_register_one(ary = [])
|
||||
assert_equal [1], ary
|
||||
RUBY
|
||||
end
|
||||
|
||||
def test_legacy_register_one_same
|
||||
assert_separately([], __FILE__, __LINE__, <<-'RUBY')
|
||||
require '-test-/postponed_job'
|
||||
# Registering the same job three times should result in three of the same handle
|
||||
handles = Bug.postponed_job_register_one_same
|
||||
assert_equal [handles[0]], handles.uniq
|
||||
RUBY
|
||||
end
|
||||
|
||||
if Bug.respond_to?(:postponed_job_register_in_c_thread)
|
||||
def test_register_in_c_thread
|
||||
assert Bug.postponed_job_register_in_c_thread(ary = [])
|
||||
assert_equal [1], ary
|
||||
def test_legacy_register_in_c_thread
|
||||
assert_separately([], __FILE__, __LINE__, <<-'RUBY')
|
||||
require '-test-/postponed_job'
|
||||
assert Bug.postponed_job_register_in_c_thread(ary = [])
|
||||
assert_equal [1], ary
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
1
thread.c
1
thread.c
|
@ -4626,6 +4626,7 @@ rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const r
|
|||
rb_vm_living_threads_init(vm);
|
||||
|
||||
rb_ractor_atfork(vm, th);
|
||||
rb_vm_postponed_job_atfork();
|
||||
|
||||
/* may be held by RJIT threads in parent */
|
||||
rb_native_mutex_initialize(&vm->workqueue_lock);
|
||||
|
|
8
vm.c
8
vm.c
|
@ -3029,7 +3029,7 @@ ruby_vm_destruct(rb_vm_t *vm)
|
|||
st_free_table(vm->static_ext_inits);
|
||||
st_free_table(vm->ensure_rollback_table);
|
||||
|
||||
ruby_xfree(vm->postponed_job_buffer);
|
||||
rb_vm_postponed_job_free();
|
||||
st_free_table(vm->defined_module_hash);
|
||||
|
||||
rb_id_table_free(vm->constant_cache);
|
||||
|
@ -3077,7 +3077,6 @@ ruby_vm_destruct(rb_vm_t *vm)
|
|||
}
|
||||
|
||||
size_t rb_vm_memsize_waiting_fds(struct ccan_list_head *waiting_fds); // thread.c
|
||||
size_t rb_vm_memsize_postponed_job_buffer(void); // vm_trace.c
|
||||
size_t rb_vm_memsize_workqueue(struct ccan_list_head *workqueue); // vm_trace.c
|
||||
|
||||
// Used for VM memsize reporting. Returns the size of the at_exit list by
|
||||
|
@ -3136,7 +3135,7 @@ vm_memsize(const void *ptr)
|
|||
rb_st_memsize(vm->loaded_features_index) +
|
||||
rb_st_memsize(vm->loading_table) +
|
||||
rb_st_memsize(vm->ensure_rollback_table) +
|
||||
rb_vm_memsize_postponed_job_buffer() +
|
||||
rb_vm_memsize_postponed_job_queue() +
|
||||
rb_vm_memsize_workqueue(&vm->workqueue) +
|
||||
rb_st_memsize(vm->defined_module_hash) +
|
||||
vm_memsize_at_exit_list(vm->at_exit) +
|
||||
|
@ -4180,8 +4179,9 @@ Init_BareVM(void)
|
|||
MEMZERO(th, rb_thread_t, 1);
|
||||
vm_init2(vm);
|
||||
|
||||
vm->objspace = rb_objspace_alloc();
|
||||
rb_vm_postponed_job_queue_init(vm);
|
||||
ruby_current_vm_ptr = vm;
|
||||
vm->objspace = rb_objspace_alloc();
|
||||
vm->negative_cme_table = rb_id_table_create(16);
|
||||
vm->overloaded_cme_table = st_init_numtable();
|
||||
vm->constant_cache = rb_id_table_create(0);
|
||||
|
|
|
@ -726,9 +726,8 @@ typedef struct rb_vm_struct {
|
|||
/* relation table of ensure - rollback for callcc */
|
||||
struct st_table *ensure_rollback_table;
|
||||
|
||||
/* postponed_job (async-signal-safe, NOT thread-safe) */
|
||||
struct rb_postponed_job_struct *postponed_job_buffer;
|
||||
rb_atomic_t postponed_job_index;
|
||||
/* postponed_job (async-signal-safe, and thread-safe) */
|
||||
struct rb_postponed_job_queue *postponed_job_queue;
|
||||
|
||||
int src_encoding_index;
|
||||
|
||||
|
@ -2171,6 +2170,10 @@ rb_exec_event_hook_script_compiled(rb_execution_context_t *ec, const rb_iseq_t *
|
|||
}
|
||||
|
||||
void rb_vm_trap_exit(rb_vm_t *vm);
|
||||
void rb_vm_postponed_job_atfork(void); /* vm_trace.c */
|
||||
void rb_vm_postponed_job_free(void); /* vm_trace.c */
|
||||
size_t rb_vm_memsize_postponed_job_queue(void); /* vm_trace.c */
|
||||
void rb_vm_postponed_job_queue_init(rb_vm_t *vm); /* vm_trace.c */
|
||||
|
||||
RUBY_SYMBOL_EXPORT_BEGIN
|
||||
|
||||
|
|
302
vm_trace.c
302
vm_trace.c
|
@ -23,11 +23,15 @@
|
|||
|
||||
#include "eval_intern.h"
|
||||
#include "internal.h"
|
||||
#include "internal/bits.h"
|
||||
#include "internal/class.h"
|
||||
#include "internal/gc.h"
|
||||
#include "internal/hash.h"
|
||||
#include "internal/symbol.h"
|
||||
#include "internal/thread.h"
|
||||
#include "iseq.h"
|
||||
#include "rjit.h"
|
||||
#include "ruby/atomic.h"
|
||||
#include "ruby/debug.h"
|
||||
#include "vm_core.h"
|
||||
#include "ruby/ractor.h"
|
||||
|
@ -1617,17 +1621,22 @@ Init_vm_trace(void)
|
|||
rb_undef_alloc_func(rb_cTracePoint);
|
||||
}
|
||||
|
||||
typedef struct rb_postponed_job_struct {
|
||||
rb_postponed_job_func_t func;
|
||||
void *data;
|
||||
} rb_postponed_job_t;
|
||||
|
||||
#define MAX_POSTPONED_JOB 1000
|
||||
#define MAX_POSTPONED_JOB_SPECIAL_ADDITION 24
|
||||
/*
|
||||
* Ruby actually has two separate mechanisms for enqueueing work from contexts
|
||||
* where it is not safe to run Ruby code, to run later on when it is safe. One
|
||||
* is async-signal-safe but more limited, and accessed through the
|
||||
* `rb_postponed_job_preregister` and `rb_postponed_job_trigger` functions. The
|
||||
* other is more flexible but cannot be used in signal handlers, and is accessed
|
||||
* through the `rb_workqueue_register` function.
|
||||
*
|
||||
* The postponed job functions form part of Ruby's extension API, but the
|
||||
* workqueue functions are for internal use only.
|
||||
*/
|
||||
|
||||
struct rb_workqueue_job {
|
||||
struct ccan_list_node jnode; /* <=> vm->workqueue */
|
||||
rb_postponed_job_t job;
|
||||
rb_postponed_job_func_t func;
|
||||
void *data;
|
||||
};
|
||||
|
||||
// Used for VM memsize reporting. Returns the size of a list of rb_workqueue_job
|
||||
|
@ -1645,110 +1654,6 @@ rb_vm_memsize_workqueue(struct ccan_list_head *workqueue)
|
|||
return size;
|
||||
}
|
||||
|
||||
// Used for VM memsize reporting. Returns the total size of the postponed job
|
||||
// buffer that was allocated at initialization.
|
||||
size_t
|
||||
rb_vm_memsize_postponed_job_buffer(void)
|
||||
{
|
||||
return sizeof(rb_postponed_job_t) * MAX_POSTPONED_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 {
|
||||
PJRR_SUCCESS = 0,
|
||||
PJRR_FULL = 1,
|
||||
PJRR_INTERRUPTED = 2
|
||||
};
|
||||
|
||||
/* 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, rb_atomic_t max, rb_atomic_t expected_index)
|
||||
{
|
||||
rb_postponed_job_t *pjob;
|
||||
|
||||
if (expected_index >= max) return PJRR_FULL; /* failed */
|
||||
|
||||
if (ATOMIC_CAS(vm->postponed_job_index, expected_index, expected_index+1) == expected_index) {
|
||||
pjob = &vm->postponed_job_buffer[expected_index];
|
||||
}
|
||||
else {
|
||||
return PJRR_INTERRUPTED;
|
||||
}
|
||||
|
||||
/* unused: pjob->flags = flags; */
|
||||
pjob->func = func;
|
||||
pjob->data = data;
|
||||
|
||||
RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(ec);
|
||||
|
||||
return PJRR_SUCCESS;
|
||||
}
|
||||
|
||||
static rb_execution_context_t *
|
||||
get_valid_ec(rb_vm_t *vm)
|
||||
{
|
||||
rb_execution_context_t *ec = rb_current_execution_context(false);
|
||||
if (ec == NULL) ec = rb_vm_main_ractor_ec(vm);
|
||||
return ec;
|
||||
}
|
||||
|
||||
/*
|
||||
* return 0 if job buffer is full
|
||||
* Async-signal-safe
|
||||
*/
|
||||
int
|
||||
rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void *data)
|
||||
{
|
||||
rb_vm_t *vm = GET_VM();
|
||||
rb_execution_context_t *ec = get_valid_ec(vm);
|
||||
|
||||
begin:
|
||||
switch (postponed_job_register(ec, vm, flags, func, data, MAX_POSTPONED_JOB, vm->postponed_job_index)) {
|
||||
case PJRR_SUCCESS : return 1;
|
||||
case PJRR_FULL : return 0;
|
||||
case PJRR_INTERRUPTED: goto begin;
|
||||
default: rb_bug("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* return 0 if job buffer is full
|
||||
* Async-signal-safe
|
||||
*/
|
||||
int
|
||||
rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data)
|
||||
{
|
||||
rb_vm_t *vm = GET_VM();
|
||||
rb_execution_context_t *ec = get_valid_ec(vm);
|
||||
rb_postponed_job_t *pjob;
|
||||
rb_atomic_t i, index;
|
||||
|
||||
begin:
|
||||
index = vm->postponed_job_index;
|
||||
for (i=0; i<index; i++) {
|
||||
pjob = &vm->postponed_job_buffer[i];
|
||||
if (pjob->func == func) {
|
||||
RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(ec);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
switch (postponed_job_register(ec, vm, flags, func, data, MAX_POSTPONED_JOB + MAX_POSTPONED_JOB_SPECIAL_ADDITION, index)) {
|
||||
case PJRR_SUCCESS : return 1;
|
||||
case PJRR_FULL : return 0;
|
||||
case PJRR_INTERRUPTED: goto begin;
|
||||
default: rb_bug("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* thread-safe and called from non-Ruby thread
|
||||
* returns FALSE on failure (ENOMEM), TRUE otherwise
|
||||
|
@ -1760,8 +1665,8 @@ rb_workqueue_register(unsigned flags, rb_postponed_job_func_t func, void *data)
|
|||
rb_vm_t *vm = GET_VM();
|
||||
|
||||
if (!wq_job) return FALSE;
|
||||
wq_job->job.func = func;
|
||||
wq_job->job.data = data;
|
||||
wq_job->func = func;
|
||||
wq_job->data = data;
|
||||
|
||||
rb_nativethread_lock_lock(&vm->workqueue_lock);
|
||||
ccan_list_add_tail(&vm->workqueue, &wq_job->jnode);
|
||||
|
@ -1773,11 +1678,144 @@ rb_workqueue_register(unsigned flags, rb_postponed_job_func_t func, void *data)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
#define PJOB_PREREG_TABLE_SIZE (sizeof(rb_atomic_t) * CHAR_BIT)
|
||||
/* pre-registered jobs table, for async-safe jobs */
|
||||
typedef struct rb_postponed_job_queue {
|
||||
struct {
|
||||
rb_postponed_job_func_t func;
|
||||
void *data;
|
||||
} prereg_table[PJOB_PREREG_TABLE_SIZE];
|
||||
/* Bits in this are set when the corresponding entry in prereg_table has non-zero
|
||||
* triggered_count; i.e. somebody called rb_postponed_job_trigger */
|
||||
rb_atomic_t prereg_triggered_bitset;
|
||||
} rb_postponed_job_queues_t;
|
||||
|
||||
void
|
||||
rb_vm_postponed_job_queue_init(rb_vm_t *vm)
|
||||
{
|
||||
/* use mimmalloc; postponed job registration is a dependency of objspace, so this gets
|
||||
* called _VERY_ early inside Init_BareVM */
|
||||
rb_postponed_job_queues_t *pjq = ruby_mimmalloc(sizeof(rb_postponed_job_queues_t));
|
||||
pjq->prereg_triggered_bitset = 0;
|
||||
memset(pjq->prereg_table, 0, sizeof(pjq->prereg_table));
|
||||
vm->postponed_job_queue = pjq;
|
||||
}
|
||||
|
||||
static rb_execution_context_t *
|
||||
get_valid_ec(rb_vm_t *vm)
|
||||
{
|
||||
rb_execution_context_t *ec = rb_current_execution_context(false);
|
||||
if (ec == NULL) ec = rb_vm_main_ractor_ec(vm);
|
||||
return ec;
|
||||
}
|
||||
|
||||
void
|
||||
rb_vm_postponed_job_atfork(void)
|
||||
{
|
||||
rb_vm_t *vm = GET_VM();
|
||||
rb_postponed_job_queues_t *pjq = vm->postponed_job_queue;
|
||||
/* make sure we set the interrupt flag on _this_ thread if we carried any pjobs over
|
||||
* from the other side of the fork */
|
||||
if (pjq->prereg_triggered_bitset) {
|
||||
RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(get_valid_ec(vm));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Frees the memory managed by the postponed job infrastructure at shutdown */
|
||||
void
|
||||
rb_vm_postponed_job_free(void)
|
||||
{
|
||||
rb_vm_t *vm = GET_VM();
|
||||
ruby_xfree(vm->postponed_job_queue);
|
||||
vm->postponed_job_queue = NULL;
|
||||
}
|
||||
|
||||
// Used for VM memsize reporting. Returns the total size of the postponed job
|
||||
// queue infrastructure.
|
||||
size_t
|
||||
rb_vm_memsize_postponed_job_queue(void)
|
||||
{
|
||||
return sizeof(rb_postponed_job_queues_t);
|
||||
}
|
||||
|
||||
|
||||
rb_postponed_job_handle_t
|
||||
rb_postponed_job_preregister(rb_postponed_job_func_t func, void *data)
|
||||
{
|
||||
/* The doc comments say that this function should be called under the GVL, because
|
||||
* that is actually required to get the guarantee that "if a given (func, data) pair
|
||||
* was already pre-registered, this method will return the same handle instance".
|
||||
*
|
||||
* However, the actual implementation here is called without the GVL, from inside
|
||||
* rb_postponed_job_register, to support that legacy interface. In the presence
|
||||
* of concurrent calls to both _preregister and _register functions on the same
|
||||
* func, however, the data may get mixed up between them. */
|
||||
|
||||
rb_postponed_job_queues_t *pjq = GET_VM()->postponed_job_queue;
|
||||
for (unsigned int i = 0; i < PJOB_PREREG_TABLE_SIZE; i++) {
|
||||
/* Try and set this slot to equal `func` */
|
||||
rb_postponed_job_func_t existing_func = (rb_postponed_job_func_t)RUBY_ATOMIC_PTR_CAS(pjq->prereg_table[i], NULL, (void *)func);
|
||||
if (existing_func == NULL || existing_func == func) {
|
||||
/* Either this slot was NULL, and we set it to func, or, this slot was already equal to func.
|
||||
* In either case, clobber the data with our data. Note that concurrent calls to
|
||||
* rb_postponed_job_register with the same func & different data will result in either of the
|
||||
* datas being written */
|
||||
RUBY_ATOMIC_PTR_EXCHANGE(pjq->prereg_table[i].data, data);
|
||||
return (rb_postponed_job_handle_t)i;
|
||||
} else {
|
||||
/* Try the next slot if this one already has a func in it */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* full */
|
||||
return POSTPONED_JOB_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
void
|
||||
rb_postponed_job_trigger(rb_postponed_job_handle_t h)
|
||||
{
|
||||
rb_vm_t *vm = GET_VM();
|
||||
rb_postponed_job_queues_t *pjq = vm->postponed_job_queue;
|
||||
|
||||
RUBY_ATOMIC_OR(pjq->prereg_triggered_bitset, (((rb_atomic_t)1UL) << h));
|
||||
RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(get_valid_ec(vm));
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
pjob_register_legacy_impl(unsigned int flags, rb_postponed_job_func_t func, void *data)
|
||||
{
|
||||
/* We _know_ calling preregister from a signal handler like this is racy; what is
|
||||
* and is not promised is very exhaustively documented in debug.h */
|
||||
rb_postponed_job_handle_t h = rb_postponed_job_preregister(func, data);
|
||||
if (h == POSTPONED_JOB_HANDLE_INVALID) {
|
||||
return 0;
|
||||
}
|
||||
rb_postponed_job_trigger(h);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void *data)
|
||||
{
|
||||
return pjob_register_legacy_impl(flags, func, data);
|
||||
}
|
||||
|
||||
int
|
||||
rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data)
|
||||
{
|
||||
return pjob_register_legacy_impl(flags, func, data);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
rb_postponed_job_flush(rb_vm_t *vm)
|
||||
{
|
||||
rb_postponed_job_queues_t *pjq = GET_VM()->postponed_job_queue;
|
||||
rb_execution_context_t *ec = GET_EC();
|
||||
const rb_atomic_t block_mask = POSTPONED_JOB_INTERRUPT_MASK|TRAP_INTERRUPT_MASK;
|
||||
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 ccan_list_head tmp;
|
||||
|
@ -1788,26 +1826,31 @@ rb_postponed_job_flush(rb_vm_t *vm)
|
|||
ccan_list_append_list(&tmp, &vm->workqueue);
|
||||
rb_nativethread_lock_unlock(&vm->workqueue_lock);
|
||||
|
||||
rb_atomic_t prereg_triggered_bits = RUBY_ATOMIC_EXCHANGE(pjq->prereg_triggered_bitset, 0);
|
||||
|
||||
ec->errinfo = Qnil;
|
||||
/* mask POSTPONED_JOB dispatch */
|
||||
ec->interrupt_mask |= block_mask;
|
||||
{
|
||||
EC_PUSH_TAG(ec);
|
||||
if (EC_EXEC_TAG() == TAG_NONE) {
|
||||
rb_atomic_t 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);
|
||||
}
|
||||
/* execute postponed jobs */
|
||||
while (prereg_triggered_bits) {
|
||||
unsigned int i = bit_length(prereg_triggered_bits) - 1;
|
||||
prereg_triggered_bits ^= ((1UL) << i); /* toggle ith bit off */
|
||||
rb_postponed_job_func_t func = pjq->prereg_table[i].func;
|
||||
void *data = pjq->prereg_table[i].data;
|
||||
(func)(data);
|
||||
}
|
||||
|
||||
/* execute workqueue jobs */
|
||||
struct rb_workqueue_job *wq_job;
|
||||
while ((wq_job = ccan_list_pop(&tmp, struct rb_workqueue_job, jnode))) {
|
||||
rb_postponed_job_t pjob = wq_job->job;
|
||||
rb_postponed_job_func_t func = wq_job->func;
|
||||
void *data = wq_job->data;
|
||||
|
||||
free(wq_job);
|
||||
(pjob.func)(pjob.data);
|
||||
(func)(data);
|
||||
}
|
||||
}
|
||||
EC_POP_TAG();
|
||||
|
@ -1816,7 +1859,8 @@ rb_postponed_job_flush(rb_vm_t *vm)
|
|||
ec->interrupt_mask &= ~(saved_mask ^ block_mask);
|
||||
ec->errinfo = saved_errno;
|
||||
|
||||
/* don't leak memory if a job threw an exception */
|
||||
/* If we threw an exception, there might be leftover workqueue items; carry them over
|
||||
* to a subsequent execution of flush */
|
||||
if (!ccan_list_empty(&tmp)) {
|
||||
rb_nativethread_lock_lock(&vm->workqueue_lock);
|
||||
ccan_list_prepend_list(&vm->workqueue, &tmp);
|
||||
|
@ -1824,4 +1868,10 @@ rb_postponed_job_flush(rb_vm_t *vm)
|
|||
|
||||
RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(GET_EC());
|
||||
}
|
||||
/* likewise with any remaining-to-be-executed bits of the preregistered postponed
|
||||
* job table */
|
||||
if (prereg_triggered_bits) {
|
||||
RUBY_ATOMIC_OR(pjq->prereg_triggered_bitset, prereg_triggered_bits);
|
||||
RUBY_VM_SET_POSTPONED_JOB_INTERRUPT(GET_EC());
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче