childprocess 0.4.0 → 0.4.1.rc1

Sign up to get free protection for your applications and to get access to all the features.
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