childprocess 0.4.0 → 0.4.1.rc1

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
  SHA1:
3
- metadata.gz: 04d64c66d047e986bc57da09cbb13b3a4e791e28
4
- data.tar.gz: ce4828aee09a5499e7f5858c93401dd42c851af9
3
+ metadata.gz: 3f15d02987e0f6fb61ee1de1d2a7a58463ed533e
4
+ data.tar.gz: 00bae3b277c92838151774688d3d69c691dc74f2
5
5
  SHA512:
6
- metadata.gz: d57f5c928a964451e325c06b60cf8680c1e0c0f3141b38ddb7d381e0914f5f4286871a8d4a1766e62375907963d7a977ff0d4f6732c4408e7eb8f7855f93894a
7
- data.tar.gz: ddfcf22a34b38b47e5c7d42fab0b4a0449d0332b78dd118f191387ec72f81b8bbad9c68c94e08748c243aa7d3205f2b48edb0bd2977b0b5cc85d54905a347b35
6
+ metadata.gz: dccbf2db7db79bb2dcaed2726ddb1b71e9c6620bc80dd22bf4b45c6c1d9142e56731961294df701296972efada62bed9db15a12bf2af0b35073f4469fef80b47
7
+ data.tar.gz: ba1b3c25f5a7141b5632960054d9617af5fbf40b360dba30d0c73a34aac7f0f42cfdfe909066cc7e4408a18c6340901aaa433a5b3d729e469d465e3e6162fd23
data/.gitignore CHANGED
@@ -20,5 +20,6 @@ pkg
20
20
  .rbx
21
21
  Gemfile.lock
22
22
  .ruby-version
23
+ .bundle
23
24
 
24
25
  ## PROJECT::SPECIFIC
@@ -10,7 +10,5 @@ env:
10
10
  - CHILDPROCESS_POSIX_SPAWN=false
11
11
  matrix:
12
12
  allow_failures:
13
- - rvm: jruby
14
- env: CHILDPROCESS_POSIX_SPAWN=true
15
13
  - rvm: rbx
16
14
  - rvm: ruby-head
@@ -18,6 +18,8 @@ module ChildProcess
18
18
  end
19
19
 
