[前][次][番号順一覧][スレッド一覧]

ruby-changes:64031

From: Samuel <ko1@a...>
Date: Wed, 9 Dec 2020 04:56:05 +0900 (JST)
Subject: [ruby-changes:64031] 2553c5f94a (master): Add support for non-blocking `Process.wait`.

https://git.ruby-lang.org/ruby.git/commit/?id=2553c5f94a

From 2553c5f94a5d51c2c5876b31e4c1521ad9be12f6 Mon Sep 17 00:00:00 2001
From: Samuel Williams <samuel.williams@o...>
Date: Tue, 8 Dec 2020 09:29:09 +1300
Subject: Add support for non-blocking `Process.wait`.


diff --git a/common.mk b/common.mk
index 158828a..c2d6a72 100644
--- a/common.mk
+++ b/common.mk
@@ -10002,6 +10002,7 @@ process.$(OBJEXT): $(top_srcdir)/internal/thread.h https://github.com/ruby/ruby/blob/trunk/common.mk#L10002
 process.$(OBJEXT): $(top_srcdir)/internal/variable.h
 process.$(OBJEXT): $(top_srcdir)/internal/vm.h
 process.$(OBJEXT): $(top_srcdir)/internal/warnings.h
+process.$(OBJEXT): {$(VPATH)}$(COROUTINE_H)
 process.$(OBJEXT): {$(VPATH)}assert.h
 process.$(OBJEXT): {$(VPATH)}backward/2/assume.h
 process.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
diff --git a/doc/scheduler.md b/doc/scheduler.md
index 9994831..a6e2d78 100644
--- a/doc/scheduler.md
+++ b/doc/scheduler.md
@@ -12,6 +12,17 @@ This is the interface you need to implement. https://github.com/ruby/ruby/blob/trunk/doc/scheduler.md#L12
 
 ~~~ ruby
 class Scheduler
+  # Wait for the specified process ID to exit.
+  # This hook is optional.
+  # @parameter pid [Integer] The process ID to wait for.
+  # @parameter flags [Integer] A bit-mask of flags suitable for `Process::Status.wait`.
+  # @returns [Process::Status] A process status instance.
+  def process_wait(pid, flags)
+    Thread.new do
+      Process::Status.wait(pid, flags)
+    end.value
+  end
+
   # Wait for the given file descriptor to match the specified events within
   # the specified timeout.
   # @parameter event [Integer] A bit mask of `IO::READABLE`,
diff --git a/include/ruby/internal/intern/process.h b/include/ruby/internal/intern/process.h
index 2b1005a..bcb5a7e 100644
--- a/include/ruby/internal/intern/process.h
+++ b/include/ruby/internal/intern/process.h
@@ -28,7 +28,9 @@ https://github.com/ruby/ruby/blob/trunk/include/ruby/internal/intern/process.h#L28
 RBIMPL_SYMBOL_EXPORT_BEGIN()
 
 /* process.c */
-void rb_last_status_set(int status, rb_pid_t pid);
+RUBY_EXTERN void (* rb_socket_before_fork_func)();
+
+void rb_last_status_set(rb_pid_t pid, int status, int error);
 VALUE rb_last_status_get(void);
 int rb_proc_exec(const char*);
 
diff --git a/internal/scheduler.h b/internal/scheduler.h
index 472edc6..8314020 100644
--- a/internal/scheduler.h
+++ b/internal/scheduler.h
@@ -25,6 +25,9 @@ VALUE rb_scheduler_close(VALUE scheduler); https://github.com/ruby/ruby/blob/trunk/internal/scheduler.h#L25
 VALUE rb_scheduler_kernel_sleep(VALUE scheduler, VALUE duration);
 VALUE rb_scheduler_kernel_sleepv(VALUE scheduler, int argc, VALUE * argv);
 
+int rb_scheduler_supports_process_wait(VALUE scheduler);
+VALUE rb_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags);
+
 VALUE rb_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout);
 VALUE rb_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber);
 
diff --git a/io.c b/io.c
index 0182042..b5553b5 100644
--- a/io.c
+++ b/io.c
@@ -4913,9 +4913,9 @@ fptr_waitpid(rb_io_t *fptr, int nohang) https://github.com/ruby/ruby/blob/trunk/io.c#L4913
 {
     int status;
     if (fptr->pid) {
-	rb_last_status_clear();
-	rb_waitpid(fptr->pid, &status, nohang ? WNOHANG : 0);
-	fptr->pid = 0;
+        rb_last_status_clear();
+        rb_waitpid(fptr->pid, &status, nohang ? WNOHANG : 0);
+        fptr->pid = 0;
     }
 }
 
@@ -6433,11 +6433,11 @@ pipe_finalize(rb_io_t *fptr, int noraise) https://github.com/ruby/ruby/blob/trunk/io.c#L6433
 #if !defined(HAVE_WORKING_FORK) && !defined(_WIN32)
     int status = 0;
     if (fptr->stdio_file) {
-	status = pclose(fptr->stdio_file);
+        status = pclose(fptr->stdio_file);
     }
     fptr->fd = -1;
     fptr->stdio_file = 0;
-    rb_last_status_set(status, fptr->pid);
+    rb_last_status_set(fptr->pid, status, 0);
 #else
     fptr_finalize(fptr, noraise);
 #endif
diff --git a/process.c b/process.c
index 8ad555f..acc9de1 100644
--- a/process.c
+++ b/process.c
@@ -14,6 +14,7 @@ https://github.com/ruby/ruby/blob/trunk/process.c#L14
 #include "ruby/internal/config.h"
 
 #include "internal/scheduler.h"
+#include "coroutine/Stack.h"
 
 #include <ctype.h>
 #include <errno.h>
@@ -568,6 +569,27 @@ proc_get_ppid(VALUE _) https://github.com/ruby/ruby/blob/trunk/process.c#L569
 
 static VALUE rb_cProcessStatus;
 
+struct rb_process_status {
+    rb_pid_t pid;
+    int status;
+    int error;
+};
+
+static const rb_data_type_t rb_process_status_type = {
+    .wrap_struct_name = "Process::Status",
+    .function = {
+        .dfree = RUBY_DEFAULT_FREE,
+    },
+    .data = NULL,
+    .flags = RUBY_TYPED_FREE_IMMEDIATELY,
+};
+
+static VALUE rb_process_status_allocate(VALUE klass) {
+    struct rb_process_status *data = NULL;
+
+    return TypedData_Make_Struct(klass, struct rb_process_status, &rb_process_status_type, data);
+}
+
 VALUE
 rb_last_status_get(void)
 {
@@ -596,13 +618,20 @@ proc_s_last_status(VALUE mod) https://github.com/ruby/ruby/blob/trunk/process.c#L618
 }
 
 void
-rb_last_status_set(int status, rb_pid_t pid)
+rb_last_status_set(rb_pid_t pid, int status, int error)
 {
     rb_thread_t *th = GET_THREAD();
-    th->last_status = rb_obj_alloc(rb_cProcessStatus);
-    rb_ivar_set(th->last_status, id_status, INT2FIX(status));
-    rb_ivar_set(th->last_status, id_pid, PIDT2NUM(pid));
-    rb_obj_freeze(th->last_status);
+
+    VALUE last_status = rb_process_status_allocate(rb_cProcessStatus);
+
+    struct rb_process_status *data = RTYPEDDATA_DATA(last_status);
+    data->pid = pid;
+    data->status = status;
+    data->error = error;
+
+    rb_obj_freeze(last_status);
+
+    th->last_status = last_status;
 }
 
 void
@@ -624,9 +653,11 @@ rb_last_status_clear(void) https://github.com/ruby/ruby/blob/trunk/process.c#L653
  */
 
 static VALUE
-pst_to_i(VALUE st)
+pst_to_i(VALUE self)
 {
-    return rb_ivar_get(st, id_status);
+    struct rb_process_status *data = RTYPEDDATA_DATA(self);
+
+    return RB_INT2NUM(data->status);
 }
 
 #define PST2INT(st) NUM2INT(pst_to_i(st))
@@ -643,9 +674,11 @@ pst_to_i(VALUE st) https://github.com/ruby/ruby/blob/trunk/process.c#L674
  */
 
 static VALUE
-pst_pid(VALUE st)
+pst_pid(VALUE self)
 {
-    return rb_attr_get(st, id_pid);
+    struct rb_process_status *data = RTYPEDDATA_DATA(self);
+
+    return PIDT2NUM(data->pid);
 }
 
 static VALUE pst_message_status(VALUE str, int status);
@@ -1104,6 +1137,7 @@ waitpid_state_init(struct waitpid_state *w, rb_pid_t pid, int options) https://github.com/ruby/ruby/blob/trunk/process.c#L1137
     w->ret = 0;
     w->pid = pid;
     w->options = options;
+    w->errnum = 0;
 }
 
 static const rb_hrtime_t *
@@ -1214,8 +1248,10 @@ waitpid_wait(struct waitpid_state *w) https://github.com/ruby/ruby/blob/trunk/process.c#L1248
      */
     rb_native_mutex_lock(&vm->waitpid_lock);
 
-    if (w->pid > 0 || list_empty(&vm->waiting_pids))
+    if (w->pid > 0 || list_empty(&vm->waiting_pids)) {
         w->ret = do_waitpid(w->pid, &w->status, w->options | WNOHANG);
+    }
+
     if (w->ret) {
         if (w->ret == -1) w->errnum = errno;
     }
@@ -1264,35 +1300,125 @@ waitpid_no_SIGCHLD(struct waitpid_state *w) https://github.com/ruby/ruby/blob/trunk/process.c#L1300
         w->errnum = errno;
 }
 
-rb_pid_t
-rb_waitpid(rb_pid_t pid, int *st, int flags)
+/*
+ *  call-seq:
+ *     Process::Status.wait(pid=-1, flags=0)      -> Process::Status
+ *
+ *  Waits for a child process to exit and returns a Process::Status object
+ *  containing information on that process. Which child it waits on
+ *  depends on the value of _pid_:
+ *
+ *  > 0::   Waits for the child whose process ID equals _pid_.
+ *
+ *  0::     Waits for any child whose process group ID equals that of the
+ *          calling process.
+ *
+ *  -1::    Waits for any child process (the default if no _pid_ is
+ *          given).
+ *
+ *  < -1::  Waits for any child whose process group ID equals the absolute
+ *          value of _pid_.
+ *
+ *  The _flags_ argument may be a logical or of the flag values
+ *  Process::WNOHANG (do not block if no child available)
+ *  or Process::WUNTRACED (return stopped children that
+ *  haven't been reported). Not all flags are available on all
+ *  platforms, but a flag value of zero will work on all platforms.
+ *
+ *  Calling this method raises a SystemCallError if there are no child
+ *  processes. Not available on all platforms.
+ *
+ *  May invoke the scheduler hook _process_wait_.
+ *
+ *     fork { exit 99 }                              #=> 27429
+ *     Process::Status.wait                          #=> pid 27429 exit 99
+ *     $?                                            #=> nil
+ *
+ *     pid = fork { sleep 3 }                        #=> 27440
+ *     Time.now                                      #=> 2008-03-08 19:56:16 +0900
+ *     Process::Status.wait(pid, Process::WNOHANG)   #=> nil
+ *     Time.now                                      #=> 2008-03-08 19:56:16 +0900
+ *     Process::Status.wait(pid, 0)                  #=> pid 27440 exit 99
+ *     Time.now                                      #=> 2008-03-08 19:56:19 +0900
+ */
+VALUE rb_process_status_wait(rb_pid_t pid, int flags)
 {
-    struct waitpid_state w;
+    // We only enter the scheduler if we are "blocking":
+    if (!(flags & WNOHANG)) {
+        VALUE scheduler = rb_scheduler_current();
+        if (rb_scheduler_supports_process_wait(scheduler)) {
+            return rb_scheduler_process_wait(scheduler, pid, flags);
+        }
+    }
 
-    waitpid_state_init(&w, pid, flags);
-    w.ec = GET_EC();
+    COROUTINE_STACK_LOCAL(struct waitpid_state, w);
+
+    waitpid_state_init(w, pid, flags);
+    w->ec = GET_EC();
 
     if (WAITPID_USE_SIGCHLD) {
-        waitpid_wait(&w);
+        waitpid_wait(w);
     }
     else {
-        waitpid_no_SIGCHLD(&w);
+        waitpid_no_SIGCHLD(w);
     }
 
-    if (st) *st = w.status;
-    if (w.ret == -1) {
-        errno = w.errnum;
-    }
-    else if (w.ret > 0) {
+    if (w->ret  (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]