adeona 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/adeona.rb +68 -0
- metadata +50 -0
data/lib/adeona.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
module Adeona
|
2
|
+
def Adeona::spawn_child(options = {}, &block)
|
3
|
+
# handle default options
|
4
|
+
# - detach (true): causes Process.detach to be called on the child process. If set to false, the
|
5
|
+
# parent process (ideally) needs to call Process.waitpid on the child_pid returned by spawn_child.
|
6
|
+
# - timeout (nil): by default no timeout interval is set. Can be used to specify the number of
|
7
|
+
# seconds after which the child_process automatically exits.
|
8
|
+
# - verbose (false): when set to true Adeona will write to stdout when a child process exits due to
|
9
|
+
# losing the connection to its parent or due to its timeout interval expiring.
|
10
|
+
default_options = {:detach => true, :timeout => nil, :verbose => false}
|
11
|
+
options = default_options.merge(options)
|
12
|
+
|
13
|
+
# create a pipe that allows for communication between parent and child process. This pipe will
|
14
|
+
# be used to let the child process know when the parent is no longer there (this works even if
|
15
|
+
# the parent process gets killed with SIGKILL).
|
16
|
+
lifeline = IO.pipe()
|
17
|
+
|
18
|
+
# use fork to create the actual child process
|
19
|
+
child_pid = fork do
|
20
|
+
# make the child process close its write endpoint of the pipe.
|
21
|
+
# make the child process set sync to true for its read endpoint so as to avoid message buffering.
|
22
|
+
lifeline[1].close()
|
23
|
+
lifeline[0].sync = true
|
24
|
+
|
25
|
+
begin
|
26
|
+
# here's where the magic happens that makes the child process exit as soon as the parent is no longer active.
|
27
|
+
# In the child process we create a thread that calls IO.select on its read endpoint. This is a blocking operation that
|
28
|
+
# causes the thread to keep waiting. Now remember that the child process has already closed its write endpoint (line 22).
|
29
|
+
# This means that only the main process has an open write endpoint to this pipe. So when the main process disappears,
|
30
|
+
# the kernel detects nothing can write to this pipe anymore, and causes an EOF to be sent to the pipe. This EOF causes
|
31
|
+
# IO.select to return, letting the child process know the parent process no longer exists. IO.select can also take a
|
32
|
+
# timeout value, which we use to kill the child process when the timeout interval has expired.
|
33
|
+
# In both cases we cause an exception to be raised in the main thread, which exits the child process.
|
34
|
+
lifeline_thread = Thread.new(Thread.current) do |main_thread|
|
35
|
+
result = IO.select([lifeline[0]], nil, nil, options[:timeout])
|
36
|
+
main_thread.raise 'Adeona: Connection with parent process was lost.' if !result.nil?
|
37
|
+
main_thread.raise 'Adeona: Connection with parent process was lost due to timeout.' if result.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
# this is where the user specified code in the block is executed.
|
41
|
+
block.call
|
42
|
+
|
43
|
+
# this exception handler will handle exceptions thrown by the user specified code in the block, as well as
|
44
|
+
# exceptions thrown by the lifeline_thread that cause the child process to exit when the parent process is
|
45
|
+
# no longer active.
|
46
|
+
rescue Exception => e
|
47
|
+
if(options[:verbose])
|
48
|
+
puts "Exception caught: #{e.message}"
|
49
|
+
puts e.backtrace
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# here we are back in the parent process. The parent process closes its read endpoint of the pipe, as it has no use for it.
|
55
|
+
# The write endpoint is kept open in order for the lifeline mechanism to work (line 29). We also set sync to true so as to
|
56
|
+
# prevent message buffering.
|
57
|
+
lifeline[0].close()
|
58
|
+
lifeline[1].sync = true
|
59
|
+
|
60
|
+
# detaching the child process is ideal for fire-and-forget type child processes. You want to set the detach option to false
|
61
|
+
# when you want to use Process.waitpid to make the parent process wait for the child.
|
62
|
+
if(options[:detach])
|
63
|
+
Process.detach(child_pid)
|
64
|
+
end
|
65
|
+
|
66
|
+
child_pid
|
67
|
+
end
|
68
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: adeona
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tom Van Eyck
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-15 00:00:00.000000000 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
description: A module that makes it easy to create child processes that die when their
|
16
|
+
parent process is killed. It works even if the parent is disabled with SIGKILL.
|
17
|
+
It also avoids busy waiting.
|
18
|
+
email: tomvaneyck@gmail.com
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- lib/adeona.rb
|
24
|
+
has_rdoc: true
|
25
|
+
homepage: https://github.com/vaneyckt/Adeona
|
26
|
+
licenses: []
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
none: false
|
33
|
+
requirements:
|
34
|
+
- - ! '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubyforge_project:
|
45
|
+
rubygems_version: 1.6.2
|
46
|
+
signing_key:
|
47
|
+
specification_version: 3
|
48
|
+
summary: A module that makes it easy to create child processes that die when their
|
49
|
+
parent process is killed.
|
50
|
+
test_files: []
|