20
20
  @pid = fork {
21
+ ::Process.setpgid 0, 0 # same process group as parent
22
+
21
23
  if @cwd
22
24
  Dir.chdir(@cwd)
23
25
  end
@@ -12,7 +12,6 @@ module ChildProcess
12
12
  pid_ptr = FFI::MemoryPointer.new(:pid_t)
13
13
  actions = Lib::FileActions.new
14
14
  attrs = Lib::Attrs.new
15
- flags = 0
16
15
 
17
16
  if @io
18
17
  if @io.stdout
@@ -34,11 +33,8 @@ module ChildProcess
34
33
  actions.add_close fileno_for(writer)
35
34
  end
36
35
 
37
- if defined? Platform::POSIX_SPAWN_USEVFORK
38
- flags |= Platform::POSIX_SPAWN_USEVFORK
39
- end
40
-
41
- attrs.flags = flags
36
+ attrs.flags |= Platform::POSIX_SPAWN_SETPGROUP
37
+ attrs.flags |= Platform::POSIX_SPAWN_USEVFORK if defined? Platform::POSIX_SPAWN_USEVFORK
42
38
 
43
39
  # wrap in helper classes in order to avoid GC'ed pointers
44
40
  argv = Argv.new(@args)
@@ -66,7 +66,7 @@ module ChildProcess
66
66
  assert_started
67
67
 
68
68
  log "sending #{sig}"
69
- ::Process.kill sig, @pid
69
+ ::Process.kill sig, -@pid
70
70
  end
71
71
 
72
72
  end # Process
@@ -1,3 +1,3 @@
1
1
  module ChildProcess
2
- VERSION = "0.4.0"
2
+ VERSION = "0.4.1.rc1"
3
3
  end
@@ -23,23 +23,25 @@ module ChildProcess
23
23
  end
24
24
  end
25
25
 
26
- def initialize(handle, pid)
27
- unless handle.kind_of?(FFI::Pointer)
28
- raise TypeError, "invalid handle: #{handle.inspect}"
26
+ attr_reader :pointer
27
+
28
+ def initialize(pointer, pid)
29
+ unless pointer.kind_of?(FFI::Pointer)
30
+ raise TypeError, "invalid handle: #{pointer.inspect}"
29
31
  end
30
32
 
31
- if handle.null?
32
- raise ArgumentError, "handle is null: #{handle.inspect}"
33
+ if pointer.null?
34
+ raise ArgumentError, "handle is null: #{pointer.inspect}"
33
35
  end
34
36
 
35
- @pid = pid
36
- @handle = handle
37
- @closed = false
37
+ @pid = pid
38
+ @pointer = pointer
39
+ @closed = false
38
40
  end
39
41
 
40
42
  def exit_code
41
43
  code_pointer = FFI::MemoryPointer.new :ulong
42
- ok = Lib.get_exit_code(@handle, code_pointer)
44
+ ok = Lib.get_exit_code(@pointer, code_pointer)
43
45
 
44
46
  if ok
45
47
  code_pointer.get_ulong(0)
@@ -58,14 +60,14 @@ module ChildProcess
58
60
  when WIN_SIGBREAK
59
61
  Lib.generate_console_ctrl_event(CTRL_BREAK_EVENT, @pid)
60
62
  when WIN_SIGKILL
61
- ok = Lib.terminate_process(@handle, @pid)
63
+ ok = Lib.terminate_process(@pointer, @pid)
62
64
  Lib.check_error ok
63
65
  else
64
66
  thread_id = FFI::MemoryPointer.new(:ulong)
65
67
  module_handle = Lib.get_module_handle("kernel32")
66
68
  proc_address = Lib.get_proc_address(module_handle, "ExitProcess")
67
69
 
68
- thread = Lib.create_remote_thread(@handle, 0, 0, proc_address, 0, 0, thread_id)
70
+ thread = Lib.create_remote_thread(@pointer, 0, 0, proc_address, 0, 0, thread_id)
69
71
  check_error thread
70
72
 
71
73
  Lib.wait_for_single_object(thread, 5)
@@ -76,12 +78,12 @@ module ChildProcess
76
78
  def close
77
79
  return if @closed
78
80
 
79
- Lib.close_handle(@handle)
81
+ Lib.close_handle(@pointer)
80
82
  @closed = true
81
83
  end
82
84
 
83
85
  def wait(milliseconds = nil)
84
- Lib.wait_for_single_object(@handle, milliseconds || INFINITE)
86
+ Lib.wait_for_single_object(@pointer, milliseconds || INFINITE)
85
87
  end
86
88
 
87
89
  end # Handle
@@ -1,30 +1,34 @@
1
1
  module ChildProcess
2
2
  module Windows
3
- FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
4
- FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000
3
+ FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
4
+ FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000
5
5
 
6
- PROCESS_ALL_ACCESS = 0x1F0FFF
7
- PROCESS_QUERY_INFORMATION = 0x0400
8
- PROCESS_VM_READ = 0x0010
9
- PROCESS_STILL_ACTIVE = 259
6
+ PROCESS_ALL_ACCESS = 0x1F0FFF
7
+ PROCESS_QUERY_INFORMATION = 0x0400
8
+ PROCESS_VM_READ = 0x0010
9
+ PROCESS_STILL_ACTIVE = 259
10
10
 
11
- INFINITE = 0xFFFFFFFF
11
+ INFINITE = 0xFFFFFFFF
12
12
 
13
- WIN_SIGINT = 2
14
- WIN_SIGBREAK = 3
15
- WIN_SIGKILL = 9
13
+ WIN_SIGINT = 2
14
+ WIN_SIGBREAK = 3
15
+ WIN_SIGKILL = 9
16
16
 
17
- CTRL_C_EVENT = 0
18
- CTRL_BREAK_EVENT = 1
17
+ CTRL_C_EVENT = 0
18
+ CTRL_BREAK_EVENT = 1
19
19
 
20
- DETACHED_PROCESS = 0x00000008
20
+ DETACHED_PROCESS = 0x00000008
21
21
 
22
- STARTF_USESTDHANDLES = 0x00000100
23
- INVALID_HANDLE_VALUE = -1
24
- HANDLE_FLAG_INHERIT = 0x00000001
22
+ STARTF_USESTDHANDLES = 0x00000100
23
+ INVALID_HANDLE_VALUE = -1
24
+ HANDLE_FLAG_INHERIT = 0x00000001
25
25
 
26
- DUPLICATE_SAME_ACCESS = 0x00000002
27
- CREATE_UNICODE_ENVIRONMENT = 0x00000400
26
+ DUPLICATE_SAME_ACCESS = 0x00000002
27
+ CREATE_UNICODE_ENVIRONMENT = 0x00000400
28
+
29
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000
30
+ JOB_OBJECT_EXTENDED_LIMIT_INFORMATION = 9
31
+ JOB_OBJECT_BASIC_LIMIT_INFORMATION = 2
28
32
 
29
33
  module Lib
30
34
  enum :wait_status, [
@@ -61,12 +65,6 @@ module ChildProcess
61
65
  :pointer,
62
66
  :pointer], :bool
63
67
 
64
- #
65
- # DWORD WINAPI GetLastError(void);
66
- #
67
-
68
- attach_function :get_last_error, :GetLastError, [], :ulong
69
-
70
68
  #
71
69
  # DWORD WINAPI FormatMessage(
72
70
  # __in DWORD dwFlags,
@@ -101,6 +99,35 @@ module ChildProcess
101
99
 
102
100
  attach_function :open_process, :OpenProcess, [:ulong, :bool, :ulong], :pointer
103
101
 
102
+ #
103
+ # HANDLE WINAPI CreateJobObject(
104
+ # _In_opt_ LPSECURITY_ATTRIBUTES lpJobAttributes,
105
+ # _In_opt_ LPCTSTR lpName
106
+ # );
107
+ #
108
+
109
+ attach_function :create_job_object, :CreateJobObjectA, [:pointer, :pointer], :pointer
110
+
111
+ #
112
+ # BOOL WINAPI AssignProcessToJobObject(
113
+ # _In_ HANDLE hJob,
114
+ # _In_ HANDLE hProcess
115
+ # );
116
+
117
+ attach_function :assign_process_to_job_object, :AssignProcessToJobObject, [:pointer, :pointer], :bool
118
+
119
+ #
120
+ # BOOL WINAPI SetInformationJobObject(
121
+ # _In_ HANDLE hJob,
122
+ # _In_ JOBOBJECTINFOCLASS JobObjectInfoClass,
123
+ # _In_ LPVOID lpJobObjectInfo,
124
+ # _In_ DWORD cbJobObjectInfoLength
125
+ # );
126
+ #
127
+
128
+ attach_function :set_information_job_object, :SetInformationJobObject, [:pointer, :int, :pointer, :ulong], :bool
129
+
130
+ #
104
131
  #
105
132
  # DWORD WINAPI WaitForSingleObject(
106
133
  # __in HANDLE hHandle,
@@ -243,7 +270,8 @@ module ChildProcess
243
270
  end
244
271
 
245
272
  def last_error_message
246
- errnum = get_last_error
273
+ errnum = FFI.errno
274
+
247
275
  buf = FFI::MemoryPointer.new :char, 512
248
276
 
249
277
  size = format_message(
@@ -259,6 +287,18 @@ module ChildProcess
259
287
  end
260
288
  end
261
289
 
290
+ def each_child_of(pid, &blk)
291
+ raise NotImplementedError
292
+
293
+ # http://stackoverflow.com/questions/1173342/terminate-a-process-tree-c-for-windows?rq=1
294
+
295
+ # for each process entry
296
+ # if pe.th32ParentProcessID == pid
297
+ # Handle.open(pe.pe.th32ProcessId, &blk)
298
+ # end
299
+ #
300
+ end
301
+
262
302
  def handle_for(fd_or_io)
263
303
  if fd_or_io.kind_of?(IO) || fd_or_io.respond_to?(:fileno)
264
304
  if ChildProcess.jruby?
@@ -344,6 +384,17 @@ module ChildProcess
344
384
  bool or raise Error, last_error_message
345
385
  end
346
386
 
387
+ def alive?(pid)
388
+ handle = Lib.open_process(PROCESS_ALL_ACCESS, false, pid)
389
+ if handle.null?
390
+ false
391
+ else
392
+ ptr = FFI::MemoryPointer.new :ulong
393
+ Lib.check_error Lib.get_exit_code(handle, ptr)
394
+ ptr.read_ulong == PROCESS_STILL_ACTIVE
395
+ end
396
+ end
397
+
347
398
  def no_hang?(flags)
348
399
  (flags & Process::WNOHANG) == Process::WNOHANG
349
400
  end
@@ -11,13 +11,13 @@ module ChildProcess
11
11
  def stop(timeout = 3)
12
12
  assert_started
13
13
 
14
- # just kill right away on windows.
15
14
  log "sending KILL"
16
15
  @handle.send(WIN_SIGKILL)
17
16
 
18
17
  poll_for_exit(timeout)
19
18
  ensure
20
19
  @handle.close
20
+ @job.close
21
21
  end
22
22
 
23
23
  def wait
@@ -27,6 +27,7 @@ module ChildProcess
27
27
  @handle.wait
28
28
  @exit_code = @handle.exit_code
29
29
  @handle.close
30
+ @job.close
30
31
 
31
32
  @exit_code
32
33
  end
@@ -63,9 +64,12 @@ module ChildProcess
63
64
  builder.stderr = @io.stderr
64
65
  end
65
66
 
67
+ @job = Job.new
66
68
  @pid = builder.start
67
69
  @handle = Handle.open @pid
68
70
 
71
+ @job << @handle
72
+
69
73
  if duplex?
70
74
  raise Error, "no stdin stream" unless builder.stdin
71
75
  io._stdin = builder.stdin
@@ -74,6 +78,39 @@ module ChildProcess
74
78
  self
75
79
  end
76
80
 
81
+ class Job
82
+ def initialize
83
+ @pointer = Lib.create_job_object(nil, nil)
84
+
85
+ if @pointer.nil? || @pointer.null?
86
+ raise Error, "unable to create job object"
87
+ end
88
+
89
+ basic = JobObjectBasicLimitInformation.new
90
+ basic[:LimitFlags] = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
91
+
92
+ extended = JobObjectExtendedLimitInformation.new
93
+ extended[:BasicLimitInformation] = basic
94
+
95
+ ret = Lib.set_information_job_object(
96
+ @pointer,
97
+ JOB_OBJECT_EXTENDED_LIMIT_INFORMATION,
98
+ extended,
99
+ extended.size
100
+ )
101
+
102
+ Lib.check_error ret
103
+ end
104
+
105
+ def <<(handle)
106
+ Lib.check_error Lib.assign_process_to_job_object(@pointer, handle.pointer)
107
+ end
108
+
109
+ def close
110
+ Lib.close_handle @pointer
111
+ end
112
+ end
113
+
77
114
  end # Process
78
115
  end # Windows
79
116
  end # ChildProcess
@@ -17,6 +17,7 @@ module ChildProcess
17
17
  @stdin = nil
18
18
 
19
19
  @flags = 0
20
+ @job_ptr = nil
20
21
  @cmd_ptr = nil
21
22
  @env_ptr = nil
22
23
  @cwd_ptr = nil
@@ -79,5 +79,71 @@ module ChildProcess
79
79
  self[:bInheritHandle] = opts[:inherit] ? 1 : 0
80
80
  end
81
81
  end
82
+
83
+ #
84
+ # typedef struct _JOBOBJECT_BASIC_LIMIT_INFORMATION {
85
+ # LARGE_INTEGER PerProcessUserTimeLimit;
86
+ # LARGE_INTEGER PerJobUserTimeLimit;
87
+ # DWORD LimitFlags;
88
+ # SIZE_T MinimumWorkingSetSize;
89
+ # SIZE_T MaximumWorkingSetSize;
90
+ # DWORD ActiveProcessLimit;
91
+ # ULONG_PTR Affinity;
92
+ # DWORD PriorityClass;
93
+ # DWORD SchedulingClass;
94
+ # } JOBOBJECT_BASIC_LIMIT_INFORMATION, *PJOBOBJECT_BASIC_LIMIT_INFORMATION;
95
+ #
96
+ class JobObjectBasicLimitInformation < FFI::Struct
97
+ layout :PerProcessUserTimeLimit, :int64,
98
+ :PerJobUserTimeLimit, :int64,
99
+ :LimitFlags, :ulong,
100
+ :MinimumWorkingSetSize, :size_t,
101
+ :MaximumWorkingSetSize, :size_t,
102
+ :ActiveProcessLimit, :ulong,
103
+ :Affinity, :pointer,
104
+ :PriorityClass, :ulong,
105
+ :SchedulingClass, :ulong
106
+ end
107
+
108
+ #
109
+ # typedef struct _IO_COUNTERS {
110
+ # ULONGLONG ReadOperationCount;
111
+ # ULONGLONG WriteOperationCount;
112
+ # ULONGLONG OtherOperationCount;
113
+ # ULONGLONG ReadTransferCount;
114
+ # ULONGLONG WriteTransferCount;
115
+ # ULONGLONG OtherTransferCount;
116
+ # } IO_COUNTERS, *PIO_COUNTERS;
117
+ #
118
+
119
+ class IoCounters < FFI::Struct
120
+ layout :ReadOperationCount, :ulong_long,
121
+ :WriteOperationCount, :ulong_long,
122
+ :OtherOperationCount, :ulong_long,
123
+ :ReadTransferCount, :ulong_long,
124
+ :WriteTransferCount, :ulong_long,
125
+ :OtherTransferCount, :ulong_long
126
+ end
127
+ #
128
+ # typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
129
+ # JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
130
+ # IO_COUNTERS IoInfo;
131
+ # SIZE_T ProcessMemoryLimit;
132
+ # SIZE_T JobMemoryLimit;
133
+ # SIZE_T PeakProcessMemoryUsed;
134
+ # SIZE_T PeakJobMemoryUsed;
135
+ # } JOBOBJECT_EXTENDED_LIMIT_INFORMATION, *PJOBOBJECT_EXTENDED_LIMIT_INFORMATION;
136
+ #
137
+
138
+ class JobObjectExtendedLimitInformation < FFI::Struct
139
+ layout :BasicLimitInformation, JobObjectBasicLimitInformation,
140
+ :IoInfo, IoCounters,
141
+ :ProcessMemoryLimit, :size_t,
142
+ :JobMemoryLimit, :size_t,
143
+ :PeakProcessMemoryUsed, :size_t,
144
+ :PeakJobMemoryUsed, :size_t
145
+ end
146
+
147
+
82
148
  end # Windows
83
149
  end # ChildProcess
@@ -73,8 +73,7 @@ describe ChildProcess do
73
73
  process.wait
74
74
  end
75
75
 
76
- file.rewind
77
- child_env = eval(file.read)
76
+ child_env = eval rewind_and_read(file)
78
77
  child_env['INHERITED'].should == 'yes'
79
78
  end
80
79
  end
@@ -88,9 +87,8 @@ describe ChildProcess do
88
87
  ENV['CHILD_ONLY'].should be_nil
89
88
 
90
89
  process.wait
91
- file.rewind
92
90
 
93
- child_env = eval(file.read)
91
+ child_env = eval rewind_and_read(file)
94
92
  child_env['CHILD_ONLY'].should == '1'
95
93
  end
96
94
  end
@@ -104,8 +102,7 @@ describe ChildProcess do
104
102
  process.start
105
103
  process.wait
106
104
 
107
- file.rewind
108
- child_env = eval(file.read)
105
+ child_env = eval rewind_and_read(file)
109
106
 
110
107
  child_env['INHERITED'].should eq 'yes'
111
108
  child_env['CHILD_ONLY'].should eq 'yes'
@@ -121,9 +118,8 @@ describe ChildProcess do
121
118
  process.start
122
119
 
123
120
  process.wait
124
- file.rewind
125
121
 
126
- child_env = eval(file.read)
122
+ child_env = eval rewind_and_read(file)
127
123
  child_env.should_not have_key('CHILDPROCESS_UNSET')
128
124
  end
129
125
  end
@@ -136,8 +132,7 @@ describe ChildProcess do
136
132
  process = write_argv(file.path, *args).start
137
133
  process.wait
138
134
 
139
- file.rewind
140
- file.read.should == args.inspect
135
+ rewind_and_read(file).should == args.inspect
141
136
  end
142
137
  end
143
138
 
@@ -158,8 +153,7 @@ describe ChildProcess do
158
153
 
159
154
  process.wait
160
155
 
161
- file.rewind
162
- file.read.should == expected_dir
156
+ rewind_and_read(file).should == expected_dir
163
157
  end
164
158
  end
165
159
 
@@ -170,8 +164,7 @@ describe ChildProcess do
170
164
  process = write_argv(file.path, *args).start
171
165
  process.wait
172
166
 
173
- file.rewind
174
- file.read.should == args.inspect
167
+ rewind_and_read(file).should == args.inspect
175
168
  end
176
169
  end
177
170
 
@@ -203,11 +196,23 @@ describe ChildProcess do
203
196
  process.start
204
197
  process.wait
205
198
 
206
- file.rewind
207
- file.read.should == dir
199
+ rewind_and_read(file).should == dir
208
200
  end
209
201
 
210
202
  Dir.pwd.should == orig_pwd
211
203
  }
212
204
  end
205
+
206
+ it 'kills the full process tree' do
207
+ Tempfile.open('kill-process-tree') do |file|
208
+ process = write_pid_in_sleepy_grand_child(file.path).start
209
+
210
+ pid = within(5) do
211
+ Integer(rewind_and_read(file)) rescue nil
212
+ end
213
+
214
+ process.stop
215
+ within(3) { alive?(pid).should be_false }
216
+ end
217
+ end
213
218
  end
@@ -20,11 +20,8 @@ describe ChildProcess do
20
20
  process.io.stdin.should be_nil
21
21
  process.wait
22
22
 
23
- out.rewind
24
- err.rewind
25
-
26
- out.read.should eq "0\n"
27
- err.read.should eq "1\n"
23
+ rewind_and_read(out).should eq "0\n"
24
+ rewind_and_read(err).should eq "1\n"
28
25
  ensure
29
26
  out.close
30
27
  err.close
@@ -47,8 +44,7 @@ describe ChildProcess do
47
44
  process.start
48
45
  process.wait
49
46
 
50
- out.rewind
51
- out.read.should == "0\n"
47
+ rewind_and_read(out).should == "0\n"
52
48
  ensure
53
49
  out.close
54
50
  end
@@ -66,8 +62,7 @@ describe ChildProcess do
66
62
  process.start
67
63
  process.poll_for_exit(exit_timeout)
68
64
 
69
- out.rewind
70
- out.read.should == "hello\n"
65
+ rewind_and_read(out).should == "hello\n"
71
66
  ensure
72
67
  out.close
73
68
  end
@@ -91,8 +86,7 @@ describe ChildProcess do
91
86
 
92
87
  process.poll_for_exit(exit_timeout)
93
88
 
94
- out.rewind
95
- out.read.should == "hello world\n"
89
+ rewind_and_read(out).should == "hello world\n"
96
90
  ensure
97
91
  out.close
98
92
  end
@@ -207,8 +201,7 @@ describe ChildProcess do
207
201
  process.start
208
202
  process.wait
209
203
 
210
- out.rewind
211
- out.read.size.should == 3000
204
+ rewind_and_read(out).size.should == 3000
212
205
  ensure
213
206
  out.close
214
207
  end
@@ -5,9 +5,8 @@ shared_examples_for "a platform that provides the child's pid" do
5
5
  Tempfile.open("pid-spec") do |file|
6
6
  process = write_pid(file.path).start
7
7
  process.wait
8
- file.rewind
9
8
 
10
- process.pid.should == file.read.chomp.to_i
9
+ process.pid.should == rewind_and_read(file).chomp.to_i
11
10
  end
12
11
  end
13
12
  end
@@ -60,6 +60,14 @@ module ChildProcessSpecHelper
60
60
  ruby_process tmp_script(code)
61
61
  end
62
62
 
63
+ def write_pid_in_sleepy_grand_child(path)
64
+ code = <<-RUBY
65
+ system "ruby", "-e", 'File.open(#{path.inspect}, "w") { |f| f << Process.pid }; sleep'
66
+ RUBY
67
+
68
+ ruby_process tmp_script(code)
69
+ end
70
+
63
71
  def exit_with(exit_code)
64
72
  ruby_process(tmp_script("exit(#{exit_code})"))
65
73
  end
@@ -97,6 +105,8 @@ module ChildProcessSpecHelper
97
105
  end
98
106
 
99
107
  raise last_error unless ok
108
+
109
+ ok
100
110
  end
101
111
 
102
112
  def cat
@@ -201,6 +211,19 @@ module ChildProcessSpecHelper
201
211
  io.read
202
212
  end
203
213
 
214
+ def alive?(pid)
215
+ if ChildProcess.windows?
216
+ ChildProcess::Windows::Lib.alive?(pid)
217
+ else
218
+ begin
219
+ Process.getpgid pid
220
+ true
221
+ rescue Errno::ESRCH
222
+ false
223
+ end
224
+ end
225
+ end
226
+
204
227
  end # ChildProcessSpecHelper
205
228
 
206
229
  Thread.abort_on_exception = true
metadata CHANGED
@@ -1,89 +1,89 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: childprocess
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jari Bakken
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-18 00:00:00.000000000 Z
11
+ date: 2014-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 2.0.0
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 2.0.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: yard
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: 0.9.2
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: 0.9.2
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: coveralls
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: ffi
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ~>
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
75
  version: '1.0'
76
- - - '>='
76
+ - - ">="
77
77
  - !ruby/object:Gem::Version
78
78
  version: 1.0.11
79
79
  type: :runtime
80
80
  prerelease: false
81
81
  version_requirements: !ruby/object:Gem::Requirement
82
82
  requirements:
83
- - - ~>
83
+ - - "~>"
84
84
  - !ruby/object:Gem::Version
85
85
  version: '1.0'
86
- - - '>='
86
+ - - ">="
87
87
  - !ruby/object:Gem::Version
88
88
  version: 1.0.11
89
89
  description: This gem aims at being a simple and reliable solution for controlling
@@ -94,10 +94,10 @@ executables: []
94
94
  extensions: []
95
95
  extra_rdoc_files: []
96
96
  files:
97
- - .document
98
- - .gitignore
99
- - .rspec
100
- - .travis.yml
97
+ - ".document"
98
+ - ".gitignore"
99
+ - ".rspec"
100
+ - ".travis.yml"
101
101
  - Gemfile
102
102
  - LICENSE
103
103
  - README.md
@@ -148,17 +148,17 @@ require_paths:
148
148
  - lib
149
149
  required_ruby_version: !ruby/object:Gem::Requirement
150
150
  requirements:
151
- - - '>='
151
+ - - ">="
152
152
  - !ruby/object:Gem::Version
153
153
  version: '0'
154
154
  required_rubygems_version: !ruby/object:Gem::Requirement
155
155
  requirements:
156
- - - '>='
156
+ - - ">"
157
157
  - !ruby/object:Gem::Version
158
- version: '0'
158
+ version: 1.3.1
159
159
  requirements: []
160
160
  rubyforge_project: childprocess
161
- rubygems_version: 2.0.14
161
+ rubygems_version: 2.2.0
162
162
  signing_key:
163
163
  specification_version: 4
164
164
  summary: This gem aims at being a simple and reliable solution for controlling external