forkr 0.1.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 +7 -0
- data/lib/forkr.rb +155 -0
- metadata +44 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 76c883a35bda24bd878cfa72a2a45f506d67cd2c
|
4
|
+
data.tar.gz: e378330ad4969a24d3554216a0a7a0521aa0df0e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bf8932667a70eb7b31b6d0ce367f17faa166267d77c0a49eb3317b25e33454d8439c6edcb97f1df0dbc6e35f729a92fc364713f4067977244127b53e6348c5a1
|
7
|
+
data.tar.gz: 5da12ece3a39c67f62e41295a9226cb736c33d666bf79d081e6d02e4b7fe77e87c7b9c8d7823706ede81a604250bb2e7c3dc2878cef25f704c2aa5defaed9135
|
data/lib/forkr.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
class Forkr
|
2
|
+
attr_reader :master_pid, :children, :inbound, :outbound, :child_count
|
3
|
+
|
4
|
+
def initialize(forklet, num_kids = 1)
|
5
|
+
@worker_client = forklet
|
6
|
+
@master_pid = $$
|
7
|
+
@children = []
|
8
|
+
@child_count = num_kids
|
9
|
+
@in_shutdown = false
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
@inbound, @outbound = IO.pipe
|
14
|
+
Signal.trap('CHLD') { dead_child }
|
15
|
+
Signal.trap('INT') { interrupt }
|
16
|
+
Signal.trap('TERM') { shutdown }
|
17
|
+
Signal.trap('QUIT') { core_dump_quit }
|
18
|
+
Signal.trap('TTIN') { add_worker }
|
19
|
+
Signal.trap('TTOU') { remove_worker }
|
20
|
+
master_loop
|
21
|
+
end
|
22
|
+
|
23
|
+
def send_wake_notice(notice)
|
24
|
+
return(nil) if $$ != master_pid
|
25
|
+
return(nil) if @in_shutdown
|
26
|
+
@outbound.write(notice)
|
27
|
+
end
|
28
|
+
|
29
|
+
def core_dump_quit
|
30
|
+
send_wake_notice("Q")
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_worker
|
34
|
+
send_wake_notice("+")
|
35
|
+
end
|
36
|
+
|
37
|
+
def remove_worker
|
38
|
+
send_wake_notice("-")
|
39
|
+
end
|
40
|
+
|
41
|
+
def interrupt
|
42
|
+
send_wake_notice("I")
|
43
|
+
end
|
44
|
+
|
45
|
+
def shutdown
|
46
|
+
send_wake_notice("T")
|
47
|
+
end
|
48
|
+
|
49
|
+
def dead_child
|
50
|
+
send_wake_notice("D")
|
51
|
+
end
|
52
|
+
|
53
|
+
def spawn_worker
|
54
|
+
if new_pid = fork
|
55
|
+
@children << new_pid
|
56
|
+
else
|
57
|
+
worker_loop
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def increment_workers
|
62
|
+
@child_count = @child_count + 1
|
63
|
+
end
|
64
|
+
|
65
|
+
def decrement_workers
|
66
|
+
if @child_count > 1
|
67
|
+
@child_count = @child_count - 1
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def shutdown_using(sig)
|
72
|
+
@in_shutdown = true
|
73
|
+
signal_all_workers(sig)
|
74
|
+
raise StopIteration.new
|
75
|
+
end
|
76
|
+
|
77
|
+
def master_loop
|
78
|
+
ensure_right_worker_count
|
79
|
+
loop do
|
80
|
+
fds = IO.select([@inbound],nil,nil,2)
|
81
|
+
unless fds.nil?
|
82
|
+
data_read = fds.first.first.read(1)
|
83
|
+
if data_read == "I"
|
84
|
+
shutdown_using(:INT)
|
85
|
+
elsif data_read == "T"
|
86
|
+
shutdown_using(:TERM)
|
87
|
+
elsif data_read == "Q"
|
88
|
+
shutdown_using(:QUIT)
|
89
|
+
elsif data_read == "+"
|
90
|
+
increment_workers
|
91
|
+
elsif data_read == "-"
|
92
|
+
decrement_workers
|
93
|
+
end
|
94
|
+
end
|
95
|
+
prune_workers
|
96
|
+
ensure_right_worker_count
|
97
|
+
end
|
98
|
+
reap_all_workers
|
99
|
+
@outbound.close
|
100
|
+
@inbound.close
|
101
|
+
end
|
102
|
+
|
103
|
+
def reap_all_workers
|
104
|
+
begin
|
105
|
+
wpid, status = Process.waitpid2(-1, Process::WNOHANG)
|
106
|
+
rescue Errno::ECHILD
|
107
|
+
break
|
108
|
+
end while true
|
109
|
+
end
|
110
|
+
|
111
|
+
def ensure_right_worker_count
|
112
|
+
existing_workers = @children.length
|
113
|
+
off_by = @child_count - @children.length
|
114
|
+
if off_by > 0
|
115
|
+
off_by.times do
|
116
|
+
spawn_worker
|
117
|
+
end
|
118
|
+
elsif off_by < 0
|
119
|
+
@children.take(off_by.abs).each do |kid|
|
120
|
+
signal_worker(kid, :TERM)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def signal_all_workers(sig)
|
126
|
+
@children.each { |c| signal_worker(c, sig) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def signal_worker(wpid, signal)
|
130
|
+
begin
|
131
|
+
Process.kill(signal, wpid)
|
132
|
+
rescue Errno::ESRCH
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def prune_workers
|
137
|
+
@children = @children.reject { |pid| child_dead?(pid) }
|
138
|
+
end
|
139
|
+
|
140
|
+
def worker_loop
|
141
|
+
@worker_client.after_fork if @worker_client.respond_to?(:after_fork)
|
142
|
+
@inbound.close
|
143
|
+
@outbound.close
|
144
|
+
$stderr.puts "Worker spawned as #{$$}!"
|
145
|
+
@worker_client.run
|
146
|
+
end
|
147
|
+
|
148
|
+
def child_dead?(pid)
|
149
|
+
status = Process.waitpid(pid, Process::WNOHANG)
|
150
|
+
unless status.nil?
|
151
|
+
$stderr.puts "Process #{pid} dead: #{status}"
|
152
|
+
end
|
153
|
+
!status.nil?
|
154
|
+
end
|
155
|
+
end
|
metadata
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: forkr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Trey Evans
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-12-01 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A pre-forking worker host.
|
14
|
+
email: lewis.r.evans@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/forkr.rb
|
20
|
+
homepage: http://rubygems.org/gems/hola
|
21
|
+
licenses:
|
22
|
+
- MIT
|
23
|
+
metadata: {}
|
24
|
+
post_install_message:
|
25
|
+
rdoc_options: []
|
26
|
+
require_paths:
|
27
|
+
- lib
|
28
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
requirements: []
|
39
|
+
rubyforge_project:
|
40
|
+
rubygems_version: 2.2.2
|
41
|
+
signing_key:
|
42
|
+
specification_version: 4
|
43
|
+
summary: A pre-forking worker host - shamelessly inspired by unicorn.
|
44
|
+
test_files: []
|