prefork_engine 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 227fa879a618645e19ba65f3abfc259f69f5dc79
4
+ data.tar.gz: 416e99bead194dec4626ce28a7bcb0db3a5f9b01
5
+ SHA512:
6
+ metadata.gz: 9c7b50e3ebe7fed72b1c390c77ec102fb19aa36ad5350361a92f36b9fbeaa242d51c799f0674e93811b500f598fc2e16e04b557f45b78b3af8c7d9b6641f5041
7
+ data.tar.gz: adf4233d2a8d8b164d1dafdb160e994855edb56721096bda279dd423cf461b87c97ffe2ba9f597c808724d62f2fb10aecb7b62834a019542c0b3fa83af08ed92
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *~
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in prefork_engine.gemspec
4
+ gemspec
@@ -0,0 +1,378 @@
1
+ This software is copyright (c) 2014 by Masahiro Nagano <kazeburo@gmail.com>.
2
+
3
+ This is free software; you can redistribute it and/or modify it under
4
+ the same terms as the Perl 5 programming language system itself.
5
+
6
+ Terms of the Perl programming language system itself
7
+
8
+ a) the GNU General Public License as published by the Free
9
+ Software Foundation; either version 1, or (at your option) any
10
+ later version, or
11
+ b) the "Artistic License"
12
+
13
+ --- The GNU General Public License, Version 1, February 1989 ---
14
+
15
+ This software is Copyright (c) 2014 by Masahiro Nagano <kazeburo@gmail.com>.
16
+
17
+ This is free software, licensed under:
18
+
19
+ The GNU General Public License, Version 1, February 1989
20
+
21
+ GNU GENERAL PUBLIC LICENSE
22
+ Version 1, February 1989
23
+
24
+ Copyright (C) 1989 Free Software Foundation, Inc.
25
+ 51 Franklin St, Suite 500, Boston, MA 02110-1335 USA
26
+
27
+ Everyone is permitted to copy and distribute verbatim copies
28
+ of this license document, but changing it is not allowed.
29
+
30
+ Preamble
31
+
32
+ The license agreements of most software companies try to keep users
33
+ at the mercy of those companies. By contrast, our General Public
34
+ License is intended to guarantee your freedom to share and change free
35
+ software--to make sure the software is free for all its users. The
36
+ General Public License applies to the Free Software Foundation's
37
+ software and to any other program whose authors commit to using it.
38
+ You can use it for your programs, too.
39
+
40
+ When we speak of free software, we are referring to freedom, not
41
+ price. Specifically, the General Public License is designed to make
42
+ sure that you have the freedom to give away or sell copies of free
43
+ software, that you receive source code or can get it if you want it,
44
+ that you can change the software or use pieces of it in new free
45
+ programs; and that you know you can do these things.
46
+
47
+ To protect your rights, we need to make restrictions that forbid
48
+ anyone to deny you these rights or to ask you to surrender the rights.
49
+ These restrictions translate to certain responsibilities for you if you
50
+ distribute copies of the software, or if you modify it.
51
+
52
+ For example, if you distribute copies of a such a program, whether
53
+ gratis or for a fee, you must give the recipients all the rights that
54
+ you have. You must make sure that they, too, receive or can get the
55
+ source code. And you must tell them their rights.
56
+
57
+ We protect your rights with two steps: (1) copyright the software, and
58
+ (2) offer you this license which gives you legal permission to copy,
59
+ distribute and/or modify the software.
60
+
61
+ Also, for each author's protection and ours, we want to make certain
62
+ that everyone understands that there is no warranty for this free
63
+ software. If the software is modified by someone else and passed on, we
64
+ want its recipients to know that what they have is not the original, so
65
+ that any problems introduced by others will not reflect on the original
66
+ authors' reputations.
67
+
68
+ The precise terms and conditions for copying, distribution and
69
+ modification follow.
70
+
71
+ GNU GENERAL PUBLIC LICENSE
72
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
73
+
74
+ 0. This License Agreement applies to any program or other work which
75
+ contains a notice placed by the copyright holder saying it may be
76
+ distributed under the terms of this General Public License. The
77
+ "Program", below, refers to any such program or work, and a "work based
78
+ on the Program" means either the Program or any work containing the
79
+ Program or a portion of it, either verbatim or with modifications. Each
80
+ licensee is addressed as "you".
81
+
82
+ 1. You may copy and distribute verbatim copies of the Program's source
83
+ code as you receive it, in any medium, provided that you conspicuously and
84
+ appropriately publish on each copy an appropriate copyright notice and
85
+ disclaimer of warranty; keep intact all the notices that refer to this
86
+ General Public License and to the absence of any warranty; and give any
87
+ other recipients of the Program a copy of this General Public License
88
+ along with the Program. You may charge a fee for the physical act of
89
+ transferring a copy.
90
+
91
+ 2. You may modify your copy or copies of the Program or any portion of
92
+ it, and copy and distribute such modifications under the terms of Paragraph
93
+ 1 above, provided that you also do the following:
94
+
95
+ a) cause the modified files to carry prominent notices stating that
96
+ you changed the files and the date of any change; and
97
+
98
+ b) cause the whole of any work that you distribute or publish, that
99
+ in whole or in part contains the Program or any part thereof, either
100
+ with or without modifications, to be licensed at no charge to all
101
+ third parties under the terms of this General Public License (except
102
+ that you may choose to grant warranty protection to some or all
103
+ third parties, at your option).
104
+
105
+ c) If the modified program normally reads commands interactively when
106
+ run, you must cause it, when started running for such interactive use
107
+ in the simplest and most usual way, to print or display an
108
+ announcement including an appropriate copyright notice and a notice
109
+ that there is no warranty (or else, saying that you provide a
110
+ warranty) and that users may redistribute the program under these
111
+ conditions, and telling the user how to view a copy of this General
112
+ Public License.
113
+
114
+ d) You may charge a fee for the physical act of transferring a
115
+ copy, and you may at your option offer warranty protection in
116
+ exchange for a fee.
117
+
118
+ Mere aggregation of another independent work with the Program (or its
119
+ derivative) on a volume of a storage or distribution medium does not bring
120
+ the other work under the scope of these terms.
121
+
122
+ 3. You may copy and distribute the Program (or a portion or derivative of
123
+ it, under Paragraph 2) in object code or executable form under the terms of
124
+ Paragraphs 1 and 2 above provided that you also do one of the following:
125
+
126
+ a) accompany it with the complete corresponding machine-readable
127
+ source code, which must be distributed under the terms of
128
+ Paragraphs 1 and 2 above; or,
129
+
130
+ b) accompany it with a written offer, valid for at least three
131
+ years, to give any third party free (except for a nominal charge
132
+ for the cost of distribution) a complete machine-readable copy of the
133
+ corresponding source code, to be distributed under the terms of
134
+ Paragraphs 1 and 2 above; or,
135
+
136
+ c) accompany it with the information you received as to where the
137
+ corresponding source code may be obtained. (This alternative is
138
+ allowed only for noncommercial distribution and only if you
139
+ received the program in object code or executable form alone.)
140
+
141
+ Source code for a work means the preferred form of the work for making
142
+ modifications to it. For an executable file, complete source code means
143
+ all the source code for all modules it contains; but, as a special
144
+ exception, it need not include source code for modules which are standard
145
+ libraries that accompany the operating system on which the executable
146
+ file runs, or for standard header files or definitions files that
147
+ accompany that operating system.
148
+
149
+ 4. You may not copy, modify, sublicense, distribute or transfer the
150
+ Program except as expressly provided under this General Public License.
151
+ Any attempt otherwise to copy, modify, sublicense, distribute or transfer
152
+ the Program is void, and will automatically terminate your rights to use
153
+ the Program under this License. However, parties who have received
154
+ copies, or rights to use copies, from you under this General Public
155
+ License will not have their licenses terminated so long as such parties
156
+ remain in full compliance.
157
+
158
+ 5. By copying, distributing or modifying the Program (or any work based
159
+ on the Program) you indicate your acceptance of this license to do so,
160
+ and all its terms and conditions.
161
+
162
+ 6. Each time you redistribute the Program (or any work based on the
163
+ Program), the recipient automatically receives a license from the original
164
+ licensor to copy, distribute or modify the Program subject to these
165
+ terms and conditions. You may not impose any further restrictions on the
166
+ recipients' exercise of the rights granted herein.
167
+
168
+ 7. The Free Software Foundation may publish revised and/or new versions
169
+ of the General Public License from time to time. Such new versions will
170
+ be similar in spirit to the present version, but may differ in detail to
171
+ address new problems or concerns.
172
+
173
+ Each version is given a distinguishing version number. If the Program
174
+ specifies a version number of the license which applies to it and "any
175
+ later version", you have the option of following the terms and conditions
176
+ either of that version or of any later version published by the Free
177
+ Software Foundation. If the Program does not specify a version number of
178
+ the license, you may choose any version ever published by the Free Software
179
+ Foundation.
180
+
181
+ 8. If you wish to incorporate parts of the Program into other free
182
+ programs whose distribution conditions are different, write to the author
183
+ to ask for permission. For software which is copyrighted by the Free
184
+ Software Foundation, write to the Free Software Foundation; we sometimes
185
+ make exceptions for this. Our decision will be guided by the two goals
186
+ of preserving the free status of all derivatives of our free software and
187
+ of promoting the sharing and reuse of software generally.
188
+
189
+ NO WARRANTY
190
+
191
+ 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
192
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
193
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
194
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
195
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
196
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
197
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
198
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
199
+ REPAIR OR CORRECTION.
200
+
201
+ 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
202
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
203
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
204
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
205
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
206
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
207
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
208
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
209
+ POSSIBILITY OF SUCH DAMAGES.
210
+
211
+ END OF TERMS AND CONDITIONS
212
+
213
+ Appendix: How to Apply These Terms to Your New Programs
214
+
215
+ If you develop a new program, and you want it to be of the greatest
216
+ possible use to humanity, the best way to achieve this is to make it
217
+ free software which everyone can redistribute and change under these
218
+ terms.
219
+
220
+ To do so, attach the following notices to the program. It is safest to
221
+ attach them to the start of each source file to most effectively convey
222
+ the exclusion of warranty; and each file should have at least the
223
+ "copyright" line and a pointer to where the full notice is found.
224
+
225
+ <one line to give the program's name and a brief idea of what it does.>
226
+ Copyright (C) 19yy <name of author>
227
+
228
+ This program is free software; you can redistribute it and/or modify
229
+ it under the terms of the GNU General Public License as published by
230
+ the Free Software Foundation; either version 1, or (at your option)
231
+ any later version.
232
+
233
+ This program is distributed in the hope that it will be useful,
234
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
235
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
236
+ GNU General Public License for more details.
237
+
238
+ You should have received a copy of the GNU General Public License
239
+ along with this program; if not, write to the Free Software
240
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
241
+
242
+
243
+ Also add information on how to contact you by electronic and paper mail.
244
+
245
+ If the program is interactive, make it output a short notice like this
246
+ when it starts in an interactive mode:
247
+
248
+ Gnomovision version 69, Copyright (C) 19xx name of author
249
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
250
+ This is free software, and you are welcome to redistribute it
251
+ under certain conditions; type `show c' for details.
252
+
253
+ The hypothetical commands `show w' and `show c' should show the
254
+ appropriate parts of the General Public License. Of course, the
255
+ commands you use may be called something other than `show w' and `show
256
+ c'; they could even be mouse-clicks or menu items--whatever suits your
257
+ program.
258
+
259
+ You should also get your employer (if you work as a programmer) or your
260
+ school, if any, to sign a "copyright disclaimer" for the program, if
261
+ necessary. Here a sample; alter the names:
262
+
263
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
264
+ program `Gnomovision' (a program to direct compilers to make passes
265
+ at assemblers) written by James Hacker.
266
+
267
+ <signature of Ty Coon>, 1 April 1989
268
+ Ty Coon, President of Vice
269
+
270
+ That's all there is to it!
271
+
272
+
273
+ --- The Artistic License 1.0 ---
274
+
275
+ This software is Copyright (c) 2014 by Masahiro Nagano <kazeburo@gmail.com>.
276
+
277
+ This is free software, licensed under:
278
+
279
+ The Artistic License 1.0
280
+
281
+ The Artistic License
282
+
283
+ Preamble
284
+
285
+ The intent of this document is to state the conditions under which a Package
286
+ may be copied, such that the Copyright Holder maintains some semblance of
287
+ artistic control over the development of the package, while giving the users of
288
+ the package the right to use and distribute the Package in a more-or-less
289
+ customary fashion, plus the right to make reasonable modifications.
290
+
291
+ Definitions:
292
+
293
+ - "Package" refers to the collection of files distributed by the Copyright
294
+ Holder, and derivatives of that collection of files created through
295
+ textual modification.
296
+ - "Standard Version" refers to such a Package if it has not been modified,
297
+ or has been modified in accordance with the wishes of the Copyright
298
+ Holder.
299
+ - "Copyright Holder" is whoever is named in the copyright or copyrights for
300
+ the package.
301
+ - "You" is you, if you're thinking about copying or distributing this Package.
302
+ - "Reasonable copying fee" is whatever you can justify on the basis of media
303
+ cost, duplication charges, time of people involved, and so on. (You will
304
+ not be required to justify it to the Copyright Holder, but only to the
305
+ computing community at large as a market that must bear the fee.)
306
+ - "Freely Available" means that no fee is charged for the item itself, though
307
+ there may be fees involved in handling the item. It also means that
308
+ recipients of the item may redistribute it under the same conditions they
309
+ received it.
310
+
311
+ 1. You may make and give away verbatim copies of the source form of the
312
+ Standard Version of this Package without restriction, provided that you
313
+ duplicate all of the original copyright notices and associated disclaimers.
314
+
315
+ 2. You may apply bug fixes, portability fixes and other modifications derived
316
+ from the Public Domain or from the Copyright Holder. A Package modified in such
317
+ a way shall still be considered the Standard Version.
318
+
319
+ 3. You may otherwise modify your copy of this Package in any way, provided that
320
+ you insert a prominent notice in each changed file stating how and when you
321
+ changed that file, and provided that you do at least ONE of the following:
322
+
323
+ a) place your modifications in the Public Domain or otherwise make them
324
+ Freely Available, such as by posting said modifications to Usenet or an
325
+ equivalent medium, or placing the modifications on a major archive site
326
+ such as ftp.uu.net, or by allowing the Copyright Holder to include your
327
+ modifications in the Standard Version of the Package.
328
+
329
+ b) use the modified Package only within your corporation or organization.
330
+
331
+ c) rename any non-standard executables so the names do not conflict with
332
+ standard executables, which must also be provided, and provide a separate
333
+ manual page for each non-standard executable that clearly documents how it
334
+ differs from the Standard Version.
335
+
336
+ d) make other distribution arrangements with the Copyright Holder.
337
+
338
+ 4. You may distribute the programs of this Package in object code or executable
339
+ form, provided that you do at least ONE of the following:
340
+
341
+ a) distribute a Standard Version of the executables and library files,
342
+ together with instructions (in the manual page or equivalent) on where to
343
+ get the Standard Version.
344
+
345
+ b) accompany the distribution with the machine-readable source of the Package
346
+ with your modifications.
347
+
348
+ c) accompany any non-standard executables with their corresponding Standard
349
+ Version executables, giving the non-standard executables non-standard
350
+ names, and clearly documenting the differences in manual pages (or
351
+ equivalent), together with instructions on where to get the Standard
352
+ Version.
353
+
354
+ d) make other distribution arrangements with the Copyright Holder.
355
+
356
+ 5. You may charge a reasonable copying fee for any distribution of this
357
+ Package. You may charge any fee you choose for support of this Package. You
358
+ may not charge a fee for this Package itself. However, you may distribute this
359
+ Package in aggregate with other (possibly commercial) programs as part of a
360
+ larger (possibly commercial) software distribution provided that you do not
361
+ advertise this Package as a product of your own.
362
+
363
+ 6. The scripts and library files supplied as input to or produced as output
364
+ from the programs of this Package do not automatically fall under the copyright
365
+ of this Package, but belong to whomever generated them, and may be sold
366
+ commercially, and may be aggregated with this Package.
367
+
368
+ 7. C or perl subroutines supplied by you and linked into this Package shall not
369
+ be considered part of this Package.
370
+
371
+ 8. The name of the Copyright Holder may not be used to endorse or promote
372
+ products derived from this software without specific prior written permission.
373
+
374
+ 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
375
+ WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
376
+ MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
377
+
378
+ The End
@@ -0,0 +1,102 @@
1
+ # PreforkEngine
2
+
3
+ a simple prefork server framework. ruby port of perl's Parallel::Prefork
4
+ PreforkEngine supports graceful shutdown and runtime reconfiguration
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'prefork_engine'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install prefork_engine
21
+
22
+ ## Usage
23
+
24
+ ```
25
+ pm = PreforkEngine.new(
26
+ "max_workers" => 3
27
+ "spawn_interval" => 1,
28
+ "err_respawn_interval" => 3,
29
+ "trap_signals" => {
30
+ "TERM" => "TERM",
31
+ "HUP" => "TERM"
32
+ }
33
+ )
34
+
35
+ while !pm.signal_received.match(/^(TERM|HUP)$/)
36
+ pm.start {
37
+ ... do some work within the child process ...
38
+ }
39
+ end
40
+
41
+ pm.wait_all_children
42
+ ```
43
+
44
+ ## METHODS
45
+
46
+ ### new
47
+
48
+ create instance. accepts following attributes
49
+
50
+ #### max_workers
51
+
52
+ number of worker processes (default: 10)
53
+
54
+ #### spawn_interval
55
+
56
+ nterval in seconds between spawning child processes unless a child process exits abnormally (default: 0)
57
+
58
+ #### err_respawn_interval
59
+
60
+ number of seconds to deter spawning of child processes after a worker exits abnormally (default: 1)
61
+
62
+ #### trap_signals
63
+
64
+ Hash of signals to be trapped. Manager process will trap the signals listed in the keys of the hash, and send the signal specified in the associated value (if any) to all worker processes. If the associated value is a String then it is treated as the name of the signal to be sent immediately to all the worker processes. If the value is an Array the first value is treated the name of the signal and the second value is treated as the interval (in seconds) between sending the signal to each worker process.
65
+
66
+ #### on_child_reap
67
+
68
+ Proc object that is called when a child is reaped. Receives the instance to the current PreforkEngine, the child's pid, and its exit status.
69
+
70
+ #### before_fork
71
+
72
+ #### after_fork
73
+
74
+ Proc object that are called in the manager process before and after fork, if being set
75
+
76
+ ### start
77
+
78
+ The main routine. this method requires Proc object.
79
+
80
+ `start` forks child processes and executes given Proc object within the child processes.
81
+
82
+ The start function returns true within manager process upon receiving a signal specified in the `trap_signals`
83
+
84
+ ### signal_all_children
85
+
86
+ Sends singal to all worker processes
87
+
88
+ ### wait_all_children
89
+
90
+ waits until all workers exit
91
+
92
+ ## SEE ALSO
93
+
94
+ https://metacpan.org/pod/Parallel::Prefork
95
+
96
+ ## Contributing
97
+
98
+ 1. Fork it ( https://github.com/kazeburo/prefork_engine/fork )
99
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
100
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
101
+ 4. Push to the branch (`git push origin my-new-feature`)
102
+ 5. Create a new Pull Request
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
@@ -0,0 +1,192 @@
1
+ require "prefork_engine/version"
2
+ require 'proc/wait3'
3
+
4
+ class PreforkEngine
5
+ attr_reader :signal_received
6
+ attr_reader :manager_pid
7
+
8
+ def initialize(options={})
9
+ defaults = {
10
+ "max_workers" => 10,
11
+ "spawn_interval" => 0,
12
+ "err_respawn_interval" => 1,
13
+ "trap_signals" => {
14
+ "TERM" => "TERM"
15
+ },
16
+ "before_fork" => nil,
17
+ "after_fork" => nil,
18
+ "on_child_reap" => nil,
19
+ }
20
+ @options = defaults.merge(options)
21
+ @signal_received = ""
22
+ @manager_pid = ""
23
+ @generation = 0
24
+ @_no_adjust_until = 0.0
25
+ @in_child = false
26
+ @worker_pids = {}
27
+ @trap_action = {}
28
+ @options["trap_signals"].each do |k,kv|
29
+ @trap_action[k] = Signal.trap(k) { |signo|
30
+ @signal_received = Signal.signame(signo)
31
+ }
32
+ end
33
+ @trap_action["CHLD"] = Signal.trap("CHLD") {
34
+ #do nothing
35
+ }
36
+ end
37
+
38
+ def start(&block)
39
+ @manager_pid = $$
40
+ @signal_received = ""
41
+ @generation += 1
42
+ raise "cannot start another process while you are in child process" if @in_child
43
+
44
+ # main loop
45
+ while @signal_received.size == 0
46
+ action = self._decide_action() if @_no_adjust_until <= Time.now.to_f
47
+ if action > 0 then
48
+ # start a new worker
49
+ if @options["before_fork"] then
50
+ @options["before_fork"].call(self)
51
+ end
52
+ pid = nil
53
+ begin
54
+ pid = fork
55
+ rescue => e
56
+ # fork failed
57
+ warn "fork failed:#{e}";
58
+ self._update_spawn_delay(@options["err_respawn_interval"])
59
+ next
60
+ end
61
+ if pid == nil then
62
+ @in_child = true
63
+ @options["trap_signals"].each do |k,kv|
64
+ Signal.trap(k, 0) #XXX ??
65
+ end
66
+ Signal.trap("CHLD", 0) #XXX ??
67
+ exit!(true) if @signal_received.size > 0
68
+ block.call
69
+ exit!(true)
70
+ end
71
+ # parent
72
+ if @options["after_fork"] then
73
+ @options["after_fork"].call(self)
74
+ end
75
+ @worker_pids[pid] = @generation
76
+ self._update_spawn_delay(@options["spawn_interval"])
77
+ end
78
+ if res = self._wait() then
79
+ exit_pid = res[0]
80
+ status = res[1]
81
+ self._on_child_reap(exit_pid, status)
82
+ if @worker_pids.delete(exit_pid) == @generation && status != 0 then
83
+ self._update_spawn_delay(@options["err_respawn_interval"])
84
+ end
85
+ end
86
+ end
87
+
88
+ # send signals to workers
89
+ if action = self._action_for(@signal_received) then
90
+ sig = action[0]
91
+ interval = action[1]
92
+ if interval > 0 then
93
+ pids = @worker_pids.keys.sort
94
+ @delayed_task = proc {
95
+ pid = pids.shift
96
+ Process.kill(sig, pid)
97
+ if pids.empty? then
98
+ @delayed_task = nil
99
+ @delayed_task_at = nil
100
+ else
101
+ @delayed_task_at = Time.now.to_f + interval
102
+ end
103
+ }
104
+ @delayed_task_at = 0.0
105
+ @delayed_task.call
106
+ else
107
+ self.signal_all_children(sig)
108
+ end
109
+ end
110
+ return true
111
+ end #start
112
+
113
+ def signal_all_children(sig)
114
+ @worker_pids.keys.sort.each do |pid|
115
+ Process.kill(sig,pid)
116
+ end
117
+ end #signal_all_children
118
+
119
+ def num_workers
120
+ return @worker_pids.keys.length
121
+ end
122
+
123
+ def _decide_action
124
+ return 1 if self.num_workers < @options["max_workers"]
125
+ return 0
126
+ end #_decide_action
127
+
128
+ def _on_child_reap(pid,status)
129
+ if @options["on_child_reap"] then
130
+ @options["on_child_reap"].call(pid,status)
131
+ end
132
+ end
133
+
134
+ def _handle_delayed_task
135
+ while true
136
+ return nil if !@delayed_task
137
+ timeleft = @delayed_task_at - Time.now.to_f;
138
+ return timeleft if timeleft > 0
139
+ @delayed_task.call
140
+ end
141
+ end #_handle_delayed_task
142
+
143
+ def _action_for(sig)
144
+ return nil if !@options["trap_signals"].has_key?(sig)
145
+ t = @options["trap_signals"][sig]
146
+ t = [t,0] if !t.kind_of?(Enumerable)
147
+ return t
148
+ end
149
+
150
+ def wait_all_children
151
+ #XXX todo timeout
152
+ while !@worker_pids.keys.empty?
153
+ if res = self._wait() then
154
+ if @worker_pids.delete(res[0]) then
155
+ self._on_child_reap(res[0],$?.exitstatus)
156
+ end
157
+ end
158
+ end
159
+ return 0
160
+ end # wait_all_children
161
+
162
+ def _update_spawn_delay(secs)
163
+ @_no_adjust_until = secs ? Time.now.to_f + secs : 0.0
164
+ end
165
+
166
+ def _wait()
167
+ #XXX always blocking
168
+ delayed_task_sleep = self._handle_delayed_task()
169
+ delayed_fork_sleep = self._decide_action > 0 ? [@_no_adjust_until - Time.now.to_f,0].max : nil
170
+ sleep_secs = [delayed_task_sleep,delayed_fork_sleep,self._max_wait].select {|v| v != nil}
171
+ begin
172
+ if sleep_secs.min != nil then
173
+ sleep(sleep_secs.min)
174
+ # nonblock
175
+ return Process.wait3(1)
176
+ else
177
+ #block
178
+ return Process.wait3(0)
179
+ end
180
+ rescue Errno::EINTR
181
+ # nothing
182
+ rescue Errno::ECHILD
183
+ # nothing
184
+ end
185
+ return nil
186
+ end #_wait
187
+
188
+ def _max_wait
189
+ return nil
190
+ end
191
+
192
+ end
@@ -0,0 +1,3 @@
1
+ class PreforkEngine
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'prefork_engine/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "prefork_engine"
8
+ spec.version = PreforkEngine::VERSION
9
+ spec.authors = ["Masahiro Nagano"]
10
+ spec.email = ["kazeburo@gmail.com"]
11
+ spec.summary = %q{a simple prefork server framework}
12
+ spec.description = %q{a simple prefork server framework. ruby port of perl's Parallel::Prefork}
13
+ spec.homepage = "https://github.com/kazeburo/prefork_engine/"
14
+ spec.license = "Artistic"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec"
24
+
25
+ spec.add_dependency "pico_http_parser", "~> 0.0.3"
26
+ spec.add_dependency "proc-wait3", "~> 1.7.2"
27
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+ require "tempfile"
3
+
4
+ describe PreforkEngine do
5
+ it 'has a version number' do
6
+ expect(PreforkEngine::VERSION).not_to be nil
7
+ end
8
+
9
+ it 'base' do
10
+ max_workers = 10
11
+ reaped = 0
12
+ pm = PreforkEngine.new(
13
+ "max_workers" => max_workers,
14
+ "spawn_interval" => 0,
15
+ "on_child_reap" => proc {
16
+ reaped += 1
17
+ },
18
+ )
19
+ expect(pm).to be_an_instance_of(PreforkEngine)
20
+
21
+ temp = Tempfile.open(['01-base-', 'txt'])
22
+ temp.syswrite('0')
23
+ temp_path = temp.path
24
+ ppid = $$
25
+
26
+ while ! pm.signal_received.match(/^TERM$/)
27
+ pm.start {
28
+ term_req = 0
29
+ f = open(temp_path, "r+")
30
+ f.flock(File::LOCK_EX)
31
+ c = f.sysread(10)
32
+ c = c.to_i + 1
33
+ f.sysseek(0,0)
34
+ f.syswrite(c.to_s)
35
+ f.flock(File::LOCK_UN)
36
+ Signal.trap("TERM", proc {
37
+ f.flock(File::LOCK_EX)
38
+ f.sysseek(0,0)
39
+ c = f.sysread(10)
40
+ c = c.to_i + 1
41
+ f.sysseek(0,0)
42
+ f.syswrite(c.to_s)
43
+ f.flock(File::LOCK_UN)
44
+ term_req += 1
45
+ })
46
+ if c == max_workers then
47
+ sleep 3
48
+ Process.kill("TERM",ppid)
49
+ end
50
+ while term_req == 0
51
+ sleep 0.3
52
+ end
53
+ } # pm.start
54
+ end
55
+ pm.wait_all_children()
56
+
57
+ temp.sysseek(0,0)
58
+ c = temp.sysread(10)
59
+ c = c.to_i
60
+
61
+ expect(c).to eql(max_workers * 2)
62
+ expect(reaped).to eql(max_workers)
63
+ temp.close;
64
+ end
65
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require "tempfile"
3
+
4
+ describe PreforkEngine do
5
+ it 'reconfigure' do
6
+ pm = PreforkEngine.new(
7
+ "max_workers" => 1,
8
+ "trap_signals" => {
9
+ "TERM" => 'TERM',
10
+ "HUP" => 'TERM',
11
+ },
12
+ )
13
+ expect(pm).to be_an_instance_of(PreforkEngine)
14
+ c = 0
15
+ while pm.signal_received != "TERM"
16
+ c += 1
17
+ pm.start {
18
+ sleep 1
19
+ if c == 1 then
20
+ Process.kill("HUP",pm.manager_pid)
21
+ else
22
+ Process.kill("TERM",pm.manager_pid)
23
+ end
24
+ }
25
+ end
26
+ pm.wait_all_children();
27
+ expect(c).to eql(2)
28
+ end
29
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+ require "tempfile"
3
+ require 'tmpdir'
4
+
5
+ describe PreforkEngine do
6
+ it 'interval' do
7
+ d = Dir.mktmpdir
8
+ pid = fork
9
+ if pid != nil then
10
+ # parent
11
+ sleep 0.5
12
+ expect(Dir.glob("#{d}/status*").length).to eql(1)
13
+ sleep 1
14
+ expect(Dir.glob("#{d}/status*").length).to eql(2)
15
+ sleep 1
16
+ expect(Dir.glob("#{d}/status*").length).to eql(3)
17
+ sleep 1
18
+ expect(Dir.glob("#{d}/status*").length).to eql(3)
19
+ Process.kill("TERM",pid)
20
+ sleep 0.5
21
+ expect(Dir.glob("#{d}/status*").length).to eql(2)
22
+ sleep 2
23
+ expect(Dir.glob("#{d}/status*").length).to eql(1)
24
+ Process.waitpid(pid)
25
+ else
26
+ pm = PreforkEngine.new(
27
+ "max_workers" => 3,
28
+ "spawn_interval" => 1,
29
+ "trap_signals" => {
30
+ "TERM" => ["TERM",2],
31
+ "HUP" => "TERM"
32
+ }
33
+ )
34
+ while pm.signal_received != "TERM"
35
+ pm.start {
36
+ temp = Tempfile.open(['status', 'txt'],d)
37
+ term_req = 0
38
+ Signal.trap("TERM", proc {
39
+ term_req += 1
40
+ })
41
+ while term_req == 0
42
+ sleep 0.01
43
+ end
44
+ temp.close!
45
+ }
46
+ end
47
+ pm.wait_all_children
48
+ exit!()
49
+ end
50
+ FileUtils.remove_entry_secure(d)
51
+ end
52
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe PreforkEngine do
4
+ it 'before_after_fork' do
5
+ i = 0
6
+ j = 0
7
+ pm = PreforkEngine.new(
8
+ "max_workers" => 3,
9
+ "trap_signals" => {
10
+ "TERM" => "TERM",
11
+ },
12
+ "before_fork" => proc { |pm2|
13
+ i += 1
14
+ },
15
+ "after_fork" => proc { |pm3|
16
+ j += 1
17
+ }
18
+ )
19
+ while pm.signal_received != "TERM"
20
+ pm.start {
21
+ if i == 10 then
22
+ Process.kill("TERM", pm.manager_pid)
23
+ end
24
+ }
25
+ end
26
+ pm.wait_all_children()
27
+ expect(i).to be >= 10
28
+ expect(j).to be >= 10
29
+ end
30
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'prefork_engine'
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prefork_engine
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Masahiro Nagano
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pico_http_parser
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.0.3
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.0.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: proc-wait3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.7.2
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.7.2
83
+ description: a simple prefork server framework. ruby port of perl's Parallel::Prefork
84
+ email:
85
+ - kazeburo@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - lib/prefork_engine.rb
98
+ - lib/prefork_engine/version.rb
99
+ - prefork_engine.gemspec
100
+ - spec/01_base_spec.rb
101
+ - spec/02_reconfigure_spec.rb
102
+ - spec/04_interval_spec.rb
103
+ - spec/05_before_after_fork_spec.rb
104
+ - spec/spec_helper.rb
105
+ homepage: https://github.com/kazeburo/prefork_engine/
106
+ licenses:
107
+ - Artistic
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.2.2
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: a simple prefork server framework
129
+ test_files:
130
+ - spec/01_base_spec.rb
131
+ - spec/02_reconfigure_spec.rb
132
+ - spec/04_interval_spec.rb
133
+ - spec/05_before_after_fork_spec.rb
134
+ - spec/spec_helper.rb