diff options
-rw-r--r-- | NEWS.md | 7 | ||||
-rw-r--r-- | common.mk | 2 | ||||
-rw-r--r-- | gc.c | 31 | ||||
-rw-r--r-- | internal/gc.h | 1 | ||||
-rw-r--r-- | process.c | 34 | ||||
-rw-r--r-- | spec/ruby/core/process/warmup_spec.rb | 11 | ||||
-rw-r--r-- | test/ruby/test_process.rb | 8 |
7 files changed, 94 insertions, 0 deletions
@@ -35,6 +35,13 @@ Note: We're only listing outstanding class updates. The class use equality semantic to lookup keys like a regular hash, but it doesn't hold strong references on the keys. [[Feature #18498]] +* Process.warnup + + * Notify the Ruby virtual machine that the boot sequence is finished, + and that now is a good time to optimize the application. This is useful + for long running applications. The actual optimizations performed are entirely + implementation specific and may change in the future without notice. [[Feature #18885] + ## Stdlib updates The following default gems are updated. @@ -11024,7 +11024,9 @@ process.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h process.$(OBJEXT): {$(VPATH)}thread_native.h process.$(OBJEXT): {$(VPATH)}util.h process.$(OBJEXT): {$(VPATH)}vm_core.h +process.$(OBJEXT): {$(VPATH)}vm_debug.h process.$(OBJEXT): {$(VPATH)}vm_opts.h +process.$(OBJEXT): {$(VPATH)}vm_sync.h ractor.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h ractor.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h ractor.$(OBJEXT): $(CCAN_DIR)/list/list.h @@ -9833,6 +9833,26 @@ garbage_collect_with_gvl(rb_objspace_t *objspace, unsigned int reason) } } +static int +gc_promote_object_i(void *vstart, void *vend, size_t stride, void *data) +{ + rb_objspace_t *objspace = &rb_objspace; + VALUE v = (VALUE)vstart; + for (; v != (VALUE)vend; v += stride) { + switch (BUILTIN_TYPE(v)) { + case T_NONE: + case T_ZOMBIE: + break; + default: + if (!RVALUE_OLD_P(v) && !RVALUE_WB_UNPROTECTED(v)) { + RVALUE_AGE_SET_OLD(objspace, v); + } + } + } + + return 0; +} + static VALUE gc_start_internal(rb_execution_context_t *ec, VALUE self, VALUE full_mark, VALUE immediate_mark, VALUE immediate_sweep, VALUE compact) { @@ -9860,6 +9880,17 @@ gc_start_internal(rb_execution_context_t *ec, VALUE self, VALUE full_mark, VALUE return Qnil; } +void +rb_gc_prepare_heap(void) +{ + gc_start_internal(NULL, Qtrue, Qtrue, Qtrue, Qtrue, Qtrue); + + /* The transient heap need to be evacuated before we promote objects */ + rb_transient_heap_start_marking(true); + rb_transient_heap_evacuate(); + rb_objspace_each_objects(gc_promote_object_i, NULL); +} + static int gc_is_moveable_obj(rb_objspace_t *objspace, VALUE obj) { diff --git a/internal/gc.h b/internal/gc.h index 2b67ca40dc..e0cc3bfa8a 100644 --- a/internal/gc.h +++ b/internal/gc.h @@ -214,6 +214,7 @@ extern VALUE *ruby_initial_gc_stress_ptr; extern int ruby_disable_gc; RUBY_ATTR_MALLOC void *ruby_mimmalloc(size_t size); void ruby_mimfree(void *ptr); +void rb_gc_prepare_heap(void); void rb_objspace_set_event_hook(const rb_event_flag_t event); VALUE rb_objspace_gc_enable(struct rb_objspace *); VALUE rb_objspace_gc_disable(struct rb_objspace *); @@ -115,6 +115,7 @@ int initgroups(const char *, rb_gid_t); #include "ruby/thread.h" #include "ruby/util.h" #include "vm_core.h" +#include "vm_sync.h" #include "ruby/ractor.h" /* define system APIs */ @@ -8510,6 +8511,37 @@ static VALUE rb_mProcUID; static VALUE rb_mProcGID; static VALUE rb_mProcID_Syscall; +/* + * call-seq: + * Process.warmup -> true + * + * Notify the Ruby virtual machine that the boot sequence is finished, + * and that now is a good time to optimize the application. This is useful + * for long running applications. + * + * This method is expected to be called at the end of the application boot. + * If the application is deployed using a pre-forking model, +Process.warmup+ + * should be called in the original process before the first fork. + * + * The actual optimizations performed are entirely implementation specific + * and may change in the future without notice. + * + * On CRuby, +Process.warmup+: + * + * * Perform a major GC. + * * Compacts the heap. + * * Promotes all surviving objects to the old generation. + */ + +static VALUE +proc_warmup(VALUE _) +{ + RB_VM_LOCK_ENTER(); + rb_gc_prepare_heap(); + RB_VM_LOCK_LEAVE(); + return Qtrue; +} + /* * Document-module: Process @@ -8627,6 +8659,8 @@ InitVM_process(void) rb_define_module_function(rb_mProcess, "getpriority", proc_getpriority, 2); rb_define_module_function(rb_mProcess, "setpriority", proc_setpriority, 3); + rb_define_module_function(rb_mProcess, "warmup", proc_warmup, 0); + #ifdef HAVE_GETPRIORITY /* see Process.setpriority */ rb_define_const(rb_mProcess, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS)); diff --git a/spec/ruby/core/process/warmup_spec.rb b/spec/ruby/core/process/warmup_spec.rb new file mode 100644 index 0000000000..fbdfd34848 --- /dev/null +++ b/spec/ruby/core/process/warmup_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../spec_helper' + +describe "Process.warmup" do + ruby_version_is "3.3" do + # The behavior is entirely implementation dependant. + # Other implementations are free to just make it a noop + it "is implemented" do + Process.warmup.should == true + end + end +end diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index 6e8050a54a..fa25d10353 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -4,6 +4,7 @@ require 'test/unit' require 'tempfile' require 'timeout' require 'rbconfig' +require 'objspace' class TestProcess < Test::Unit::TestCase RUBY = EnvUtil.rubybin @@ -2679,4 +2680,11 @@ EOS end end; end if Process.respond_to?(:_fork) + + def test_warmup_promote_all_objects_to_oldgen + obj = Object.new + refute_includes(ObjectSpace.dump(obj), '"old":true') + Process.warmup + assert_includes(ObjectSpace.dump(obj), '"old":true') + end end |