joechill 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/LICENSE +21 -0
  2. data/README.md +30 -0
  3. data/chill.gemspec +19 -0
  4. data/lib/chill.rb +125 -0
  5. metadata +51 -0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Michael Sechooler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ The Chill gem provides a way to fork or spawn a subprocess
2
+ that will die when its parent does. It accomplishes this by
3
+ also spawning a watchdog process. The watchdog and the parent
4
+ are connected by an anonymous pipe; when the pipe is broken
5
+ (because the parent dies), the watchdog will then send a signal
6
+ to the child.
7
+
8
+ There are some limitations to this. Firstly, the parent must
9
+ maintain the pipe. It cannot, for example, overlay itself
10
+ with another process (through an exec call) that closes all
11
+ open file descriptors. Secondly, there is a minor race
12
+ condition. If the child terminates before the parent, the parent
13
+ reaps the child, and a new process is started with the same PID,
14
+ the watchdog will mistakenly kill this different process when
15
+ the parent dies. This could be avoided by having the watchdog
16
+ spawn the child, but that would complicate the relationship between
17
+ the parent and child.
18
+
19
+ The easiest way to use the class is to call one of its class methods,
20
+ `::fork` or `::spawn`. You can also construct an instance, which will give
21
+ you control over the signal it issues to the child (by default `SIGTERM`).
22
+
23
+ This gem was created to quickly solve a problem in a development
24
+ environment. It isn't necessarily ideal for production use. For Linux
25
+ environments, you might want to look into `PR_SET_PDEATHSIG`.
26
+
27
+ More information is available in the doc directory, which is generated by
28
+ running rdoc against the whole project. An
29
+ [online copy](http://htmlpreview.github.io/?https://github.com/mikesech/chill/blob/master/doc/Chill.html)
30
+ is available.
data/chill.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
3
+ require 'chill'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'joechill'
7
+ s.summary = 'Starts a subprocess that dies when its parent does.'
8
+ s.description = 'Chill starts a subprocess that dies when its parent does.'
9
+ s.homepage = 'http://github.com/mikesech/chill'
10
+ s.version = Chill::VERSION
11
+ s.licenses = ['MIT']
12
+
13
+ s.authors = [ 'Michael Sechooler' ]
14
+ s.email = [ 'mikesech@gmail.com' ]
15
+
16
+ s.require_paths = ['lib']
17
+ s.files = %w( README.md LICENSE chill.gemspec )
18
+ s.files += Dir.glob('lib/**/*')
19
+ end
data/lib/chill.rb ADDED
@@ -0,0 +1,125 @@
1
+ ##
2
+ # Starts a subprocess that dies when its parent does.
3
+ #
4
+ # The Chill class provides a way to fork or spawn a subprocess
5
+ # that will die when its parent does. It accomplishes this by
6
+ # also spawning a watchdog process. The watchdog and the parent
7
+ # are connected by an anonymous pipe; when the pipe is broken
8
+ # (because the parent dies), the watchdog will then send a signal
9
+ # to the child.
10
+ #
11
+ # There are some limitations to this. Firstly, the parent must
12
+ # maintain the pipe. It cannot, for example, overlay itself
13
+ # with another process (through an exec call) that closes all
14
+ # open file descriptors. Secondly, there is a minor race
15
+ # condition. If the child terminates before the parent, the parent
16
+ # reaps the child, and a new process is started with the same PID,
17
+ # the watchdog will mistakenly kill this different process when
18
+ # the parent dies. This could be avoided by having the watchdog
19
+ # spawn the child, but that would complicate the relationship between
20
+ # the parent and child.
21
+ #
22
+ # The easiest way to use this class is to call one of its class methods,
23
+ # ::fork or ::spawn. You can also construct an instance, which will give
24
+ # you control over the signal it issues to the child (by default +SIGTERM+).
25
+ #
26
+ # This class was created to quickly solve a problem in a development
27
+ # environment. It isn't necessarily ideal for production use. For Linux
28
+ # environments, you might want to look into +PR_SET_PDEATHSIG+.
29
+ #
30
+ class Chill
31
+ VERSION = '1.0.0'
32
+
33
+ ##
34
+ # The PID of the spawned subprocess.
35
+ #
36
+ attr_reader :pid
37
+
38
+ ##
39
+ # Constructs a new Chill object.
40
+ #
41
+ # Also creates a new anonymous pipe to be used as the underlying
42
+ # signaling mechanism.
43
+ #
44
+ # +signal+::
45
+ # The name of the POSIX signal (e.g., "TERM") to issue
46
+ # the child when its parent dies.
47
+ #
48
+ def initialize(signal = "TERM")
49
+ @reader, @writer = IO.pipe
50
+ @signal = signal
51
+ end
52
+
53
+ ##
54
+ # Forks a child process and executes a block in it.
55
+ #
56
+ # Returns the PID of the child process.
57
+ #
58
+ def fork(&block)
59
+ raise RuntimeError, "already in use" unless @pid.nil?
60
+ @pid = Process.fork do
61
+ # The child doesn't need the pipe, and leaving it open
62
+ # will keep the pipe alive even after the parent dies.
63
+ @reader.close
64
+ @writer.close
65
+ block.call
66
+ end
67
+ spawn_watchdog_process
68
+
69
+ # The parent of the watchdog process (the original parent)
70
+ # doesn't need to use the pipe. Let's close the other end now.
71
+ @reader.close
72
+
73
+ @pid
74
+ end
75
+ ##
76
+ # Spawns a new process.
77
+ # See <tt>Kernel::exec</tt> for the arguments accepted.
78
+ #
79
+ # Returns the PID of the child process.
80
+ #
81
+ def spawn(*args)
82
+ fork do
83
+ Process.exec(*args)
84
+ end
85
+ end
86
+
87
+ ##
88
+ # Forks a child process and executes a block in it.
89
+ #
90
+ # Returns the Chill instance created. The PID of the child process
91
+ # can be accessed through its +pid+ method.
92
+ #
93
+ def self.fork(&block); x = self.new; x.fork(&block); x; end
94
+ ##
95
+ # Spawns a new process.
96
+ # See <tt>Kernel::exec</tt> for the arguments accepted.
97
+ #
98
+ # Returns the Chill instance created. The PID of the child process
99
+ # can be accessed through its +pid+ method.
100
+ #
101
+ def self.spawn(*args); x = self.new; x.spawn(*args); x; end
102
+
103
+ private
104
+
105
+ ##
106
+ # Spawns a subprocess to monitor the parent.
107
+ #
108
+ # The watchdog process' parent is the original parent.
109
+ # The watchdog works by waiting for a pipe also held by the parent to
110
+ # break. When it does, it will issue a signal to the child process.
111
+ #
112
+ def spawn_watchdog_process
113
+ Process.fork do
114
+ # If we don't close the write end, the read might never end since the OS doesn't
115
+ # generate EOFs if there is a write end still open. We use EOF to signal a failure
116
+ # or abrupt termination of the signaling process.
117
+ @writer.close
118
+
119
+ val = @reader.sysread(1) rescue nil
120
+ @reader.close
121
+
122
+ Process.kill(@signal, @pid) if val.nil?
123
+ end
124
+ end
125
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: joechill
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael Sechooler
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-04-04 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Chill starts a subprocess that dies when its parent does.
15
+ email:
16
+ - mikesech@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.md
22
+ - LICENSE
23
+ - chill.gemspec
24
+ - lib/chill.rb
25
+ homepage: http://github.com/mikesech/chill
26
+ licenses:
27
+ - MIT
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 1.8.28
47
+ signing_key:
48
+ specification_version: 3
49
+ summary: Starts a subprocess that dies when its parent does.
50
+ test_files: []
51
+ has_rdoc: