runaway 1.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ee88a2e47d1bf2847893a4b258d236efcfff3fae
4
- data.tar.gz: f3edef5cc5fae9363f1d56978c0465b56faa0a24
3
+ metadata.gz: 8140c09b38d805e869821a9e25fcc4807de4931f
4
+ data.tar.gz: 44fe561135e1fb3b8abb21094f20fc2435ecdea4
5
5
  SHA512:
6
- metadata.gz: 0489708f0657aeb6e7b91c7f23e2007974d22577afe01b9c04b2dca6d12a9fcdbdf68cdbb965b75970351333eab45ec6a1599f3d34a3727abda9f8daa47ab248
7
- data.tar.gz: 34eeca38ad28dfb7b205a40b9517f3b3123683a9fdd466031b848bd1780f9b67e1b477c3a1d10104b412b36ee80df21c33e1ab2dfcf5af799e0b0f86963d3ede
6
+ metadata.gz: d46f84a99dd4e7e84ccab8779c67e67264bd6bb4e35ce56d3418d0115d0e9617001faabe1224b5ed26455c7d3e14777fb8982b487c95e64579b780ccedc82180
7
+ data.tar.gz: 8dd7da8f9d6f8fc92c886d68d4b2c6be7bb8f785140ef82f215d47751465100baaa009a69d2542d1529b2a14af2de258d55ed2bf5b738dccc51f917ddff6e0e6
data/README.md CHANGED
@@ -1,18 +1,9 @@
1
1
  # runaway
2
2
 
3
- Spin off work into child processes and terminate it on time
3
+ Spin off work into child processes and terminate it on time. If you need the process to be done
4
+ within a certain time period (and if it takes longer it probably is hanging):
4
5
 
5
- To simply ensure the process keeps responding (the Ruby runtime still can do context-switching
6
- and can respond to Unix signals):
7
-
8
- Runaway.spin do
9
- ActiveRAMGobbler.load_many_things(num_things: 10_000_000)
10
- end
11
-
12
- If you need the process to be done within a certain time period (and if it takes longer
13
- it probably is hanging):
14
-
15
- Runaway.spin(must_quite_within: 15) do # ensures termination within 15 seconds
6
+ Runaway.spin(must_quit_within: 15) do # ensures termination within 15 seconds
16
7
  `/bin/proprietary_render_server/bin/render --put-server-on-fire=yes`
17
8
  end
18
9
 
@@ -1,14 +1,10 @@
1
- require 'securerandom'
2
-
3
1
  module Runaway
4
- VERSION = '1.0.1'
2
+ VERSION = '2.0.0'
5
3
 
6
4
  UncleanExit = Class.new(StandardError)
7
5
  Child = Class.new(StandardError)
8
- HeartbeatTimeout = Class.new(Child)
9
6
  RuntimeExceeded = Class.new(Child)
10
7
 
11
- DEFAULT_HEARTBEAT_INTERVAL = 2
12
8
  TERM = 'TERM'.freeze
13
9
  KILL = 'KILL'.freeze
14
10
  USR2 = 'USR2'.freeze
@@ -18,20 +14,12 @@ module Runaway
18
14
  # Acts as a substitute for a Logger
19
15
  module NullLogger; def self.warn(*); end; end
20
16
 
21
- def self.spin(must_quit_within: INF, heartbeat_interval: DEFAULT_HEARTBEAT_INTERVAL,
22
- logger: NullLogger, &block_to_run_in_child)
23
- cookie = SecureRandom.hex(1)
24
- r, w = IO.pipe
17
+ def self.spin(must_quit_within: INF, logger: NullLogger, &block_to_run_in_child)
25
18
  child_pid = fork do
26
- r.close_read
27
19
  # Remove anything that was there from the parent
28
- [USR2, TERM, KILL].each { |reset_sig| trap(reset_sig, DEFAULT) }
29
-
30
- # When the parent asks us for a heartbeat, send the cookie back
31
- trap(USR2) { w.write(cookie); w.flush }
20
+ [TERM, KILL].each { |reset_sig| trap(reset_sig, DEFAULT) }
32
21
  block_to_run_in_child.call
33
22
  end
34
- w.close_write
35
23
 
36
24
  started_at = Time.now
37
25
 
@@ -49,10 +37,9 @@ module Runaway
49
37
  (Process.kill(sig, child_pid) rescue Errno::ESRCH) if !has_quit
50
38
  }
51
39
 
52
- last_heartbeat_sent = started_at
53
40
  begin
54
41
  loop do
55
- sleep 0.5
42
+ sleep 1
56
43
 
57
44
  break if has_quit
58
45
 
@@ -62,22 +49,6 @@ module Runaway
62
49
  raise RuntimeExceeded.new('%d did not terminate after %d secs (limited to %d secs)' % [
63
50
  child_pid, running_for, must_quit_within])
64
51
  end
65
-
66
- # Then check if it is time to poke it with a heartbeat
67
- at = Time.now
68
- next if (at - last_heartbeat_sent) < heartbeat_interval
69
- last_heartbeat_sent = at
70
-
71
- # Then send it the USR2 as a "ping", and expect a "pong" in
72
- # the form of a pipe write. If the pipe is still not readable
73
- # after a certain time, we assume the process has hung.
74
- Process.kill(USR2, child_pid)
75
- select_timeout = (heartbeat_interval * 2)
76
- ready_read = IO.select([r], [], [], select_timeout)
77
- if ready_read.nil?
78
- raise HeartbeatTimeout.new('%d did not reply to heartbeat after %d secs' % [child_pid, select_timeout])
79
- end
80
- r.read(cookie.bytesize)
81
52
  end
82
53
  rescue Runaway => terminating_error
83
54
  logger.error "Terminating %d - %s: %s" % [child_pid, terminating_error.class, terminating_error.message]
@@ -97,7 +68,5 @@ module Runaway
97
68
  raise unclean_exit_error if unclean_exit_error
98
69
 
99
70
  :done
100
- ensure
101
- r.close_read
102
71
  end
103
72
  end
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: runaway 1.0.1 ruby lib
5
+ # stub: runaway 2.0.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "runaway"
9
- s.version = "1.0.1"
9
+ s.version = "2.0.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Julik Tarkhanov"]
14
- s.date = "2016-03-01"
14
+ s.date = "2016-04-07"
15
15
  s.description = "Spin off blocks in child processes and make sure they terminate on time"
16
16
  s.email = "me@julik.nl"
17
17
  s.extra_rdoc_files = [
@@ -5,7 +5,7 @@ describe "Runaway" do
5
5
 
6
6
  it 'supports all the options' do
7
7
  require 'logger'
8
- Runaway.spin(must_quit_within: 2, heartbeat_interval: 0.3, logger: Logger.new($stdout)) {} # just do nothing
8
+ Runaway.spin(must_quit_within: 2, logger: Logger.new($stdout)) {} # just do nothing
9
9
  end
10
10
 
11
11
  context 'with a process that quits cleanly' do
@@ -42,28 +42,4 @@ describe "Runaway" do
42
42
  }
43
43
  end
44
44
  end
45
-
46
- context 'when a process terminates before the first heartbeat has to be dispatched' do
47
- it 'just returns :done' do
48
- t = Time.now
49
- return_token = Runaway.spin(heartbeat_interval: 5) { sleep 0.1 }
50
- expect(return_token).to eq(:done)
51
- delta = Time.now - t
52
- expect(delta).to be < 2
53
- end
54
- end
55
-
56
- context 'when a process stops responding to heartbeats' do
57
- it 'kills it quickly and raises an error' do
58
- t = Time.now
59
- expect {
60
- # Delete, then override the USR2 trap so that heartbeats do not get handled at all
61
- Runaway.spin(heartbeat_interval: 0.8) { trap('USR2', 'DEFAULT'); trap('USR2') {}; sleep 45 }
62
- }.to raise_error {|err|
63
- expect(err).to be_kind_of(Runaway::HeartbeatTimeout)
64
- expect(err.message).to match(/\d+ did not reply to heartbeat after \d+ secs/)
65
- expect(Time.now - t).to be < 5 # should really ahve killed it fast
66
- }
67
- end
68
- end
69
45
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: runaway
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-01 00:00:00.000000000 Z
11
+ date: 2016-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec