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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/forkr.gemspec +2 -2
  3. data/lib/multi_forkr.rb +163 -0
  4. metadata +2 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f0539b882a7e449399d84d53b5f84514db12af81
4
- data.tar.gz: 41f718fe40fb072bd4c5e6f0e28953ac97f0d917
3
+ metadata.gz: 3def66e8f139ed116bb7beaf96207eea08e0dab7
4
+ data.tar.gz: 9f34069eaafcf424c414803a184b4cf7aa8b9554
5
5
  SHA512:
6
- metadata.gz: 6c4f42032dee53cbfd4898dc0bdc9fae67146afdd68fe132c6d5338f4a9b21115057b8b981b70fde957a033266d66b471f2f06dd3313471c42c3de94c26802b6
7
- data.tar.gz: bc1dd886ec97c7129f362bc56b2c9cd4fa4d98a5c6a45c7f93e1d74c2e7ebc8dfa10221380cd5bff34cf0f12c6fd7449be0bb16b307d63482b8048e08388e8fe
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.1.7'
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
@@ -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.1.7
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