pitchfork 0.16.0 → 0.18.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.
@@ -44,20 +44,47 @@ module Pitchfork
44
44
  end
45
45
  end
46
46
 
47
- def mold_deadline
48
- self[MOLD_TICK_OFFSET]
47
+ class WorkerState
48
+ def initialize(field)
49
+ @field = field
50
+ end
51
+
52
+ def ready?
53
+ (@field.value & 1) == 1
54
+ end
55
+
56
+ def ready=(bool)
57
+ if bool
58
+ @field.value |= 1
59
+ else
60
+ @field.value &= ~1
61
+ end
62
+ end
63
+
64
+ def deadline=(value)
65
+ # Shift the value up and preserve the current ready bit.
66
+ @field.value = (value << 1) | (@field.value & 1)
67
+ end
68
+
69
+ def deadline
70
+ @field.value >> 1
71
+ end
72
+ end
73
+
74
+ def mold_state
75
+ WorkerState.new(self[MOLD_TICK_OFFSET])
49
76
  end
50
77
 
51
- def mold_promotion_deadline
52
- self[MOLD_PROMOTION_TICK_OFFSET]
78
+ def mold_promotion_state
79
+ WorkerState.new(self[MOLD_PROMOTION_TICK_OFFSET])
53
80
  end
54
81
 
55
- def service_deadline
56
- self[SERVICE_TICK_OFFSET]
82
+ def service_state
83
+ WorkerState.new(self[SERVICE_TICK_OFFSET])
57
84
  end
58
85
 
59
- def worker_deadline(worker_nr)
60
- self[WORKER_TICK_OFFSET + worker_nr]
86
+ def worker_state(worker_nr)
87
+ WorkerState.new(self[WORKER_TICK_OFFSET + worker_nr])
61
88
  end
62
89
 
63
90
  def [](offset)
@@ -75,4 +102,4 @@ module Pitchfork
75
102
  end
76
103
  end
77
104
  end
78
- end
105
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pitchfork
4
- VERSION = "0.16.0"
4
+ VERSION = "0.18.0"
5
5
  module Const
6
6
  UNICORN_VERSION = '6.1.0'
7
7
  end
@@ -14,17 +14,17 @@ module Pitchfork
14
14
  # :stopdoc:
15
15
  EXIT_SIGNALS = [:QUIT, :TERM]
16
16
  attr_accessor :nr, :pid, :generation
17
- attr_reader :master, :requests_count
17
+ attr_reader :monitor, :requests_count
18
18
 
19
19
  def initialize(nr, pid: nil, generation: 0)
20
20
  @nr = nr
21
21
  @pid = pid
22
22
  @generation = generation
23
23
  @mold = false
24
- @to_io = @master = nil
24
+ @to_io = @monitor = nil
25
25
  @exiting = false
26
26
  @requests_count = 0
27
- init_deadline
27
+ init_state
28
28
  end
29
29
 
30
30
  def exiting?
@@ -32,7 +32,7 @@ module Pitchfork
32
32
  end
33
33
 
34
34
  def pending?
35
- @master.nil?
35
+ @monitor.nil?
36
36
  end
37
37
 
38
38
  def outdated?
@@ -46,31 +46,44 @@ module Pitchfork
46
46
 
47
47
  case message
48
48
  when Message::MoldSpawned
49
- @deadline_drop = SharedMemory.mold_promotion_deadline
49
+ @state_drop = SharedMemory.mold_promotion_state
50
50
  when Message::MoldReady
51
- @deadline_drop = SharedMemory.mold_deadline
51
+ @state_drop = SharedMemory.mold_state
52
52
  end
53
53
  end
54
54
 
55
- def register_to_master(control_socket)
55
+ def register_to_monitor(control_socket)
56
56
  create_socketpair!
57
- message = Message::WorkerSpawned.new(@nr, @pid, generation, @master)
57
+ message = Message::WorkerSpawned.new(@nr, @pid, generation, @monitor)
58
58
  control_socket.sendmsg(message)
59
- @master.close
59
+ @monitor.close
60
60
  end
61
61
 
62
62
  def start_promotion(control_socket)
63
63
  create_socketpair!
