forkworker 0.0.1
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/forkworker.rb +7 -0
- data/lib/forkworker/leader.rb +162 -0
- data/lib/forkworker/logger.rb +7 -0
- data/lib/forkworker/worker.rb +32 -0
- metadata +88 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: cbe42b40a14925ceef50b2ca98e2428eedf4385fbdbe24b83e8d7036ef478c6b
|
4
|
+
data.tar.gz: a9a150452ff9f4d2df1c7f7f31f5bc3d4247aec0977c1774ae897ed306021e05
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3324c1e818b7d9fd7111cbed80e0ffa94712b0125581bb29d445561acb4a1f433ae0bf383c6290b79da6d9af196bfa0cd764b4ea434a1a2ed8a44e9075de4e57
|
7
|
+
data.tar.gz: 9e0e70f96f16b3f8b41953e372be5ad36509ba2cf877cb0dcf30eb8294b6eb9fc964d6682c55a532ad6cb6b1ac0d9d370829a7760e07e799b0156c3ed33ffedd
|
data/lib/forkworker.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
module Forkworker
|
2
|
+
class Leader
|
3
|
+
include Forkworker::Logger
|
4
|
+
|
5
|
+
def initialize(worker_num, pidfile: nil, setup_block: nil, prefork_block: nil, fork_block: nil, reporting_block: nil)
|
6
|
+
@wanted_number_of_workers = worker_num
|
7
|
+
@running = true
|
8
|
+
@worker_pids = []
|
9
|
+
@signals_received = []
|
10
|
+
@pidfile = pidfile
|
11
|
+
|
12
|
+
@setup_block = setup_block
|
13
|
+
@prefork_block = prefork_block
|
14
|
+
@fork_block = fork_block
|
15
|
+
@reporting_block = reporting_block
|
16
|
+
|
17
|
+
write_pid if @pidfile
|
18
|
+
end
|
19
|
+
|
20
|
+
def start!
|
21
|
+
traps
|
22
|
+
update_leader_title
|
23
|
+
@setup_block.call if @setup_block
|
24
|
+
spawn_missing_workers
|
25
|
+
|
26
|
+
gameloop = 1
|
27
|
+
|
28
|
+
until @worker_pids.dup.length == 0 && @running == false
|
29
|
+
sleep 0.25
|
30
|
+
|
31
|
+
# Handle actions
|
32
|
+
while(signal = @signals_received.shift)
|
33
|
+
case signal
|
34
|
+
when 'CHLD'
|
35
|
+
@worker_pids.dup.each do |wpid|
|
36
|
+
begin
|
37
|
+
wpid, _status = Process.waitpid(wpid, Process::WNOHANG)
|
38
|
+
@worker_pids.delete(wpid)
|
39
|
+
rescue Errno::ECHILD
|
40
|
+
end
|
41
|
+
end
|
42
|
+
when 'TTIN'
|
43
|
+
debug "-- handled #{signal}: wanted number of workers are now: #{@wanted_number_of_workers}"
|
44
|
+
|
45
|
+
@wanted_number_of_workers += 1
|
46
|
+
when 'TTOU'
|
47
|
+
unless @wanted_number_of_workers == 0
|
48
|
+
@wanted_number_of_workers -= 1
|
49
|
+
end
|
50
|
+
|
51
|
+
debug "-- handled #{signal}: wanted number of workers are now: #{@wanted_number_of_workers}"
|
52
|
+
when 'TERM'
|
53
|
+
debug "-- handled #{signal}"
|
54
|
+
|
55
|
+
@running = false
|
56
|
+
shutdown_each_worker(:TERM)
|
57
|
+
when 'QUIT'
|
58
|
+
debug "-- handled #{signal}"
|
59
|
+
|
60
|
+
@running = false
|
61
|
+
shutdown_each_worker(:QUIT)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Spawn missing workers if we are not getting shut down
|
66
|
+
if @running
|
67
|
+
spawn_missing_workers
|
68
|
+
end
|
69
|
+
|
70
|
+
if gameloop % 20 == 0 && @reporting_block
|
71
|
+
@reporting_block.call
|
72
|
+
end
|
73
|
+
|
74
|
+
update_leader_title
|
75
|
+
|
76
|
+
gameloop += 1
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def spawn_missing_workers
|
83
|
+
begin
|
84
|
+
while (@worker_pids.length + 1) <= @wanted_number_of_workers
|
85
|
+
worker_data = if @prefork_block
|
86
|
+
@prefork_block.call
|
87
|
+
else
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
if(pid = fork)
|
92
|
+
@worker_pids << pid
|
93
|
+
update_leader_title
|
94
|
+
else
|
95
|
+
Worker.new.work!(worker_data, &@fork_block)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
rescue Forkworker::NoMoreWork
|
99
|
+
debug "-- No more work, so we're just finishing up running processes"
|
100
|
+
@running = false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def traps
|
105
|
+
# By trapping the :CHLD signal our process will be notified by the kernel when one of its children exits.
|
106
|
+
trap(:CHLD) do
|
107
|
+
@signals_received << 'CHLD'
|
108
|
+
end
|
109
|
+
|
110
|
+
trap(:TERM) do
|
111
|
+
@signals_received << 'TERM'
|
112
|
+
end
|
113
|
+
|
114
|
+
trap(:TTIN) do
|
115
|
+
@signals_received << 'TTIN'
|
116
|
+
end
|
117
|
+
|
118
|
+
trap(:TTOU) do
|
119
|
+
@signals_received << 'TTOU'
|
120
|
+
end
|
121
|
+
|
122
|
+
[:QUIT, :INT].each do |signal|
|
123
|
+
trap(signal) do
|
124
|
+
@signals_received << 'QUIT'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def update_leader_title
|
130
|
+
run_state = @running ? 'running' : 'shutting down'
|
131
|
+
$PROGRAM_NAME = "Leader ##{Process.pid} | #{run_state} | Workers=#{@worker_pids.length}/#{@wanted_number_of_workers}"
|
132
|
+
end
|
133
|
+
|
134
|
+
def shutdown_worker(signal, wpid)
|
135
|
+
begin
|
136
|
+
Process.kill(signal, wpid)
|
137
|
+
rescue Errno::ESRCH
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def shutdown_each_worker(signal)
|
142
|
+
@worker_pids.dup.each { |wpid| shutdown_worker(signal, wpid) }
|
143
|
+
end
|
144
|
+
|
145
|
+
def write_pid
|
146
|
+
if File.exist?(@pidfile) && (pid = File.read(@pidfile))
|
147
|
+
begin
|
148
|
+
Process.getpgid(pid.to_i) # throws Errno::ESRCH if process with pid exists
|
149
|
+
debug "Program is already running on pid #{pid} specified in #{@pidfile}"
|
150
|
+
exit 1
|
151
|
+
rescue Errno::ESRCH
|
152
|
+
false
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
File.open(@pidfile, 'w') do |f|
|
157
|
+
f.write Process.pid
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Forkworker
|
2
|
+
class Worker
|
3
|
+
include Forkworker::Logger
|
4
|
+
|
5
|
+
def work!(worker_data, &block)
|
6
|
+
@worker_data = worker_data
|
7
|
+
@running = true
|
8
|
+
update_title("spawned")
|
9
|
+
traps
|
10
|
+
|
11
|
+
instance_eval(&block)
|
12
|
+
|
13
|
+
exit(0)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def traps
|
19
|
+
trap(:TERM) do
|
20
|
+
@running = false
|
21
|
+
update_title
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_title(status = nil)
|
26
|
+
@last_status = status if status
|
27
|
+
run_state = @running ? 'running' : 'shutting down'
|
28
|
+
|
29
|
+
$PROGRAM_NAME = "Worker ##{Process.pid} | #{run_state} | #{@last_status}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: forkworker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kasper Grubbe
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-07-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.9.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.9.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.13.1
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.13.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry-remote
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.1.8
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.1.8
|
55
|
+
description: Forkworker lets you manage forking workloads easily
|
56
|
+
email: rubygems@kaspergrubbe.com
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- lib/forkworker.rb
|
62
|
+
- lib/forkworker/leader.rb
|
63
|
+
- lib/forkworker/logger.rb
|
64
|
+
- lib/forkworker/worker.rb
|
65
|
+
homepage: https://rubygems.org/gems/forkworker
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubygems_version: 3.1.2
|
85
|
+
signing_key:
|
86
|
+
specification_version: 4
|
87
|
+
summary: Manage forking workloads with ease
|
88
|
+
test_files: []
|