proc-wait3 1.9.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 13f18822c2208220d663b7ddf89cd3ef3763185621f23ed81bf6dced9beb8b76
4
- data.tar.gz: 6a82d5550af63215cf4e62b3e390865f5f08e3b3ec30be50128791bf8b9a45a1
3
+ metadata.gz: d9ed8e7bd3a3b5631bbb5e2a4d658c60d1876e8880ebc95eab77f920d81b26be
4
+ data.tar.gz: fa72e2f110aa9a1c4858d7c7a51cc072f115d7de0e63c7f7f0fca0210d26315e
5
5
  SHA512:
6
- metadata.gz: b015e49e3c835d2855552e3d54f2fdf047d11889f5db55ac2db246d277c9fe3ad1135e73e6cb01a0caa5d88bd93c3de361670d74dc78b3a5139c10ea86db46c7
7
- data.tar.gz: f0134f481fa6f8d3cdbdcef1e6f380727afb894e1ed754bd77b568f3a0d43118a3c71aa93f68ea7d96b15768f569fa63199782f91c5c2d729c9efc47cdb0d0ab
6
+ metadata.gz: 027c982ad10f5e7a66bdac1e8406df6f99f4372dd739da56b4a5c05bf9115e58bda7e6fbd5b72a3b6e3734ad652928bb3dbc4750a86a70252abc7541b0792613
7
+ data.tar.gz: 0b8289e6c28ce586d9a8f932e31b4e7dd39edfc223061a07d85ff8f8a28daf50329df4933079fcf347c592aaf9d9538ee9de78e448022222294b2a2c2ba5b91e
checksums.yaml.gz.sig CHANGED
@@ -1,5 +1,5 @@
1
- e��)#
2
- PH s�/w��"
3
- ���񬫖|.6;_i�<;����;�"Zl{����u_dE�oM������ǑWX��_&�i*\akl����GoMx|y"@���|��4G�����jF�ح3���=��RYK
4
- �Vo6�.�k0Z�����E�4Z�JJ�G�ڝ"�c�8���s�[<~Vw�a-�NF� �+ {�쌌��s{�
5
- #�� ��Qw��4w�j
1
+ J�����M�%���� Z"s��n�IY;;B�0� �c?B�=Oq��N��t��XH���C׉�x��=_�� ���<O�P�PC]]c1�u���mM����&d��敔�mo�
2
+ V:m`h\ '���Qb|�b��{B�7vuE�>dC�{Sa�B��Gp
3
+ E���y+�t#=��D�\�ūgDh�K{�Tq��� �+�x�� B>w��>��gg�^��k���WxH��O%�1n`���}
4
+ ��#�/?��
5
+ ڪ)�/>mH��.҅U�u�������`ޖ��x����Wnv���1|�/�4�L�� ��GC�W."���2�m _ڳ�BOLY�7gf�� �)m��FQC�ړY8���D���S��|�K�
data/CHANGES.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 2.1.0 - 18-Jan-2026
2
+ * Modified the wait methods to automatically loop on EINTR for consistent
3
+ cross-platform behavior. Previously you had to do this manually on some
4
+ platforms, e.g. OSX.
5
+
6
+ ## 2.0.0 - 10-Jan-2026
7
+ * Added fiber scheduler support for wait3 and wait4 methods, allowing them
8
+ to cooperate with Ruby's fiber scheduler for non-blocking async operations.
9
+ Note that rusage fields will be zero when using the fiber scheduler path.
10
+ * Added `rb_thread_call_without_gvl` for blocking calls so that other Ruby
11
+ threads can run while waiting.
12
+ * Added `RB_GC_GUARD` to protect Ruby objects during blocking operations.
13
+ * Replaced deprecated `RSTRING()->len` and `RSTRING()->ptr` with `RSTRING_LEN()`
14
+ and `RSTRING_PTR()` macros.
15
+ * Added missing `HAVE_STRLCPY` guard in sigsend with strncpy fallback.
16
+ * Fixed some minor whitespace inconsistencies.
17
+ * Now requires Ruby 3.1 or later.
18
+
1
19
  ## 1.9.3 - 4-May-2024
2
20
  * Some internal refactoring where I bzero C structs before using them.
3
21
 
@@ -157,7 +175,7 @@
157
175
  * Updated tests and documentation.
158
176
 
159
177
  ## 1.2.0 - 7-Feb-2005
160
- * Added the Proc.waitid method (for those platforms that support it).
178
+ * Added the Proc.waitid method (for those platforms that support it).
161
179
  * Made the wait3.c file more rdoc friendly.
162
180
  * Added a test_waitid.rb file in the examples directory.
163
181
 
data/README.md CHANGED
@@ -70,6 +70,9 @@ BSD provides another approach using sigaction handlers + `SA_RESTART`, but it re
70
70
  the signal type in advance. So, unless you want to apply the same handler to *every* type of
71
71
  signal, I don't find it especially useful.
72
72
 
73
+ Update: As of version 2.1.0 you should no longer have to manually rescue EINTR since it's now
74
+ being handled internally.
75
+
73
76
  ## Integration with Ruby's process.c
74
77
  I considered simply providing a patch to the core process.c file, but I
75
78
  decided against it for two reasons. First, I wanted to get something
data/Rakefile CHANGED
@@ -7,17 +7,17 @@ require 'rbconfig'
7
7
  include RbConfig
8
8
 
9
9
  CLEAN.include(
10
- '**/*.gem', # Gem files
11
- '**/*.rbc', # Rubinius
12
- '**/*.o', # C object file
13
- '**/*.log', # Ruby extension build log
14
- '**/Makefile', # C Makefile
15
- '**/conftest.dSYM', # OS X build directory
16
- "**/*.#{CONFIG['DLEXT']}", # C shared object
17
- '**/*.lock' # Bundler
10
+ '**/*.gem', # Gem files
11
+ '**/*.rbc', # Rubinius
12
+ '**/*.o', # C object file
13
+ '**/*.log', # Ruby extension build log
14
+ '**/*.lock', # Gemfile.lock
15
+ '**/Makefile', # C Makefile
16
+ '**/*.dSYM', # OS X build directory
17
+ "**/*.#{CONFIG['DLEXT']}", # C shared object
18
+ '**/*.lock' # Bundler
18
19
  )
19
20
 
20
-
21
21
  desc "Build the source (but don't install it)"
22
22
  task :build => [:clean] do |t|
23
23
  Dir.chdir('ext') do
@@ -72,7 +72,12 @@ end
72
72
 
73
73
  desc 'Run the test suite'
74
74
  RSpec::Core::RakeTask.new(:spec) do |t|
75
- t.rspec_opts = '-Iext'
75
+ t.rspec_opts = '-Iext -f documentation -w'
76
+ end
77
+
78
+ # Clean up afterwards
79
+ Rake::Task[:spec].enhance do
80
+ Rake::Task[:clean].invoke
76
81
  end
77
82
 
78
83
  task :default => [:build, :spec]
data/doc/wait3.md CHANGED
@@ -80,6 +80,13 @@ Returns a `Proc::SigInfo` struct and sets `$?`.
80
80
 
81
81
  Not supported on all platforms.
82
82
 
83
+ ### `Proc.getdtablesize`
84
+
85
+ Returns the current soft limit of the maximum file descriptor number.
86
+
87
+ This is effectively the same as calling Process.getrlimit with RLIMIT_NOFILE
88
+ as the resource identifier.
89
+
83
90
  ## Standard Constants
84
91
  `Process::WAIT3_VERSION`
85
92
 
@@ -209,7 +216,7 @@ page at https://github.com/djberg96/proc-wait3.
209
216
  Apache-2.0
210
217
 
211
218
  ## Copyright
212
- (C) 2003-2024 Daniel J. Berger
219
+ (C) 2003-2026 Daniel J. Berger
213
220
 
214
221
  All Rights Reserved.
215
222
 
@@ -8,10 +8,20 @@
8
8
  #
9
9
  # Modify as you see fit.
10
10
  #######################################################################
11
- require 'English'
12
11
  require 'proc/wait3'
13
12
 
14
- pid = fork { sleep 2 }
13
+ pid1 = fork { puts "PID1 GRP: #{Process.getpgrp}"; sleep 2 }
14
+ pid2 = fork { puts "PID2 GRP: #{Process.getpgrp}"; sleep 3 }
15
+ pid3 = fork { puts "PID2 GRP: #{Process.getpgrp}"; sleep 4 }
16
+
17
+ puts "PID1: #{pid1}"
18
+ puts "PID2: #{pid2}"
19
+ puts "PID3: #{pid3}"
20
+
21
+ puts "MAIN GRP: #{Process.getpgrp}"
15
22
  p Time.now
16
- Process.waitid(Process::P_PID, pid, Process::WEXITED)
17
- p $CHILD_STATUS
23
+
24
+ status = Process.waitid(Process::P_PGID, Process.getpgrp, Process::WEXITED)
25
+
26
+ # status.pid should equal pid1 since it exits first
27
+ p status
data/ext/proc/wait3.c CHANGED
@@ -1,6 +1,9 @@
1
1
  #include <ruby.h>
2
+ #include <ruby/thread.h>
3
+ #include <ruby/fiber/scheduler.h>
2
4
  #include <string.h>
3
5
  #include <unistd.h>
6
+ #include <errno.h>
4
7
 
5
8
  /* Debian */
6
9
  #ifdef HAVE_SYS_RESOURCE_H
@@ -31,6 +34,67 @@ VALUE v_procstat_struct, v_siginfo_struct, v_usage_struct;
31
34
 
32
35
  static void sigproc(int signum, siginfo_t* info, void* ucontext);
33
36
 
37
+ /* Structs for rb_thread_call_without_gvl wrappers */
38
+ struct wait3_args {
39
+ int status;
40
+ int flags;
41
+ struct rusage rusage;
42
+ pid_t pid;
43
+ };
44
+
45
+ #ifdef HAVE_WAIT4
46
+ struct wait4_args {
47
+ pid_t pid;
48
+ int status;
49
+ int flags;
50
+ struct rusage rusage;
51
+ pid_t result;
52
+ };
53
+ #endif
54
+
55
+ #ifdef HAVE_WAITID
56
+ struct waitid_args {
57
+ idtype_t idtype;
58
+ id_t id;
59
+ siginfo_t infop;
60
+ int options;
61
+ int result;
62
+ };
63
+ #endif
64
+
65
+ struct pause_args {
66
+ int result;
67
+ };
68
+
69
+ /* GVL-free wrapper functions */
70
+ static void* wait3_without_gvl(void* data) {
71
+ struct wait3_args* args = (struct wait3_args*)data;
72
+ args->pid = wait3(&args->status, args->flags, &args->rusage);
73
+ return NULL;
74
+ }
75
+
76
+ #ifdef HAVE_WAIT4
77
+ static void* wait4_without_gvl(void* data) {
78
+ struct wait4_args* args = (struct wait4_args*)data;
79
+ args->result = wait4(args->pid, &args->status, args->flags, &args->rusage);
80
+ return NULL;
81
+ }
82
+ #endif
83
+
84
+ #ifdef HAVE_WAITID
85
+ static void* waitid_without_gvl(void* data) {
86
+ struct waitid_args* args = (struct waitid_args*)data;
87
+ args->result = waitid(args->idtype, args->id, &args->infop, args->options);
88
+ return NULL;
89
+ }
90
+ #endif
91
+
92
+ static void* pause_without_gvl(void* data) {
93
+ struct pause_args* args = (struct pause_args*)data;
94
+ args->result = pause();
95
+ return NULL;
96
+ }
97
+
34
98
  /*
35
99
  * Returns true if this process is stopped. This is only returned
36
100
  * returned if the corresponding wait() call had the WUNTRACED flag
@@ -38,10 +102,10 @@ static void sigproc(int signum, siginfo_t* info, void* ucontext);
38
102
  */
39
103
  static VALUE pst_wifstopped(int status)
40
104
  {
41
- if(WIFSTOPPED(status))
42
- return Qtrue;
43
- else
44
- return Qfalse;
105
+ if(WIFSTOPPED(status))
106
+ return Qtrue;
107
+ else
108
+ return Qfalse;
45
109
  }
46
110
 
47
111
  /*
@@ -50,7 +114,7 @@ static VALUE pst_wifstopped(int status)
50
114
  static VALUE pst_wifsignaled(int status)
51
115
  {
52
116
  if (WIFSIGNALED(status))
53
- return Qtrue;
117
+ return Qtrue;
54
118
  else
55
119
  return Qfalse;
56
120
  }
@@ -131,6 +195,42 @@ static VALUE pst_wstopsig(int status)
131
195
  return Qnil;
132
196
  }
133
197
 
198
+ /*
199
+ * Helper to build ProcStat struct from pid and status.
200
+ * Used by fiber scheduler path where rusage is not available.
201
+ * All rusage fields are set to 0.
202
+ */
203
+ static VALUE build_procstat_without_rusage(pid_t pid, int status) {
204
+ return rb_struct_new(v_procstat_struct,
205
+ INT2FIX(pid),
206
+ INT2FIX(status),
207
+ rb_float_new(0.0), /* utime - not available via fiber scheduler */
208
+ rb_float_new(0.0), /* stime - not available via fiber scheduler */
209
+ LONG2NUM(0), /* maxrss */
210
+ LONG2NUM(0), /* ixrss */
211
+ LONG2NUM(0), /* idrss */
212
+ LONG2NUM(0), /* isrss */
213
+ LONG2NUM(0), /* minflt */
214
+ LONG2NUM(0), /* majflt */
215
+ LONG2NUM(0), /* nswap */
216
+ LONG2NUM(0), /* inblock */
217
+ LONG2NUM(0), /* oublock */
218
+ LONG2NUM(0), /* msgsnd */
219
+ LONG2NUM(0), /* msgrcv */
220
+ LONG2NUM(0), /* nsignals */
221
+ LONG2NUM(0), /* nvcsw */
222
+ LONG2NUM(0), /* nivcsw */
223
+ pst_wifstopped(status),
224
+ pst_wifsignaled(status),
225
+ pst_wifexited(status),
226
+ pst_success_p(status),
227
+ pst_wcoredump(status),
228
+ pst_wexitstatus(status),
229
+ pst_wtermsig(status),
230
+ pst_wstopsig(status)
231
+ );
232
+ }
233
+
134
234
  /*
135
235
  * call-seq:
136
236
  * Process.wait3(flags=nil)
@@ -140,13 +240,15 @@ static VALUE pst_wstopsig(int status)
140
240
  *
141
241
  * The return value is a ProcStat structure. The special global $? is also
142
242
  * set. Raises a SystemError if there are no child processes.
243
+ *
244
+ * Note: When a fiber scheduler is active (Ruby 3.0+), this method will
245
+ * cooperate with the scheduler. However, rusage information will not be
246
+ * available and those fields will be set to 0 in the returned struct.
143
247
  */
144
248
  static VALUE proc_wait3(int argc, VALUE *argv, VALUE mod){
145
- int status;
146
- int flags = 0;
147
- struct rusage r;
148
- pid_t pid;
249
+ struct wait3_args args;
149
250
  VALUE v_flags = Qnil;
251
+ int flags = 0;
150
252
 
151
253
  rb_scan_args(argc,argv,"01",&v_flags);
152
254
 
@@ -154,43 +256,75 @@ static VALUE proc_wait3(int argc, VALUE *argv, VALUE mod){
154
256
  flags = NUM2INT(v_flags);
155
257
  }
156
258
 
157
- bzero(&r, sizeof(r));
158
- pid = wait3(&status, flags, &r);
259
+ VALUE scheduler = rb_fiber_scheduler_current();
260
+ if (!NIL_P(scheduler)) {
261
+ /* Use fiber scheduler - wait for any child (pid = -1) */
262
+ VALUE result = rb_fiber_scheduler_process_wait(scheduler, (rb_pid_t)-1, flags);
263
+
264
+ if (NIL_P(result)) {
265
+ return Qnil;
266
+ }
159
267
 
160
- if(pid < 0){
268
+ /* Extract pid and status from Process::Status object */
269
+ pid_t pid = NUM2PIDT(rb_funcall(result, rb_intern("pid"), 0));
270
+ int status = NUM2INT(rb_funcall(result, rb_intern("to_i"), 0));
271
+
272
+ RB_GC_GUARD(result);
273
+
274
+ v_last_status = build_procstat_without_rusage(pid, status);
275
+ rb_last_status_set(status, pid);
276
+ OBJ_FREEZE(v_last_status);
277
+
278
+ return v_last_status;
279
+ }
280
+
281
+ /* No fiber scheduler - use thread-based blocking */
282
+ bzero(&args, sizeof(args));
283
+ args.flags = flags;
284
+
285
+ /* Retry on EINTR. The RUBY_UBF_PROCESS unblocking function sends a signal
286
+ * to interrupt the blocking call. On macOS (and some other platforms),
287
+ * wait3() is not automatically restarted after being interrupted, so we
288
+ * must retry manually.
289
+ */
290
+ do {
291
+ rb_thread_call_without_gvl(wait3_without_gvl, &args, RUBY_UBF_PROCESS, NULL);
292
+ } while(args.pid < 0 && errno == EINTR);
293
+
294
+ if(args.pid < 0){
161
295
  rb_sys_fail("wait3");
162
296
  }
163
- else if(pid > 0){
297
+ else if(args.pid > 0){
164
298
  v_last_status = rb_struct_new(v_procstat_struct,
165
- INT2FIX(pid),
166
- INT2FIX(status),
167
- rb_float_new((double)r.ru_utime.tv_sec+(double)r.ru_utime.tv_usec/1e6),
168
- rb_float_new((double)r.ru_stime.tv_sec+(double)r.ru_stime.tv_usec/1e6),
169
- LONG2NUM(r.ru_maxrss),
170
- LONG2NUM(r.ru_ixrss),
171
- LONG2NUM(r.ru_idrss),
172
- LONG2NUM(r.ru_isrss),
173
- LONG2NUM(r.ru_minflt),
174
- LONG2NUM(r.ru_majflt),
175
- LONG2NUM(r.ru_nswap),
176
- LONG2NUM(r.ru_inblock),
177
- LONG2NUM(r.ru_oublock),
178
- LONG2NUM(r.ru_msgsnd),
179
- LONG2NUM(r.ru_msgrcv),
180
- LONG2NUM(r.ru_nsignals),
181
- LONG2NUM(r.ru_nvcsw),
182
- LONG2NUM(r.ru_nivcsw),
183
- pst_wifstopped(status),
184
- pst_wifsignaled(status),
185
- pst_wifexited(status),
186
- pst_success_p(status),
187
- pst_wcoredump(status),
188
- pst_wexitstatus(status),
189
- pst_wtermsig(status),
190
- pst_wstopsig(status)
299
+ INT2FIX(args.pid),
300
+ INT2FIX(args.status),
301
+ rb_float_new((double)args.rusage.ru_utime.tv_sec+(double)args.rusage.ru_utime.tv_usec/1e6),
302
+ rb_float_new((double)args.rusage.ru_stime.tv_sec+(double)args.rusage.ru_stime.tv_usec/1e6),
303
+ LONG2NUM(args.rusage.ru_maxrss),
304
+ LONG2NUM(args.rusage.ru_ixrss),
305
+ LONG2NUM(args.rusage.ru_idrss),
306
+ LONG2NUM(args.rusage.ru_isrss),
307
+ LONG2NUM(args.rusage.ru_minflt),
308
+ LONG2NUM(args.rusage.ru_majflt),
309
+ LONG2NUM(args.rusage.ru_nswap),
310
+ LONG2NUM(args.rusage.ru_inblock),
311
+ LONG2NUM(args.rusage.ru_oublock),
312
+ LONG2NUM(args.rusage.ru_msgsnd),
313
+ LONG2NUM(args.rusage.ru_msgrcv),
314
+ LONG2NUM(args.rusage.ru_nsignals),
315
+ LONG2NUM(args.rusage.ru_nvcsw),
316
+ LONG2NUM(args.rusage.ru_nivcsw),
317
+ pst_wifstopped(args.status),
318
+ pst_wifsignaled(args.status),
319
+ pst_wifexited(args.status),
320
+ pst_success_p(args.status),
321
+ pst_wcoredump(args.status),
322
+ pst_wexitstatus(args.status),
323
+ pst_wtermsig(args.status),
324
+ pst_wstopsig(args.status)
191
325
  );
192
326
 
193
- rb_last_status_set(status, pid);
327
+ rb_last_status_set(args.status, args.pid);
194
328
  OBJ_FREEZE(v_last_status);
195
329
 
196
330
  return v_last_status;
@@ -211,59 +345,95 @@ static VALUE proc_wait3(int argc, VALUE *argv, VALUE mod){
211
345
  * This method is not supported on all platforms.
212
346
  *
213
347
  * Some +flags+ are not supported on all platforms.
348
+ *
349
+ * Note: When a fiber scheduler is active (Ruby 3.0+), this method will
350
+ * cooperate with the scheduler. However, rusage information will not be
351
+ * available and those fields will be set to 0 in the returned struct.
214
352
  */
215
353
  static VALUE proc_wait4(int argc, VALUE *argv, VALUE mod){
216
- int status;
217
- int flags = 0;
218
- struct rusage r;
219
- pid_t pid;
354
+ struct wait4_args args;
220
355
  VALUE v_pid;
221
356
  VALUE v_flags = Qnil;
357
+ pid_t pid;
358
+ int flags = 0;
222
359
 
223
360
  rb_scan_args(argc, argv, "11", &v_pid, &v_flags);
224
361
 
225
- pid = NUM2INT(v_pid);
362
+ pid = NUM2PIDT(v_pid);
226
363
 
227
364
  if(RTEST(v_flags))
228
365
  flags = NUM2INT(v_flags);
229
366
 
230
- bzero(&r, sizeof(r));
231
- pid = wait4(pid, &status, flags, &r);
367
+ VALUE scheduler = rb_fiber_scheduler_current();
368
+ if (!NIL_P(scheduler)) {
369
+ /* Use fiber scheduler */
370
+ VALUE result = rb_fiber_scheduler_process_wait(scheduler, pid, flags);
371
+
372
+ if (NIL_P(result)) {
373
+ return Qnil;
374
+ }
375
+
376
+ /* Extract pid and status from Process::Status object */
377
+ pid_t rpid = NUM2PIDT(rb_funcall(result, rb_intern("pid"), 0));
378
+ int status = NUM2INT(rb_funcall(result, rb_intern("to_i"), 0));
232
379
 
233
- if(pid < 0){
380
+ RB_GC_GUARD(result);
381
+
382
+ v_last_status = build_procstat_without_rusage(rpid, status);
383
+ rb_last_status_set(status, rpid);
384
+ OBJ_FREEZE(v_last_status);
385
+
386
+ return v_last_status;
387
+ }
388
+
389
+ /* No fiber scheduler - use thread-based blocking */
390
+ bzero(&args, sizeof(args));
391
+ args.pid = pid;
392
+ args.flags = flags;
393
+
394
+ /* Retry on EINTR. The RUBY_UBF_PROCESS unblocking function sends a signal
395
+ * to interrupt the blocking call. On macOS (and some other platforms),
396
+ * wait4() is not automatically restarted after being interrupted, so we
397
+ * must retry manually.
398
+ */
399
+ do {
400
+ rb_thread_call_without_gvl(wait4_without_gvl, &args, RUBY_UBF_PROCESS, NULL);
401
+ } while(args.result < 0 && errno == EINTR);
402
+
403
+ if(args.result < 0){
234
404
  rb_sys_fail("wait4");
235
405
  }
236
- else if(pid > 0){
406
+ else if(args.result > 0){
237
407
  v_last_status = rb_struct_new(v_procstat_struct,
238
- INT2FIX(pid),
239
- INT2FIX(status),
240
- rb_float_new((double)r.ru_utime.tv_sec+(double)r.ru_utime.tv_usec/1e6),
241
- rb_float_new((double)r.ru_stime.tv_sec+(double)r.ru_stime.tv_usec/1e6),
242
- LONG2NUM(r.ru_maxrss),
243
- LONG2NUM(r.ru_ixrss),
244
- LONG2NUM(r.ru_idrss),
245
- LONG2NUM(r.ru_isrss),
246
- LONG2NUM(r.ru_minflt),
247
- LONG2NUM(r.ru_majflt),
248
- LONG2NUM(r.ru_nswap),
249
- LONG2NUM(r.ru_inblock),
250
- LONG2NUM(r.ru_oublock),
251
- LONG2NUM(r.ru_msgsnd),
252
- LONG2NUM(r.ru_msgrcv),
253
- LONG2NUM(r.ru_nsignals),
254
- LONG2NUM(r.ru_nvcsw),
255
- LONG2NUM(r.ru_nivcsw),
256
- pst_wifstopped(status),
257
- pst_wifsignaled(status),
258
- pst_wifexited(status),
259
- pst_success_p(status),
260
- pst_wcoredump(status),
261
- pst_wexitstatus(status),
262
- pst_wtermsig(status),
263
- pst_wstopsig(status)
408
+ INT2FIX(args.result),
409
+ INT2FIX(args.status),
410
+ rb_float_new((double)args.rusage.ru_utime.tv_sec+(double)args.rusage.ru_utime.tv_usec/1e6),
411
+ rb_float_new((double)args.rusage.ru_stime.tv_sec+(double)args.rusage.ru_stime.tv_usec/1e6),
412
+ LONG2NUM(args.rusage.ru_maxrss),
413
+ LONG2NUM(args.rusage.ru_ixrss),
414
+ LONG2NUM(args.rusage.ru_idrss),
415
+ LONG2NUM(args.rusage.ru_isrss),
416
+ LONG2NUM(args.rusage.ru_minflt),
417
+ LONG2NUM(args.rusage.ru_majflt),
418
+ LONG2NUM(args.rusage.ru_nswap),
419
+ LONG2NUM(args.rusage.ru_inblock),
420
+ LONG2NUM(args.rusage.ru_oublock),
421
+ LONG2NUM(args.rusage.ru_msgsnd),
422
+ LONG2NUM(args.rusage.ru_msgrcv),
423
+ LONG2NUM(args.rusage.ru_nsignals),
424
+ LONG2NUM(args.rusage.ru_nvcsw),
425
+ LONG2NUM(args.rusage.ru_nivcsw),
426
+ pst_wifstopped(args.status),
427
+ pst_wifsignaled(args.status),
428
+ pst_wifexited(args.status),
429
+ pst_success_p(args.status),
430
+ pst_wcoredump(args.status),
431
+ pst_wexitstatus(args.status),
432
+ pst_wtermsig(args.status),
433
+ pst_wstopsig(args.status)
264
434
  );
265
435
 
266
- rb_last_status_set(status, pid);
436
+ rb_last_status_set(args.status, args.result);
267
437
  OBJ_FREEZE(v_last_status);
268
438
 
269
439
  return v_last_status;
@@ -314,20 +484,18 @@ static VALUE proc_wait4(int argc, VALUE *argv, VALUE mod){
314
484
  */
315
485
  static VALUE proc_waitid(int argc, VALUE* argv, VALUE mod){
316
486
  VALUE v_type, v_id, v_options;
317
- siginfo_t infop;
318
- idtype_t idtype;
319
- id_t id = 0;
320
- int options = 0;
487
+ struct waitid_args args;
321
488
 
322
489
  rb_scan_args(argc, argv, "12", &v_type, &v_id, &v_options);
323
490
 
324
- idtype = NUM2INT(v_type);
491
+ bzero(&args, sizeof(args));
492
+ args.idtype = NUM2INT(v_type);
325
493
 
326
494
  if(RTEST(v_id))
327
- id = NUM2INT(v_id);
495
+ args.id = NUM2INT(v_id);
328
496
 
329
497
  if(RTEST(v_options))
330
- options = NUM2INT(v_options);
498
+ args.options = NUM2INT(v_options);
331
499
 
332
500
  /* The Linux man page for waitid() says to zero out the pid field and check
333
501
  * its value after the call to waitid() to detect if there were children in
@@ -335,10 +503,19 @@ static VALUE proc_waitid(int argc, VALUE* argv, VALUE mod){
335
503
  * simply check the infop.si_signo struct member against SI_NOINFO.
336
504
  */
337
505
  #ifndef SI_NOINFO
338
- infop.si_pid = 0;
506
+ args.infop.si_pid = 0;
339
507
  #endif
340
508
 
341
- if(waitid(idtype, id, &infop, options) == -1)
509
+ /* Retry on EINTR. The RUBY_UBF_PROCESS unblocking function sends a signal
510
+ * to interrupt the blocking call. On macOS (and some other platforms),
511
+ * waitid() is not automatically restarted after being interrupted, so we
512
+ * must retry manually.
513
+ */
514
+ do {
515
+ rb_thread_call_without_gvl(waitid_without_gvl, &args, RUBY_UBF_PROCESS, NULL);
516
+ } while(args.result == -1 && errno == EINTR);
517
+
518
+ if(args.result == -1)
342
519
  rb_sys_fail("waitid");
343
520
 
344
521
  /* If the si_code struct member returns SI_NOINFO, or the si_pid member
@@ -352,13 +529,13 @@ static VALUE proc_waitid(int argc, VALUE* argv, VALUE mod){
352
529
  */
353
530
 
354
531
  #ifdef SI_NOINFO
355
- if(infop.si_code == SI_NOINFO){
532
+ if(args.infop.si_code == SI_NOINFO){
356
533
  #else
357
- if(infop.si_pid == 0){
534
+ if(args.infop.si_pid == 0){
358
535
  #endif
359
536
  v_last_status = rb_struct_new(v_siginfo_struct,
360
- INT2FIX(infop.si_signo),
361
- INT2FIX(infop.si_errno),
537
+ INT2FIX(args.infop.si_signo),
538
+ INT2FIX(args.infop.si_errno),
362
539
  Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, /* code, pid, uid, utime, status, stime */
363
540
  #ifdef HAVE_ST_SI_TRAPNO
364
541
  Qnil,
@@ -424,8 +601,8 @@ static VALUE proc_waitid(int argc, VALUE* argv, VALUE mod){
424
601
  VALUE v_state = Qnil;
425
602
  #endif
426
603
  VALUE v_band = Qnil, v_entity = Qnil;
427
- int sig = infop.si_signo;
428
- int code = infop.si_code;
604
+ int sig = args.infop.si_signo;
605
+ int code = args.infop.si_code;
429
606
 
430
607
  #if defined(HAVE_ST_SI_SYSARG) || defined(HAVE_ST_SI_MSTATE)
431
608
  int i = 0;
@@ -437,13 +614,13 @@ static VALUE proc_waitid(int argc, VALUE* argv, VALUE mod){
437
614
  */
438
615
  if(sig == SIGCHLD){
439
616
  #ifdef HAVE_ST_SI_UTIME
440
- v_utime = ULL2NUM(infop.si_utime);
617
+ v_utime = ULL2NUM(args.infop.si_utime);
441
618
  #endif
442
619
  #ifdef HAVE_ST_SI_STATUS
443
- v_status = ULL2NUM(infop.si_status);
620
+ v_status = ULL2NUM(args.infop.si_status);
444
621
  #endif
445
622
  #ifdef HAVE_ST_SI_STIME
446
- v_stime = ULL2NUM(infop.si_stime);
623
+ v_stime = ULL2NUM(args.infop.si_stime);
447
624
  #endif
448
625
  }
449
626
 
@@ -451,66 +628,66 @@ static VALUE proc_waitid(int argc, VALUE* argv, VALUE mod){
451
628
  sig == SIGTRAP)
452
629
  {
453
630
  #ifdef HAVE_ST_SI_TRAPNO
454
- v_trapno = INT2FIX(infop.si_trapno);
631
+ v_trapno = INT2FIX(args.infop.si_trapno);
455
632
  #endif
456
633
  #ifdef HAVE_ST_SI_PC
457
- v_pc = INT2FIX(infop.si_pc);
634
+ v_pc = INT2FIX(args.infop.si_pc);
458
635
  #endif
459
636
  }
460
637
 
461
638
  if(sig == SIGXFSZ){
462
639
  #ifdef HAVE_ST_SI_FD
463
- v_fd = INT2FIX(infop.si_fd);
640
+ v_fd = INT2FIX(args.infop.si_fd);
464
641
  #endif
465
642
  if(code == POLL_IN || code == POLL_OUT || code == POLL_MSG){
466
- v_band = LONG2FIX(infop.si_band);
643
+ v_band = LONG2FIX(args.infop.si_band);
467
644
  }
468
645
  }
469
646
 
470
647
  if(sig == SIGPROF){
471
648
  #ifdef HAVE_ST_SI_SYSARG
472
- int ssize = sizeof(infop.si_sysarg) / sizeof(infop.si_sysarg[0]);
649
+ int ssize = sizeof(args.infop.si_sysarg) / sizeof(args.infop.si_sysarg[0]);
473
650
  v_sysarg = rb_ary_new();
474
651
 
475
652
  for(i = 0; i < ssize; i++)
476
- rb_ary_push(v_sysarg, LONG2FIX(infop.si_sysarg[i]));
653
+ rb_ary_push(v_sysarg, LONG2FIX(args.infop.si_sysarg[i]));
477
654
  #endif
478
655
  #ifdef HAVE_ST_SI_MSTATE
479
- int msize = sizeof(infop.si_mstate) / sizeof(infop.si_mstate[0]);
656
+ int msize = sizeof(args.infop.si_mstate) / sizeof(args.infop.si_mstate[0]);
480
657
  v_state = rb_ary_new();
481
658
 
482
659
  for(i = 0; i < msize; i++)
483
- rb_ary_push(v_state, INT2FIX(infop.si_mstate[i]));
660
+ rb_ary_push(v_state, INT2FIX(args.infop.si_mstate[i]));
484
661
  #endif
485
662
  #ifdef HAVE_ST_SI_FADDR
486
- v_addr = INT2FIX(infop.si_faddr);
663
+ v_addr = INT2FIX(args.infop.si_faddr);
487
664
  #endif
488
665
  #ifdef HAVE_ST_SI_SYSCALL
489
- v_syscall = INT2FIX(infop.si_syscall);
666
+ v_syscall = INT2FIX(args.infop.si_syscall);
490
667
  #endif
491
668
  #ifdef HAVE_ST_SI_NSYSARG
492
- v_nsysarg = INT2FIX(infop.si_nsysarg);
669
+ v_nsysarg = INT2FIX(args.infop.si_nsysarg);
493
670
  #endif
494
671
  #ifdef HAVE_ST_SI_FAULT
495
- v_fault = INT2FIX(infop.si_fault);
672
+ v_fault = INT2FIX(args.infop.si_fault);
496
673
  #endif
497
674
  #ifdef HAVE_ST_SI_TSTAMP
498
- v_time = rb_time_new(infop.si_tstamp.tv_sec,infop.si_tstamp.tv_nsec);
675
+ v_time = rb_time_new(args.infop.si_tstamp.tv_sec,args.infop.si_tstamp.tv_nsec);
499
676
  #endif
500
677
  }
501
678
 
502
679
  #ifdef SIGXRES
503
680
  if(sig == SIGXRES){
504
- v_entity = INT2FIX(infop.si_entity);
681
+ v_entity = INT2FIX(args.infop.si_entity);
505
682
  }
506
683
  #endif
507
684
 
508
685
  v_last_status = rb_struct_new(v_siginfo_struct,
509
- INT2FIX(infop.si_signo), // Probably SIGCHLD
510
- INT2FIX(infop.si_errno), // 0 means no error
511
- INT2FIX(infop.si_code), // Should be anything but SI_NOINFO
512
- INT2FIX(infop.si_pid), // Real PID that sent the signal
513
- INT2FIX(infop.si_uid), // Real UID of process that sent signal
686
+ INT2FIX(args.infop.si_signo), // Probably SIGCHLD
687
+ INT2FIX(args.infop.si_errno), // 0 means no error
688
+ INT2FIX(args.infop.si_code), // Should be anything but SI_NOINFO
689
+ INT2FIX(args.infop.si_pid), // Real PID that sent the signal
690
+ INT2FIX(args.infop.si_uid), // Real UID of process that sent signal
514
691
  v_utime,
515
692
  v_status,
516
693
  v_stime,
@@ -566,7 +743,7 @@ static VALUE proc_waitid(int argc, VALUE* argv, VALUE mod){
566
743
  * be sure to leave off the leading 'SIG' substring, e.g. use 'INT' instead
567
744
  * of 'SIGINT'.
568
745
  *
569
- * Note that not all platforms (notably Linux) do not support automatically
746
+ * Note that not all platforms (notably Linux) support automatically
570
747
  * converting strings to their corresponding signal values, so it is
571
748
  * recommended that you always use an array of numeric values.
572
749
  *
@@ -600,10 +777,12 @@ static VALUE proc_pause(int argc, VALUE* argv, VALUE mod){
600
777
  if(strlcpy(signame, StringValuePtr(v_val), max) >= max)
601
778
  rb_raise(rb_eArgError, "string too large");
602
779
  #else
603
- if(RSTRING(v_val)->len > max)
780
+ if(RSTRING_LEN(v_val) >= max)
604
781
  rb_raise(rb_eArgError, "string too large");
605
- else
606
- strncpy(signame, RSTRING(v_val)->ptr, max);
782
+ else{
783
+ strncpy(signame, RSTRING_PTR(v_val), max);
784
+ signame[max-1] = '\0';
785
+ }
607
786
  #endif
608
787
 
609
788
  #ifdef HAVE_STR2SIG
@@ -619,17 +798,22 @@ static VALUE proc_pause(int argc, VALUE* argv, VALUE mod){
619
798
  signum = NUM2INT(v_val);
620
799
  }
621
800
 
622
- memset(&act, 0, sizeof(act));
801
+ bzero(&act, sizeof(act));
623
802
  act.sa_flags = SA_SIGINFO;
624
803
  act.sa_sigaction = sigproc;
625
804
  res = sigaction(signum, &act, &sa);
626
805
 
627
806
  if(res)
628
- rb_sys_fail("sigaction");
807
+ rb_sys_fail("sigaction");
629
808
  }
630
809
  }
631
810
 
632
- return INT2FIX(pause()); /* Should always be -1 */
811
+ {
812
+ struct pause_args pargs;
813
+ rb_thread_call_without_gvl(pause_without_gvl, &pargs, RUBY_UBF_PROCESS, NULL);
814
+ RB_GC_GUARD(v_signals);
815
+ return INT2FIX(pargs.result); /* Should always be -1 */
816
+ }
633
817
  }
634
818
 
635
819
  /*
@@ -696,8 +880,17 @@ static VALUE proc_sigsend(int argc, VALUE* argv, VALUE mod){
696
880
  char signame[SIG2STR_MAX];
697
881
  unsigned int max = SIG2STR_MAX;
698
882
 
883
+ #ifdef HAVE_STRLCPY
699
884
  if(strlcpy(signame, StringValuePtr(v_signal), max) >= max)
700
885
  rb_raise(rb_eArgError, "string too large");
886
+ #else
887
+ if(RSTRING_LEN(v_signal) >= max)
888
+ rb_raise(rb_eArgError, "string too large");
889
+ else{
890
+ strncpy(signame, RSTRING_PTR(v_signal), max);
891
+ signame[max-1] = '\0';
892
+ }
893
+ #endif
701
894
 
702
895
  if(str2sig(signame,&sig) != 0)
703
896
  rb_sys_fail("str2sig");
@@ -973,8 +1166,8 @@ void Init_wait3(void)
973
1166
  rb_define_const(rb_mProcess, "RUSAGE_THREAD", INT2FIX(RUSAGE_THREAD));
974
1167
  #endif
975
1168
 
976
- /* 1.9.3: The version of the proc-wait3 library */
977
- rb_define_const(rb_mProcess, "WAIT3_VERSION", rb_str_freeze(rb_str_new2("1.9.3")));
1169
+ /* 2.1.0: The version of the proc-wait3 library */
1170
+ rb_define_const(rb_mProcess, "WAIT3_VERSION", rb_str_freeze(rb_str_new2("2.1.0")));
978
1171
 
979
1172
  /* Define this last in our Init_wait3 function */
980
1173
  rb_define_readonly_variable("$last_status", &v_last_status);
data/proc-wait3.gemspec CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = 'proc-wait3'
5
- spec.version = '1.9.3'
5
+ spec.version = '2.1.0'
6
6
  spec.author = 'Daniel J. Berger'
7
7
  spec.license = 'Apache-2.0'
8
8
  spec.email = 'djberg96@gmail.com'
@@ -26,7 +26,8 @@ Gem::Specification.new do |spec|
26
26
  'source_code_uri' => 'https://github.com/djberg96/proc-wait3',
27
27
  'wiki_uri' => 'https://github.com/djberg96/proc-wait3/wiki',
28
28
  'rubygems_mfa_required' => 'true',
29
- 'github_repo' => 'https://github.com/djberg96/proc-wait3'
29
+ 'github_repo' => 'https://github.com/djberg96/proc-wait3',
30
+ 'funding_uri' => 'https://github.com/sponsors/djberg96'
30
31
  }
31
32
 
32
33
  spec.description = <<-EOF
@@ -48,7 +48,7 @@ RSpec.describe Process do
48
48
  end
49
49
 
50
50
  example 'version constant is set to expected value' do
51
- expect(Process::WAIT3_VERSION).to eq('1.9.3')
51
+ expect(Process::WAIT3_VERSION).to eq('2.1.0')
52
52
  expect(Process::WAIT3_VERSION).to be_frozen
53
53
  end
54
54
 
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,11 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: proc-wait3
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.3
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel J. Berger
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain:
11
10
  - |
@@ -35,7 +34,7 @@ cert_chain:
35
34
  ORVCZpRuCPpmC8qmqxUnARDArzucjaclkxjLWvCVHeFa9UP7K3Nl9oTjJNv+7/jM
36
35
  WZs4eecIcUc4tKdHxcAJ0MO/Dkqq7hGaiHpwKY76wQ1+8xAh
37
36
  -----END CERTIFICATE-----
38
- date: 2024-05-04 00:00:00.000000000 Z
37
+ date: 1980-01-02 00:00:00.000000000 Z
39
38
  dependencies:
40
39
  - !ruby/object:Gem::Dependency
41
40
  name: rake
@@ -133,7 +132,7 @@ metadata:
133
132
  wiki_uri: https://github.com/djberg96/proc-wait3/wiki
134
133
  rubygems_mfa_required: 'true'
135
134
  github_repo: https://github.com/djberg96/proc-wait3
136
- post_install_message:
135
+ funding_uri: https://github.com/sponsors/djberg96
137
136
  rdoc_options: []
138
137
  require_paths:
139
138
  - lib
@@ -148,8 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
147
  - !ruby/object:Gem::Version
149
148
  version: '0'
150
149
  requirements: []
151
- rubygems_version: 3.4.19
152
- signing_key:
150
+ rubygems_version: 3.6.9
153
151
  specification_version: 4
154
152
  summary: Adds wait3, wait4 and other methods to the Process module
155
153
  test_files:
metadata.gz.sig CHANGED
@@ -1,3 +1 @@
1
- bj<d������&qN
2
- �溨}gdԥwf*����J�˚䋓t��!FI��`<�ѝ��Gd9ʦ��<� ���`��,{R�����y�R��#9w̻�2zk� T�'@��h�װh��S� �q��_�����v� jљ�J��Y�g2��~C(�>ҵpMNn�f��I��T��dE�פ�SCPn�2h��(�X$��|Rb�n6�9��O��Oȉ^�'
3
- p ���pN���"��n-�M~е:Jc��U��lQ�c���D�z�˾����p�� "$Gu'��w'�[47����q�E� c/�JCh������$��~/��E"[7����R��β�3������Iˍ���9�s�vG��.��8�ZX�ô�B�
1
+ `G��8��x߲���ȱ��r��/��]�@ڱl'�-N����DFJF�<`��{!(P�\���l�8;�Z#��z�x�S��L��D<��s��9�gF��I~->��qø- H�{=Q�g