1
0
Fork 0
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:
Yusuke Endoh 2021-10-25 20:47:19 +09:00 committed by GitHub
parent 1eac38c609
commit 13068ebe32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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

View file

@ -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

View file

@ -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
View file

@ -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);

View file

@ -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);

View file

@ -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