forkr 0.1.7 → 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.
- checksums.yaml +4 -4
- data/forkr.gemspec +2 -2
- data/lib/multi_forkr.rb +163 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3def66e8f139ed116bb7beaf96207eea08e0dab7
|
4
|
+
data.tar.gz: 9f34069eaafcf424c414803a184b4cf7aa8b9554
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0cc9016ae45bb4e64c586a316ee13bed3799c1aa6798fc90af5566ef1800c8d8e62340c8cc7384bc60bfe5d4122cb0a2fa02eb2883b9bd081293c7192de5d6a2
|
7
|
+
data.tar.gz: 302d7d9c68e3c82404992ea49bb874e6b934fe02177481f838b13729fc285d6611c1bbf248199c5396ffa9a5e0a212f2bc75ee772b33556f06328ddf1bb71db0
|
data/forkr.gemspec
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'forkr'
|
3
|
-
s.version = '0.
|
3
|
+
s.version = '1.0.0'
|
4
4
|
s.date = '2015-03-22'
|
5
5
|
s.summary = "A pre-forking worker host - shamelessly inspired by unicorn."
|
6
6
|
s.description = "Forkr is a preforking worker host, shamelessly inspired by unicorn. It exists to easily fork and scale ruby programs which aren't rack-based."
|
7
7
|
s.authors = ["Trey Evans"]
|
8
8
|
s.email = 'lewis.r.evans@gmail.com'
|
9
|
-
s.files = ["lib/forkr.rb", "forkr.gemspec", "README.md"]
|
9
|
+
s.files = ["lib/forkr.rb", "lib/multi_forkr.rb", "forkr.gemspec", "README.md"]
|
10
10
|
s.homepage = 'https://github.com/TreyE/forkr'
|
11
11
|
s.license = 'MIT'
|
12
12
|
end
|
data/lib/multi_forkr.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
class MultiForkr
|
2
|
+
|
3
|
+
# The PID of the Forkr master
|
4
|
+
# @return [Fixnum]
|
5
|
+
attr_reader :master_pid
|
6
|
+
|
7
|
+
# Child Process Definitions
|
8
|
+
# @return Hash<Object, Integer>
|
9
|
+
attr_reader :child_defs
|
10
|
+
|
11
|
+
# Child process pids.
|
12
|
+
# @return Hash<Object, [Pids]>
|
13
|
+
attr_reader :child_sets
|
14
|
+
|
15
|
+
# @param forklets Hash<Object, Integer> the worker objects, with counts
|
16
|
+
def initialize(forklets)
|
17
|
+
@child_defs = forklets
|
18
|
+
@master_pid = $$
|
19
|
+
@child_sets = Hash.new { |h, k| h[k] = Array.new }
|
20
|
+
@in_shutdown = false
|
21
|
+
end
|
22
|
+
|
23
|
+
# Start the master, and spawn workers
|
24
|
+
# @return [nil]
|
25
|
+
def run
|
26
|
+
@inbound, @outbound = IO.pipe
|
27
|
+
Signal.trap('CHLD') { dead_child }
|
28
|
+
Signal.trap('INT') { interrupt }
|
29
|
+
Signal.trap('TERM') { shutdown }
|
30
|
+
Signal.trap('QUIT') { core_dump_quit }
|
31
|
+
master_loop
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
attr_reader :inbound, :outbound
|
37
|
+
|
38
|
+
def send_wake_notice(notice)
|
39
|
+
return(nil) if $$ != master_pid
|
40
|
+
return(nil) if @in_shutdown
|
41
|
+
@outbound.write(notice)
|
42
|
+
end
|
43
|
+
|
44
|
+
def core_dump_quit
|
45
|
+
send_wake_notice("Q")
|
46
|
+
end
|
47
|
+
|
48
|
+
def interrupt
|
49
|
+
send_wake_notice("I")
|
50
|
+
end
|
51
|
+
|
52
|
+
def shutdown
|
53
|
+
send_wake_notice("T")
|
54
|
+
end
|
55
|
+
|
56
|
+
def dead_child
|
57
|
+
send_wake_notice("D")
|
58
|
+
end
|
59
|
+
|
60
|
+
def spawn_worker(forklet)
|
61
|
+
if new_pid = fork
|
62
|
+
existing_worker_pids = @child_sets[forklet]
|
63
|
+
@child_sets[forklet] = existing_worker_pids + [new_pid]
|
64
|
+
else
|
65
|
+
worker_loop(forklet)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def shutdown_using(sig)
|
70
|
+
@in_shutdown = true
|
71
|
+
signal_all_workers(sig)
|
72
|
+
raise StopIteration.new
|
73
|
+
end
|
74
|
+
|
75
|
+
def master_loop
|
76
|
+
catch(:bail_because_im_a_worker) do
|
77
|
+
ensure_right_worker_count
|
78
|
+
loop do
|
79
|
+
fds = IO.select([@inbound],nil,nil,2)
|
80
|
+
unless fds.nil?
|
81
|
+
data_read = fds.first.first.read(1)
|
82
|
+
if data_read == "I"
|
83
|
+
shutdown_using(:INT)
|
84
|
+
elsif data_read == "T"
|
85
|
+
shutdown_using(:TERM)
|
86
|
+
elsif data_read == "Q"
|
87
|
+
shutdown_using(:QUIT)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
prune_workers
|
91
|
+
ensure_right_worker_count
|
92
|
+
end
|
93
|
+
reap_all_workers
|
94
|
+
@outbound.close
|
95
|
+
@inbound.close
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def reap_all_workers
|
100
|
+
begin
|
101
|
+
wpid, status = Process.waitpid2(-1, Process::WNOHANG)
|
102
|
+
rescue Errno::ECHILD
|
103
|
+
break
|
104
|
+
end while true
|
105
|
+
end
|
106
|
+
|
107
|
+
def ensure_right_worker_count
|
108
|
+
@child_defs.each_pair do |k, v|
|
109
|
+
existing_workers = @child_sets[k]
|
110
|
+
off_by = v - existing_workers.length
|
111
|
+
if off_by > 0
|
112
|
+
off_by.times do
|
113
|
+
spawn_worker(k)
|
114
|
+
end
|
115
|
+
elsif off_by < 0
|
116
|
+
existing_workers.take(off_by.abs).each do |kid|
|
117
|
+
signal_worker(kid, :TERM)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def children
|
124
|
+
@child_sets.values.map { |v| v }
|
125
|
+
end
|
126
|
+
|
127
|
+
def signal_all_workers(sig)
|
128
|
+
@children.each { |c| signal_worker(c, sig) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def signal_worker(wpid, signal)
|
132
|
+
begin
|
133
|
+
Process.kill(signal, wpid)
|
134
|
+
rescue Errno::ESRCH
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def prune_workers
|
139
|
+
new_sets = {}
|
140
|
+
@child_sets.each_pair do |k, v|
|
141
|
+
living_children = v.reject { |pid| child_dead?(pid) }
|
142
|
+
new_sets[k] = living_children
|
143
|
+
end
|
144
|
+
@child_sets = new_sets
|
145
|
+
end
|
146
|
+
|
147
|
+
def worker_loop(forklet)
|
148
|
+
forklet.after_fork if forklet.respond_to?(:after_fork)
|
149
|
+
@inbound.close
|
150
|
+
@outbound.close
|
151
|
+
$stderr.puts "Worker spawned as #{$$}!"
|
152
|
+
forklet.run
|
153
|
+
throw(:bail_because_im_a_worker)
|
154
|
+
end
|
155
|
+
|
156
|
+
def child_dead?(pid)
|
157
|
+
status = Process.waitpid(pid, Process::WNOHANG)
|
158
|
+
unless status.nil?
|
159
|
+
$stderr.puts "Process #{pid} dead: #{status}"
|
160
|
+
end
|
161
|
+
!status.nil?
|
162
|
+
end
|
163
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: forkr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Trey Evans
|
@@ -20,6 +20,7 @@ files:
|
|
20
20
|
- README.md
|
21
21
|
- forkr.gemspec
|
22
22
|
- lib/forkr.rb
|
23
|
+
- lib/multi_forkr.rb
|
23
24
|
homepage: https://github.com/TreyE/forkr
|
24
25
|
licenses:
|
25
26
|
- MIT
|