adeona 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/adeona.rb +68 -0
  2. 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: []