64
- message = Message::MoldSpawned.new(@nr, @pid, generation, @master)
64
+ message = Message::MoldSpawned.new(@nr, @pid, generation, @monitor)
65
65
  control_socket.sendmsg(message)
66
- @master.close
66
+ @monitor.close
67
67
  end
68
68
 
69
69
  def finish_promotion(control_socket)
70
+ SharedMemory.current_generation = @generation
70
71
  message = Message::MoldReady.new(@nr, @pid, generation)
71
72
  control_socket.sendmsg(message)
72
- SharedMemory.current_generation = @generation
73
- @deadline_drop = SharedMemory.mold_deadline
73
+ @state_drop = SharedMemory.mold_state
74
+ end
75
+
76
+ def notify_ready(control_socket)
77
+ self.ready = true
78
+ message = if worker?
79
+ Message::WorkerReady.new(@nr, @pid, @generation)
80
+ elsif service?
81
+ Message::ServiceReady.new(@pid, @generation)
82
+ else
83
+ raise "Unexpected child type"
84
+ end
85
+
86
+ control_socket.sendmsg(message)
74
87
  end
75
88
 
76
89
  def promote(generation)
@@ -93,7 +106,7 @@ module Pitchfork
93
106
  def promoted!(timeout)
94
107
  @mold = true
95
108
  @nr = nil
96
- @deadline_drop = SharedMemory.mold_promotion_deadline
109
+ @state_drop = SharedMemory.mold_promotion_state
97
110
  update_deadline(timeout) if timeout
98
111
  self
99
112
  end
@@ -106,12 +119,16 @@ module Pitchfork
106
119
  false
107
120
  end
108
121
 
122
+ def worker?
123
+ !mold? && !service?
124
+ end
125
+
109
126
  def to_io # IO.select-compatible
110
127
  @to_io.to_io
111
128
  end
112
129
 
113
- def master=(socket)
114
- @master = MessageSocket.new(socket)
130
+ def monitor=(socket)
131
+ @monitor = MessageSocket.new(socket)
115
132
  end
116
133
 
117
134
  # call a signal handler immediately without triggering EINTR
@@ -126,7 +143,7 @@ module Pitchfork
126
143
  trap(sig, old_cb)
127
144
  end
128
145
 
129
- # master sends fake signals to children
146
+ # monitor sends fake signals to children
130
147
  def soft_kill(sig) # :nodoc:
131
148
  signum = Signal.list[sig.to_s] or raise ArgumentError, "BUG: bad signal: #{sig.inspect}"
132
149
 
@@ -149,7 +166,7 @@ module Pitchfork
149
166
  case buf = @to_io.recvmsg_nonblock(exception: false)
150
167
  when :wait_readable # keep waiting
151
168
  return false
152
- when nil # EOF master died, but we are at a safe place to exit
169
+ when nil # EOF monitor died, but we are at a safe place to exit
153
170
  fake_sig(:QUIT)
154
171
  return false
155
172
  when Message::SoftKill
@@ -171,18 +188,29 @@ module Pitchfork
171
188
  super || (!@nr.nil? && @nr == other)
172
189
  end
173
190
 
191
+ def ready?
192
+ @state_drop.ready?
193
+ end
194
+
195
+ def ready=(bool)
196
+ @state_drop.ready = bool
197
+ end
198
+
174
199
  def update_deadline(timeout)
175
200
  self.deadline = Pitchfork.time_now(true) + timeout
176
201
  end
177
202
 
178
203
  # called in the worker process
179
204
  def deadline=(value) # :nodoc:
180
- @deadline_drop.value = value
205
+ # If we are (re)setting to zero mark worker as not ready.
206
+ self.ready = false if value == 0
207
+
208
+ @state_drop.deadline = value
181
209
  end
182
210
 
183
- # called in the master process
211
+ # called in the monitor process
184
212
  def deadline # :nodoc:
185
- @deadline_drop.value
213
+ @state_drop.deadline
186
214
  end
187
215
 
188
216
  def reset
@@ -193,26 +221,34 @@ module Pitchfork
193
221
  @requests_count += by
194
222
  end
195
223
 
196
- # called in both the master (reaping worker) and worker (SIGQUIT handler)
224
+ # called in both the monitor (reaping worker) and worker (SIGQUIT handler)
197
225
  def close # :nodoc:
198
226
  self.deadline = 0
199
- @master.close if @master
227
+ @monitor.close if @monitor
200
228
  @to_io.close if @to_io
201
229
  end
202
230
 
203
231
  def create_socketpair!
204
- @to_io, @master = Info.keep_ios(Pitchfork.socketpair)
232
+ @to_io, @monitor = Info.keep_ios(Pitchfork.socketpair)
205
233
  end
206
234
 
207
235
  def after_fork_in_child
208
- @master&.close
236
+ @monitor&.close
237
+ end
238
+
239
+ def to_log
240
+ if mold?
241
+ pid ? "mold gen=#{generation} pid=#{pid}" : "mold gen=#{generation}"
242
+ else
243
+ pid ? "worker=#{nr} gen=#{generation} pid=#{pid}" : "worker=#{nr} gen=#{generation}"
244
+ end
209
245
  end
210
246
 
211
247
  private
212
248
 
213
- def init_deadline
249
+ def init_state
214
250
  if nr
215
- @deadline_drop = SharedMemory.worker_deadline(@nr)
251
+ @state_drop = SharedMemory.worker_state(@nr)
216
252
  self.deadline = 0
217
253
  else
218
254
  promoted!(nil)
@@ -222,14 +258,14 @@ module Pitchfork
222
258
  def pipe=(socket)
223
259
  raise ArgumentError, "pipe can't be nil" unless socket
224
260
  Info.keep_io(socket)
225
- @master = MessageSocket.new(socket)
261
+ @monitor = MessageSocket.new(socket)
226
262
  end
227
263
 
228
264
  def send_message_nonblock(message)
229
265
  success = false
230
- return false unless @master
266
+ return false unless @monitor
231
267
  begin
232
- case @master.sendmsg_nonblock(message, exception: false)
268
+ case @monitor.sendmsg_nonblock(message, exception: false)
233
269
  when :wait_writable
234
270
  else
235
271
  success = true
@@ -250,17 +286,21 @@ module Pitchfork
250
286
  true
251
287
  end
252
288
 
253
- def register_to_master(control_socket)
289
+ def register_to_monitor(control_socket)
254
290
  create_socketpair!
255
- message = Message::ServiceSpawned.new(@pid, generation, @master)
291
+ message = Message::ServiceSpawned.new(@pid, generation, @monitor)
256
292
  control_socket.sendmsg(message)
257
- @master.close
293
+ @monitor.close
294
+ end
295
+
296
+ def to_log
297
+ pid ? "service gen=#{generation} pid=#{pid}" : "service gen=#{generation}"
258
298
  end
259
299
 
260
300
  private
261
301
 
262
- def init_deadline
263
- @deadline_drop = SharedMemory.service_deadline
302
+ def init_state
303
+ @state_drop = SharedMemory.service_state
264
304
  self.deadline = 0
265
305
  end
266
306
  end
data/lib/pitchfork.rb CHANGED
@@ -87,7 +87,12 @@ module Pitchfork
87
87
  when /\.ru$/
88
88
  raw = File.read(ru)
89
89
  raw.sub!(/^__END__\n.*/, '')
90
- eval("Rack::Builder.new {(\n#{raw}\n)}.to_app", TOPLEVEL_BINDING, ru)
90
+ lines = raw.lines
91
+ trailing_comments_index = lines.index { |line| !line.start_with?('#') }
92
+ prelude = lines[0...trailing_comments_index].join
93
+ raw = lines[trailing_comments_index..-1].join
94
+
95
+ eval("#{prelude}\nRack::Builder.new do\n#{raw}\nend.to_app\n", TOPLEVEL_BINDING, ru)
91
96
  else
92
97
  require ru
93
98
  Object.const_get(File.basename(ru, '.rb').capitalize)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pitchfork
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.0
4
+ version: 0.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean Boussier
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-26 00:00:00.000000000 Z
11
+ date: 2025-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -141,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
141
  - !ruby/object:Gem::Version
142
142
  version: '0'
143
143
  requirements: []
144
- rubygems_version: 3.5.11
144
+ rubygems_version: 3.5.9
145
145
  signing_key:
146
146
  specification_version: 4
147
147
  summary: Rack HTTP server for fast clients and Unix