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