adeona 0.0.1
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.
- 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: []
|