-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathREADME
177 lines (123 loc) · 4.81 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
NAME
Terminator
SYNOPSIS
An external timeout mechanism based on processes and signals. Safe for
system calls. Safe for minors. but not very safe for misbehaving,
downtrodden zombied out processes.
DESCRIPTION
Terminator is a solution to the problem of 'how am I meant to kill a
system call in Ruby!?'
Ruby (at least MRI) uses green threads to "multitask". This means that
there is really only ever one ruby process running which then splits up
it's processor time between all of it's threads internally.
The processor then only has to deal with one ruby process and the ruby
process deals with all it's threads. There are pros and cons to this
method, but that is not the point of this library.
The point is, that if you make a system call to an external resource from
ruby, then the kernel will go and make that call for ruby and NOT COME BACK
to ruby until that system call completes or fails. This can take a very
long time and is why your feeble attempts at using ruby's internal "Timeout"
command has failed miserably at timing out your external web service, database
or network connections.
You see, Ruby just doesn't get a chance to do anything as the kernel goes
"I'm not going to talk to you again until your system calls complete". Sort
of a no win situation for Ruby.
That's where Terminator comes in. Like Arnie, he will come back. No matter
what, and complete his mission, unless he gets aborted before his timeout,
you can trust Terminator to thoroughly and without remorse, nuke your
misbehaving and timing out ruby processes efficiently, and quickly.
HOW IT WORKS
Basically we create a new terminator ruby process, separate to the existing
running ruby process that has a simple command of sleep for x seconds, and then
do a process TERM on the PID of the original ruby process that created it.
If your process finishes before the timeout, it will kill the Terminator first.
So really it is a race of who is going to win?
Word of warning though. Terminator is not subtle. Don't expect it to split
hairs. Trying to give a process that takes about 1 second to complete, a
2 second terminator... well... odds are 50/50 on who is going to make it.
If you have a 1 second process, give it 3 seconds to complete. Arnie doesn't
much care for casualties of war.
Another word of warning, if using Terminator inside a loop, it is possible
to exceed your open file limit. I have safely tested looping 1000 times
INSTALL
gem install terminator
URIS
http://codeforpeople.com/lib/ruby
http://rubyforge.org/projects/codeforpeople
HISTORY
0.4.2
* initial version (ara)
0.4.3
* added some extra specs and test cases (mikel)
0.4.4
* made terminator loop safe. 1000.times { Terminator.timeout(1) do true; end }
now works (mikel)
* added more test cases (mikel)
0.4.5
* upgraded to Ruby 1.9.3 (tested on p125), changed lambda calls to use proc (seangeo, wiseleyb)
* upgraded gemspec to work with bundler (wiseleyb)
* updated example code, removed float time calls, updated lambda to use proc (wiseleyb)
* updated specs (seangeo, wiseleyb)
AUTHORS
ara.t.howard - [email protected]
mikel lindsaar - [email protected]
ben wiseleyb - [email protected]
sean geoghegan - [email protected]
SAMPLES
<========< samples/a.rb >========>
~ > cat samples/a.rb
require 'terminator'
Terminator.terminate 2 do
sleep 4
end
~ > ruby samples/a.rb
samples/a.rb:3: 2s (Terminator::Error)
from samples/a.rb:3
<========< samples/b.rb >========>
~ > cat samples/b.rb
require 'terminator'
Terminator.terminate 0.2 do
sleep 0.4 rescue puts 'timed out!'
end
~ > ruby samples/b.rb
timed out!
<========< samples/c.rb >========>
~ > cat samples/c.rb
require 'terminator'
begin
Terminator.terminate :seconds => 0.2 do
sleep 0.4
end
rescue Terminator.error
puts 'timed out!'
end
~ > ruby samples/c.rb
timed out!
<========< samples/d.rb >========>
~ > cat samples/d.rb
require 'terminator'
trap = lambda{ puts "signaled @ #{ Time.now.to_i }" }
Terminator.terminate :seconds => 1, :trap => trap do
sleep 2
puts "woke up @ #{ Time.now.to_i }"
end
~ > ruby samples/d.rb
signaled @ 1221026177
woke up @ 1221026178
<========< samples/e.rb >========>
~ > cat samples/e.rb
require 'terminator'
puts "Looping 1000 times on the terminator..."
success = false
1.upto(1000) do |i|
success = false
Terminator.terminate(1) do
success = true
end
print "\b\b\b#{i}"
end
puts "\nI was successful" if success
~ > ruby samples/e.rb
Looping 1000 times on the terminator...
1000
I was successful