mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
process.c: Add Process._fork (#5017)
* process.c: Add Process._fork This API is supposed for application monitoring libraries to hook fork event. [Feature #17795] Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
This commit is contained in:
parent
1eac38c609
commit
13068ebe32
Notes:
git
2021-10-25 20:47:39 +09:00
Merged-By: mame <mame@ruby-lang.org>
5 changed files with 136 additions and 11 deletions
|
@ -10360,15 +10360,18 @@ process.$(OBJEXT): $(hdrdir)/ruby.h
|
|||
process.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
process.$(OBJEXT): $(top_srcdir)/include/ruby/fiber/scheduler.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/bignum.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/bits.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/class.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/compilers.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/dir.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/error.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/eval.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/gc.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/hash.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/imemo.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/numeric.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/object.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/process.h
|
||||
process.$(OBJEXT): $(top_srcdir)/internal/serial.h
|
||||
|
|
|
@ -75,7 +75,7 @@ struct rb_execarg {
|
|||
};
|
||||
|
||||
/* process.c */
|
||||
rb_pid_t rb_fork_ruby(int *status);
|
||||
rb_pid_t rb_call_proc__fork(void);
|
||||
void rb_last_status_clear(void);
|
||||
static inline char **ARGVSTR2ARGV(VALUE argv_str);
|
||||
static inline size_t ARGVSTR2ARGC(VALUE argv_str);
|
||||
|
|
2
io.c
2
io.c
|
@ -6897,7 +6897,7 @@ pipe_open(VALUE execarg_obj, const char *modestr, int fmode,
|
|||
}
|
||||
else {
|
||||
# if defined(HAVE_WORKING_FORK)
|
||||
pid = rb_fork_ruby(&status);
|
||||
pid = rb_call_proc__fork();
|
||||
if (pid == 0) { /* child */
|
||||
popen_redirect(&arg);
|
||||
rb_io_synchronized(RFILE(orig_stdout)->fptr);
|
||||
|
|
48
process.c
48
process.c
|
@ -103,6 +103,7 @@ int initgroups(const char *, rb_gid_t);
|
|||
#include "internal/error.h"
|
||||
#include "internal/eval.h"
|
||||
#include "internal/hash.h"
|
||||
#include "internal/numeric.h"
|
||||
#include "internal/object.h"
|
||||
#include "internal/process.h"
|
||||
#include "internal/thread.h"
|
||||
|
@ -4345,9 +4346,40 @@ rb_fork_ruby(int *status)
|
|||
return pid;
|
||||
}
|
||||
|
||||
rb_pid_t
|
||||
rb_call_proc__fork(void)
|
||||
{
|
||||
VALUE pid = rb_funcall(rb_mProcess, rb_intern("_fork"), 0);
|
||||
|
||||
return NUM2PIDT(pid);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_WORKING_FORK) && !defined(CANNOT_FORK_WITH_PTHREAD)
|
||||
/*
|
||||
* call-seq:
|
||||
* Process._fork -> integer
|
||||
*
|
||||
* An internal API for fork. Do not call this method directly.
|
||||
* Currently, this is called via +Kernel.#fork+, +Process.fork+, and
|
||||
* +popen+ with +"-"+.
|
||||
*
|
||||
* This method is not for casual code but for application monitoring
|
||||
* libraries. You can add custom code before and after fork events
|
||||
* by overriding this method.
|
||||
*/
|
||||
VALUE
|
||||
rb_proc__fork(VALUE _obj)
|
||||
{
|
||||
rb_pid_t pid = rb_fork_ruby(NULL);
|
||||
|
||||
if (pid == -1) {
|
||||
rb_sys_fail("fork(2)");
|
||||
}
|
||||
|
||||
return PIDT2NUM(pid);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Kernel.fork [{ block }] -> integer or nil
|
||||
|
@ -4378,24 +4410,21 @@ rb_f_fork(VALUE obj)
|
|||
{
|
||||
rb_pid_t pid;
|
||||
|
||||
switch (pid = rb_fork_ruby(NULL)) {
|
||||
case 0:
|
||||
pid = rb_call_proc__fork();
|
||||
|
||||
if (pid == 0) {
|
||||
if (rb_block_given_p()) {
|
||||
int status;
|
||||
rb_protect(rb_yield, Qundef, &status);
|
||||
ruby_stop(status);
|
||||
}
|
||||
return Qnil;
|
||||
|
||||
case -1:
|
||||
rb_sys_fail("fork(2)");
|
||||
return Qnil;
|
||||
|
||||
default:
|
||||
return PIDT2NUM(pid);
|
||||
}
|
||||
|
||||
return PIDT2NUM(pid);
|
||||
}
|
||||
#else
|
||||
#define rb_proc__fork rb_f_notimplement
|
||||
#define rb_f_fork rb_f_notimplement
|
||||
#endif
|
||||
|
||||
|
@ -8700,6 +8729,7 @@ InitVM_process(void)
|
|||
rb_define_singleton_method(rb_mProcess, "exit", f_exit, -1);
|
||||
rb_define_singleton_method(rb_mProcess, "abort", f_abort, -1);
|
||||
rb_define_singleton_method(rb_mProcess, "last_status", proc_s_last_status, 0);
|
||||
rb_define_singleton_method(rb_mProcess, "_fork", rb_proc__fork, 0);
|
||||
|
||||
rb_define_module_function(rb_mProcess, "kill", proc_rb_f_kill, -1);
|
||||
rb_define_module_function(rb_mProcess, "wait", proc_m_wait, -1);
|
||||
|
|
|
@ -2545,4 +2545,96 @@ EOS
|
|||
end
|
||||
assert_empty(Process.waitall)
|
||||
end
|
||||
|
||||
def test__fork
|
||||
r, w = IO.pipe
|
||||
pid = Process._fork
|
||||
if pid == 0
|
||||
begin
|
||||
r.close
|
||||
w << "ok: #$$"
|
||||
w.close
|
||||
ensure
|
||||
exit!
|
||||
end
|
||||
else
|
||||
w.close
|
||||
assert_equal("ok: #{pid}", r.read)
|
||||
r.close
|
||||
Process.waitpid(pid)
|
||||
end
|
||||
end if Process.respond_to?(:_fork)
|
||||
|
||||
def test__fork_hook
|
||||
%w(fork Process.fork).each do |method|
|
||||
feature17795 = '[ruby-core:103400] [Feature #17795]'
|
||||
assert_in_out_err([], <<-"end;", [], [], feature17795, timeout: 60) do |r, e|
|
||||
module ForkHook
|
||||
def _fork
|
||||
p :before
|
||||
ret = super
|
||||
p :after
|
||||
ret
|
||||
end
|
||||
end
|
||||
|
||||
Process.singleton_class.prepend(ForkHook)
|
||||
|
||||
pid = #{ method }
|
||||
p pid
|
||||
Process.waitpid(pid) if pid
|
||||
end;
|
||||
assert_equal([], e)
|
||||
assert_equal(":before", r.shift)
|
||||
assert_equal(":after", r.shift)
|
||||
s = r.map {|s| s.chomp }.sort #=> [pid, ":after", "nil"]
|
||||
assert_match(/^\d+$/, s[0]) # pid
|
||||
assert_equal(":after", s[1])
|
||||
assert_equal("nil", s[2])
|
||||
end
|
||||
end
|
||||
end if Process.respond_to?(:_fork)
|
||||
|
||||
def test__fork_hook_popen
|
||||
feature17795 = '[ruby-core:103400] [Feature #17795]'
|
||||
assert_in_out_err([], <<-"end;", %w(:before :after :after foo bar), [], feature17795, timeout: 60)
|
||||
module ForkHook
|
||||
def _fork
|
||||
p :before
|
||||
ret = super
|
||||
p :after
|
||||
ret
|
||||
end
|
||||
end
|
||||
|
||||
Process.singleton_class.prepend(ForkHook)
|
||||
|
||||
IO.popen("-") {|io|
|
||||
if !io
|
||||
puts "foo"
|
||||
else
|
||||
puts io.read + "bar"
|
||||
end
|
||||
}
|
||||
end;
|
||||
end if Process.respond_to?(:_fork)
|
||||
|
||||
def test__fork_wrong_type_hook
|
||||
feature17795 = '[ruby-core:103400] [Feature #17795]'
|
||||
assert_in_out_err([], <<-"end;", ["OK"], [], feature17795, timeout: 60)
|
||||
module ForkHook
|
||||
def _fork
|
||||
"BOO"
|
||||
end
|
||||
end
|
||||
|
||||
Process.singleton_class.prepend(ForkHook)
|
||||
|
||||
begin
|
||||
fork
|
||||
rescue TypeError
|
||||
puts "OK"
|
||||
end
|
||||
end;
|
||||
end if Process.respond_to?(:_fork)
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue