tracks 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/tracks.rb +35 -30
  2. metadata +6 -6
data/lib/tracks.rb CHANGED
@@ -169,18 +169,21 @@ class Tracks
169
169
  # responding to #call. options should be a hash, with the following optional
170
170
  # keys, as symbols
171
171
  #
172
- # [:host] the host to listen on, defaults to 0.0.0.0
173
- # [:port] the port to listen on, defaults to 9292
174
- # [:read_timeout] the maximum amount of time, in seconds, to wait on idle
175
- # connections, defaults to 30
172
+ # [:host] the host to listen on, defaults to 0.0.0.0
173
+ # [:port] the port to listen on, defaults to 9292
174
+ # [:read_timeout] the maximum amount of time, in seconds, to wait on idle
175
+ # connections, defaults to 30
176
+ # [:shutdown_timeout] the maximum amount of time, in seconds, to wait for
177
+ # in process requests to complete when signalled to shut
178
+ # down, defaults to 30
176
179
  #
177
180
  def initialize(app, options={})
178
181
  @host = options[:host] || options[:Host] || "0.0.0.0"
179
182
  @port = (options[:port] || options[:Port] || "9292").to_s
180
183
  @read_timeout = options[:read_timeout] || 30
184
+ @shutdown_timeout = options[:shutdown_timeout] || 30
181
185
  @app = app
182
186
  @shutdown_signal, @signal_shutdown = IO.pipe
183
- @threads = ThreadGroup.new
184
187
  end
185
188
 
186
189
  # :call-seq: Tracks.run(rack_app[, options]) -> nil
@@ -191,36 +194,25 @@ class Tracks
191
194
  new(app, options).listen
192
195
  end
193
196
 
194
- # :call-seq: Tracks.shutdown([wait_time]) -> bool
197
+ # :call-seq: Tracks.shutdown -> nil
195
198
  #
196
- # Shut down all running Tracks servers, after waiting wait_time seconds for
197
- # each to gracefully shutdown. See #shutdown for more.
199
+ # Signal all running Tracks servers to shutdown.
198
200
  #
199
- def self.shutdown(wait=30)
200
- running.dup.inject(true) {|memo, s| s.shutdown(wait) && memo}
201
+ def self.shutdown
202
+ running.dup.each {|s| s.shutdown} && nil
201
203
  end
202
204
 
203
- # :call-seq: server.shutdown(wait_time) -> bool
205
+ # :call-seq: server.shutdown -> nil
204
206
  #
205
- # Shut down the server, after waiting wait_time seconds for graceful
206
- # shutdown. Returns true on graceful shutdown, false otherwise.
207
+ # Signal the server to shut down.
207
208
  #
208
- # wait_time defaults to 30 seconds.
209
- #
210
- # A return value of false indicates there were threads left running after
211
- # wait_time had expired which were forcibly killed. This may leave resources
212
- # in an inconsistant state, and it is advised you exit the process in this
213
- # case (likely what you were planning anyway).
214
- #
215
- def shutdown(wait=30)
209
+ def shutdown
216
210
  @shutdown = true
217
211
  self.class.running.delete(self)
218
- @signal_shutdown << "x"
219
- wait -= sleep 1 until @threads.list.empty? || wait <= 0
220
- @threads.list.each {|thread| thread.kill}.empty?
212
+ @signal_shutdown << "x" && nil
221
213
  end
222
214
 
223
- # :call-seq: server.listen([socket_server]) -> nil
215
+ # :call-seq: server.listen([socket_server]) -> bool
224
216
  #
225
217
  # Start listening for/accepting connections on socket_server. socket_server
226
218
  # defaults to a TCP server listening on the host and port supplied to ::new.
@@ -231,19 +223,27 @@ class Tracks
231
223
  # This method will block until #shutdown is called. The socket_server will
232
224
  # be closed when this method returns.
233
225
  #
226
+ # A return value of false indicates there were threads left running after
227
+ # shutdown_timeout had expired which were forcibly killed. This may leave
228
+ # resources in an inconsistant state, and it is advised you exit the process
229
+ # in this case (likely what you were planning anyway).
230
+ #
234
231
  def listen(server=TCPServer.new(@host, @port))
235
232
  @shutdown = false
236
233
  server.listen(1024) if server.respond_to?(:listen)
237
234
  @port, @host = server.addr[1,2].map{|e| e.to_s} if server.respond_to?(:addr)
238
235
  servers = [server, @shutdown_signal]
236
+ threads = ThreadGroup.new
239
237
  self.class.running << self
240
238
  puts "Tracks HTTP server available at #{@host}:#{@port}"
241
239
  while select(servers, nil, nil) && !@shutdown
242
- @threads.add(Thread.new(server.accept) {|sock| on_connection(sock)})
240
+ threads.add(Thread.new(server.accept) {|sock| on_connection(sock)})
243
241
  end
244
- @shutdown_signal.sysread(1) && nil
245
- ensure
246
242
  server.close
243
+ wait = @shutdown_timeout
244
+ wait -= sleep 1 until threads.list.empty? || wait <= 0
245
+ @shutdown_signal.sysread(1)
246
+ threads.list.each {|thread| thread.kill}.empty?
247
247
  end
248
248
 
249
249
  # :call-seq: server.on_connection(socket) -> nil
@@ -260,10 +260,14 @@ class Tracks
260
260
  def on_connection(socket)
261
261
  parser = HTTPTools::Parser.new
262
262
  buffer = ""
263
- sockets = [socket]
263
+ sockets = [socket, @shutdown_signal]
264
+ idle = false
264
265
  reader = Proc.new do
265
266
  readable, = select(sockets, nil, nil, @read_timeout)
266
267
  return unless readable
268
+ sockets.delete(@shutdown_signal) if @shutdown
269
+ return if idle && @shutdown
270
+ idle = false
267
271
  begin
268
272
  socket.sysread(16384, buffer)
269
273
  parser << buffer
@@ -301,11 +305,12 @@ class Tracks
301
305
  body.each {|chunk| socket << chunk}
302
306
  body.close if body.respond_to?(:close)
303
307
 
304
- if keep_alive
308
+ if keep_alive && !@shutdown
305
309
  reader.call until parser.finished?
306
310
  input.reset
307
311
  remainder = parser.rest.lstrip
308
312
  parser.reset << remainder
313
+ idle = true
309
314
  else
310
315
  break
311
316
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tracks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-21 00:00:00.000000000Z
12
+ date: 2012-05-26 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
16
- requirement: &70357509168700 !ruby/object:Gem::Requirement
16
+ requirement: &70193797609180 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.0.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70357509168700
24
+ version_requirements: *70193797609180
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: http_tools
27
- requirement: &70357509168220 !ruby/object:Gem::Requirement
27
+ requirement: &70193797608700 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: 0.4.1
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70357509168220
35
+ version_requirements: *70193797608700
36
36
  description: A bare-bones Ruby HTTP server that talks Rack and uses a thread per connection
37
37
  model of concurrency.
38
38
  email: mat@sourcetagsandcodes.com