forkr 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/forkr.rb +155 -0
  3. 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: []