safe_timeout 0.0.3 → 0.0.4
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 +4 -4
- data/bin/safe_timeout +3 -0
- data/lib/safe_timeout/interrupting_child_process.rb +12 -30
- data/lib/safe_timeout/spawner.rb +32 -0
- data/lib/safe_timeout/version.rb +1 -1
- data/lib/safe_timeout.rb +12 -4
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1f06d3a6a1b3d03875ee2501d1bfa1ceefcc676
|
4
|
+
data.tar.gz: 7d5a7e5273989529d22bddb49927ae091866ba96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48ef6022f8aecfb0e69c348adec09d589c3dba1929896ce843685f45123bc6d43605f66823ee3271b2a73b5d70852b159f37906925160e8559f930f0fedd9b75
|
7
|
+
data.tar.gz: 14e77cc5ca9f6c1f24459fda1e35186697af31a75814658f32591bc999231de8d4c85f78d6cf8f1755a0e58341eaa3097d9f0f8b6c9e68f5f31f8bdeb0b6c35b
|
data/bin/safe_timeout
ADDED
@@ -1,43 +1,25 @@
|
|
1
1
|
module SafeTimeout
|
2
2
|
class InterruptingChildProcess
|
3
3
|
|
4
|
-
def initialize(
|
5
|
-
@
|
6
|
-
@
|
7
|
-
end
|
4
|
+
def initialize(ppid, expiration)
|
5
|
+
@ppid = ppid.to_i
|
6
|
+
@expiration = expiration.to_f
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
@child_pid = Kernel.fork { wait_for_expiration }
|
12
|
-
yield
|
13
|
-
ensure
|
14
|
-
begin
|
15
|
-
stop
|
16
|
-
rescue Errno::ESRCH
|
17
|
-
end
|
8
|
+
abort "Invalid pid to monitor: #{@ppid}" if @ppid == 0
|
9
|
+
abort "Invalid expiration: #{@expiration}" if @expiration == 0.0
|
18
10
|
end
|
19
11
|
|
20
|
-
def
|
21
|
-
|
22
|
-
Process.kill("HUP", @child_pid)
|
12
|
+
def notify_parent_of_expiration
|
13
|
+
SafeTimeout.send_signal('TRAP', @ppid)
|
23
14
|
end
|
24
15
|
|
25
|
-
def
|
26
|
-
Signal.trap(
|
16
|
+
def wait_for_timeout
|
17
|
+
Signal.trap('HUP') { exit 0 }
|
18
|
+
|
19
|
+
sleep [@expiration - Time.now.to_f, 0.1].max
|
27
20
|
|
28
|
-
|
29
|
-
# stop, it becomes an orphan and is given to the init process (1)
|
30
|
-
# or worse yet it becomes a zombie with parent 0. In either case,
|
31
|
-
# stop interrupting!
|
32
|
-
while Process.ppid > 1
|
33
|
-
sleep 0.1
|
34
|
-
if Time.now.to_f > @expiration
|
35
|
-
Process.kill("TRAP", Process.ppid)
|
36
|
-
return
|
37
|
-
end
|
38
|
-
end
|
21
|
+
notify_parent_of_expiration
|
39
22
|
end
|
40
23
|
|
41
24
|
end
|
42
|
-
|
43
25
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SafeTimeout
|
2
|
+
class Spawner
|
3
|
+
|
4
|
+
def initialize(options={})
|
5
|
+
@expiration = Time.now.to_f + options.fetch(:timeout)
|
6
|
+
@on_timeout = options.fetch(:on_timeout)
|
7
|
+
end
|
8
|
+
|
9
|
+
def start(&block)
|
10
|
+
original = Signal.trap('TRAP', &@on_timeout) || 'DEFAULT'
|
11
|
+
spawn_interrupter
|
12
|
+
yield
|
13
|
+
ensure
|
14
|
+
Signal.trap('TRAP', original)
|
15
|
+
stop
|
16
|
+
end
|
17
|
+
|
18
|
+
def stop
|
19
|
+
# Tell that child to stop interrupting!
|
20
|
+
SafeTimeout.send_signal('HUP', @child_pid)
|
21
|
+
end
|
22
|
+
|
23
|
+
def spawn_interrupter
|
24
|
+
# Create a light-weight child process to notify this process if it is
|
25
|
+
# taking too long
|
26
|
+
bin = Gem.bin_path('safe_timeout', 'safe_timeout')
|
27
|
+
@child_pid = Process.spawn(bin, $$.to_s, @expiration.to_s)
|
28
|
+
Process.detach(@child_pid)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
data/lib/safe_timeout/version.rb
CHANGED
data/lib/safe_timeout.rb
CHANGED
@@ -1,15 +1,23 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
#
|
1
|
+
#
|
2
|
+
# Ruby's Timeout is broken and highly dangerous. To avoid this risk we instead
|
3
|
+
# use a child process to handle the timeout. We fork it and let it issue a SIGINT
|
4
|
+
# (Ctrl-C) to the parent if the timeout is reached.
|
5
|
+
#
|
4
6
|
module SafeTimeout
|
5
7
|
|
6
8
|
autoload :InterruptingChildProcess, 'safe_timeout/interrupting_child_process'
|
9
|
+
autoload :Spawner, 'safe_timeout/spawner'
|
7
10
|
|
8
11
|
def self.timeout(sec, klass=nil, &block)
|
9
|
-
|
12
|
+
Spawner.new(
|
10
13
|
:timeout => sec,
|
11
14
|
:on_timeout => lambda { |_| raise(klass || Timeout::Error) }
|
12
15
|
).start(&block)
|
13
16
|
end
|
14
17
|
|
18
|
+
def self.send_signal(signal, pid)
|
19
|
+
Process.kill(signal, pid) if pid
|
20
|
+
rescue Errno::ESRCH
|
21
|
+
end
|
22
|
+
|
15
23
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: safe_timeout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David McCullars
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-09-
|
11
|
+
date: 2015-09-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -56,7 +56,8 @@ description: A safer alternative to Ruby's Timeout that uses unix processes inst
|
|
56
56
|
of threads.
|
57
57
|
email:
|
58
58
|
- david.mccullars@gmail.com
|
59
|
-
executables:
|
59
|
+
executables:
|
60
|
+
- safe_timeout
|
60
61
|
extensions: []
|
61
62
|
extra_rdoc_files: []
|
62
63
|
files:
|
@@ -66,9 +67,11 @@ files:
|
|
66
67
|
- LICENSE.txt
|
67
68
|
- README.md
|
68
69
|
- Rakefile
|
70
|
+
- bin/safe_timeout
|
69
71
|
- circle.yml
|
70
72
|
- lib/safe_timeout.rb
|
71
73
|
- lib/safe_timeout/interrupting_child_process.rb
|
74
|
+
- lib/safe_timeout/spawner.rb
|
72
75
|
- lib/safe_timeout/version.rb
|
73
76
|
- safe_timeout.gemspec
|
74
77
|
- spec/lib/safe_timeout_spec.rb
|