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.
- data/LICENSE +21 -0
- data/README.md +30 -0
- data/chill.gemspec +19 -0
- data/lib/chill.rb +125 -0
- 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:
|