joechill 1.0.0

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 (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: