sane_timeout 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 John Joseph Bachir
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,110 @@
1
+ # Timeout long-running blocks
2
+ #
3
+ # == Synopsis
4
+ #
5
+ # require 'timeout'
6
+ # status = Timeout::timeout(5) {
7
+ # # Something that should be interrupted if it takes more than 5 seconds...
8
+ # }
9
+ #
10
+ # == Description
11
+ #
12
+ # Timeout provides a way to auto-terminate a potentially long-running
13
+ # operation if it hasn't finished in a fixed amount of time.
14
+ #
15
+ # Previous versions didn't use a module for namespacing, however
16
+ # #timeout is provided for backwards compatibility. You
17
+ # should prefer Timeout#timeout instead.
18
+ #
19
+ # == Copyright
20
+ #
21
+ # Copyright:: (C) 2000 Network Applied Communication Laboratory, Inc.
22
+ # Copyright:: (C) 2000 Information-technology Promotion Agency, Japan
23
+
24
+ module Timeout
25
+ # Raised by Timeout#timeout when the block times out.
26
+ class Error < RuntimeError
27
+ end
28
+ class ExitException < ::Exception # :nodoc:
29
+ end
30
+
31
+ # :stopdoc:
32
+ THIS_FILE = /\A#{Regexp.quote(__FILE__)}:/o
33
+ CALLER_OFFSET = ((c = caller[0]) && THIS_FILE =~ c) ? 1 : 0
34
+ # :startdoc:
35
+
36
+ # Perform an operation in a block, timing it out if it takes longer
37
+ # than +sec+ seconds to complete.
38
+ #
39
+ # +sec+:: Number of seconds to wait for the block to terminate. Any number
40
+ # may be used, including Floats to specify fractional seconds.
41
+ # +klass+:: Exception Class to raise if the block fails to terminate
42
+ # in +sec+ seconds. Omitting will use the default, Timeout::Error
43
+ #
44
+ # The block will be executed on another thread and will be given one
45
+ # argument: +sec+.
46
+ #
47
+ # Returns the result of the block *if* the block completed before
48
+ # +sec+ seconds, otherwise throws an exception, based on the value of +klass+.
49
+ #
50
+ # Note that this is both a method of module Timeout, so you can <tt>include
51
+ # Timeout</tt> into your classes so they have a #timeout method, as well as
52
+ # a module method, so you can call it directly as Timeout.timeout().
53
+ def timeout(sec, klass = nil) #:yield: +sec+
54
+ return yield(sec) if sec == nil or sec.zero?
55
+ exception = klass || Class.new(ExitException)
56
+ begin
57
+ begin
58
+ current_thread = Thread.current
59
+
60
+ x = Thread.start{ yield(sec) }
61
+
62
+ y = Thread.start {
63
+ begin
64
+ sleep sec
65
+ rescue => e
66
+ x.raise e
67
+ else
68
+ x.kill
69
+ # x.join
70
+ current_thread.raise exception, "execution expired"
71
+ end
72
+ }
73
+
74
+ x.value
75
+ ensure
76
+ if y
77
+ y.kill
78
+ y.join # make sure y is dead.
79
+ end
80
+ end
81
+ rescue exception => e
82
+ rej = /\A#{Regexp.quote(__FILE__)}:#{__LINE__-4}\z/o
83
+ (bt = e.backtrace).reject! {|m| rej =~ m}
84
+ level = -caller(CALLER_OFFSET).size
85
+ while THIS_FILE =~ bt[level]
86
+ bt.delete_at(level)
87
+ level += 1
88
+ end
89
+ raise if klass # if exception class is specified, it
90
+ # would be expected outside.
91
+ raise Error, e.message, e.backtrace
92
+ end
93
+ end
94
+
95
+ module_function :timeout
96
+ end
97
+
98
+ # Identical to:
99
+ #
100
+ # Timeout::timeout(n, e, &block).
101
+ #
102
+ # This method is deprecated and provided only for backwards compatibility.
103
+ # You should use Timeout#timeout instead.
104
+ def timeout(n, e = nil, &block)
105
+ Timeout::timeout(n, e, &block)
106
+ end
107
+
108
+ # Another name for Timeout::Error, defined for backwards compatibility with
109
+ # earlier versions of timeout.rb.
110
+ TimeoutError = Timeout::Error
data/readme.md ADDED
@@ -0,0 +1,46 @@
1
+ # SaneTimeout
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'sane_timeout'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install sane_timeout
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+
24
+
25
+ ## Open Questions
26
+ * on line 69, I had tried doing x.join based on the standard lib doing the
27
+ same thing with the y thread. I assumed that kill is not synchromous so it's
28
+ necessary to wait for x to die. However, doing this causes many things to break.
29
+ I haven't been able to figure out why.
30
+ * related, is there any reason to kill y before returning x.value?
31
+
32
+ ## To Do
33
+ * determine best way to install sane_timeout so that it
34
+ replaces Timeout. possibly offer to modes, one where
35
+ Timeout is overwritten, another where sane_timeout is
36
+ invoked in its own namespace (SaneTimeout)
37
+
38
+ ## Project Improvement
39
+ * better name?
40
+ * There is redundant code between test_timeout.rb and test_sane_timeout.rb
41
+ because I wanted to annotate in the former and be DRY in the latter. Maybe this
42
+ can somehow be improved.
43
+
44
+ ## odd design choice in standard lib:
45
+ * same type of error is raised inside thread and outside when specified
46
+ * when not specified, Exception is raised inside, StandardError is raised outside
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "sane_timeout"
7
+ gem.version = '0.1'
8
+ gem.authors = ["John Joseph Bachir"]
9
+ gem.email = ["john@ganxy.com"]
10
+ gem.description = %q{Sane Timeout fixes Ruby standard lib Timeout}
11
+ gem.summary = %q{Sane Timeout fixes Ruby standard lib Timeout}
12
+ gem.homepage = "https://github.com/jjb/sane_timeout"
13
+
14
+ gem.files = `git ls-files`.split($/)
15
+ # gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.require_paths = ["lib"]
18
+ end
@@ -0,0 +1,136 @@
1
+ require 'test/unit'
2
+ require '../sane-timeout'
3
+ require 'thread'
4
+
5
+ class TestTimeout < Test::Unit::TestCase
6
+
7
+ ### Tests that come with standard lib
8
+ def test_queue
9
+ q = Queue.new
10
+ assert_raise(Timeout::Error, "[ruby-dev:32935]") {
11
+ timeout(0.1) { q.pop }
12
+ }
13
+ end
14
+
15
+ def test_timeout
16
+ @flag = true
17
+ Thread.start {
18
+ sleep 0.1
19
+ @flag = false
20
+ }
21
+ assert_nothing_raised("[ruby-dev:38319]") do
22
+ Timeout.timeout(1) {
23
+ nil while @flag
24
+ }
25
+ end
26
+ assert !@flag, "[ruby-dev:38319]"
27
+ end
28
+
29
+ def test_cannot_convert_into_time_interval
30
+ bug3168 = '[ruby-dev:41010]'
31
+ def (n = Object.new).zero?; false; end
32
+ assert_raise(TypeError, bug3168) {Timeout.timeout(n) { sleep 0.1 }}
33
+ end
34
+
35
+
36
+
37
+ ### tests not included in standard lib, but which standard lib does pass
38
+
39
+ def test_non_timing_out_code_is_successful
40
+ assert_nothing_raised do
41
+ Timeout.timeout(2) {
42
+ true
43
+ }
44
+ end
45
+ end
46
+
47
+ def test_code_that_takes_too_long_is_stopped_and_raises
48
+ assert_raise(Timeout::Error) do
49
+ Timeout.timeout(0.1) {
50
+ sleep 10
51
+ }
52
+ end
53
+ end
54
+
55
+ def test_returns_block_value_when_not_timing_out
56
+ retval = Timeout.timeout(1){ "foobar" }
57
+ assert_equal "foobar", retval
58
+ end
59
+
60
+
61
+ ### Tests that sane_timeout passes
62
+
63
+ def subject(throws, catches)
64
+ $inner_attempted=nil
65
+ $inner_succeeded=nil
66
+ $caught_in_inner=nil
67
+
68
+ $raised_in_outer=nil
69
+ $not_raised_in_outer=nil
70
+ begin
71
+ Timeout.timeout(0.1, throws){
72
+ begin
73
+ $inner_attempted=true
74
+ sleep 10
75
+ rescue catches
76
+ $caught_in_inner=true
77
+ else
78
+ $inner_succeeded=true
79
+ end
80
+ }
81
+ rescue Exception
82
+ $raised_in_outer = true
83
+ else
84
+ $not_raised_in_outer = true
85
+ end
86
+ end
87
+
88
+ def expectations
89
+ assert $inner_attempted, "Inner was not attempted"
90
+ assert !$inner_succeeded, "Inner did not succeed"
91
+ assert !$caught_in_inner, "Exception was caught in inner"
92
+ assert $raised_in_outer, "Exception was not raised in outer"
93
+ assert !$not_raised_in_outer, "Exception was not raised in outer(2)"
94
+ end
95
+
96
+ puts "when an exception to raise is not specified and the inner code does not catch Exception"
97
+ def test_1
98
+ subject(nil, StandardError)
99
+ expectations
100
+ end
101
+
102
+ puts "when an exception to raise is not specified and the inner code does catch Exception"
103
+ def test_2
104
+ subject(nil, Exception)
105
+ expectations
106
+ end
107
+
108
+ puts "when an exception to raise is StandardError and the inner code does not catch Exception"
109
+ class MyError < StandardError; end
110
+ def test_3
111
+ subject(MyError, StandardError)
112
+ expectations
113
+ end
114
+
115
+ puts "when an exception to raise is StandardError and the inner code does catch Exception"
116
+ class MyError2 < StandardError; end
117
+ def test_4
118
+ subject(MyError2, Exception)
119
+ expectations
120
+ end
121
+
122
+ puts "when an exception to raise is Exception and the inner code does not catch Exception"
123
+ class MyError3 < Exception; end
124
+ def test_5
125
+ subject(MyError3, StandardError)
126
+ expectations
127
+ end
128
+
129
+ puts "when an exception to raise is Exception and the inner code does catch Exception"
130
+ class MyError4 < Exception; end
131
+ def test_6
132
+ subject(MyError4, Exception)
133
+ expectations
134
+ end
135
+
136
+ end
@@ -0,0 +1,177 @@
1
+ require 'test/unit'
2
+ require 'timeout'
3
+ require 'thread'
4
+
5
+ class TestTimeout < Test::Unit::TestCase
6
+
7
+ ### Tests that come with standard lib
8
+
9
+ def test_queue
10
+ q = Queue.new
11
+ assert_raise(Timeout::Error, "[ruby-dev:32935]") {
12
+ timeout(0.1) { q.pop }
13
+ }
14
+ end
15
+
16
+ def test_timeout
17
+ @flag = true
18
+ Thread.start {
19
+ sleep 0.1
20
+ @flag = false
21
+ }
22
+ assert_nothing_raised("[ruby-dev:38319]") do
23
+ Timeout.timeout(1) {
24
+ nil while @flag
25
+ }
26
+ end
27
+ assert !@flag, "[ruby-dev:38319]"
28
+ end
29
+
30
+ def test_cannot_convert_into_time_interval
31
+ bug3168 = '[ruby-dev:41010]'
32
+ def (n = Object.new).zero?; false; end
33
+ assert_raise(TypeError, bug3168) {Timeout.timeout(n) { sleep 0.1 }}
34
+ end
35
+
36
+
37
+ ### tests not included in standard lib, but which standard lib does pass
38
+
39
+ def test_non_timing_out_code_is_successful
40
+ assert_nothing_raised do
41
+ Timeout.timeout(2) {
42
+ true
43
+ }
44
+ end
45
+ end
46
+
47
+ def test_code_that_takes_too_long_is_stopped_and_raises
48
+ assert_raise(Timeout::Error) do
49
+ Timeout.timeout(0.1) {
50
+ sleep 10
51
+ }
52
+ end
53
+ end
54
+
55
+ def test_returns_block_value_when_not_timing_out
56
+ retval = Timeout.timeout(1){ "foobar" }
57
+ assert_equal "foobar", retval
58
+ end
59
+
60
+
61
+ ### Tests demonstrating problems with standard lib
62
+ # NOTE: this demonstration was done such that all of the assertions pass,
63
+ # The ones marked weird, bad, and very bad should not, and their
64
+ # passing is demonstrating the brokenness.
65
+
66
+ def subject(throws, catches)
67
+ $inner_attempted=nil
68
+ $inner_succeeded=nil
69
+ $caught_in_inner=nil
70
+
71
+ $raised_in_outer=nil
72
+ $not_raised_in_outer=nil
73
+ begin
74
+ Timeout.timeout(0.1, throws){
75
+ begin
76
+ $inner_attempted=true
77
+ sleep 10
78
+ rescue catches
79
+ $caught_in_inner=true
80
+ else
81
+ $inner_succeeded=true
82
+ end
83
+ }
84
+ rescue Exception
85
+ $raised_in_outer = true
86
+ else
87
+ $not_raised_in_outer = true
88
+ end
89
+ end
90
+
91
+ puts "when an exception to raise is not specified and the inner code does not catch Exception"
92
+ def test_1
93
+ subject(nil, StandardError)
94
+
95
+ # EXPECTED
96
+ assert $inner_attempted
97
+ assert !$inner_succeeded
98
+ assert !$caught_in_inner
99
+ assert $raised_in_outer && !$not_raised_in_outer
100
+ end
101
+
102
+ puts "when an exception to raise is not specified and the inner code does catch Exception"
103
+ def test_2
104
+ subject(nil, Exception)
105
+
106
+ # EXPECTED
107
+ assert $inner_attempted
108
+ assert !$inner_succeeded
109
+
110
+ # WEIRD
111
+ assert $caught_in_inner
112
+
113
+ # BAD
114
+ assert !$raised_in_outer && $not_raised_in_outer
115
+ end
116
+
117
+ puts "when an exception to raise is StandardError and the inner code does not catch Exception"
118
+ class MyError < StandardError; end
119
+ def test_3
120
+ subject(MyError, StandardError)
121
+
122
+ # EXPECTED
123
+ assert $inner_attempted
124
+ assert !$inner_succeeded
125
+
126
+ # WEIRD
127
+ assert $caught_in_inner
128
+
129
+ # BAD
130
+ assert !$raised_in_outer && $not_raised_in_outer
131
+ end
132
+
133
+ puts "when an exception to raise is StandardError and the inner code does catch Exception"
134
+ class MyError2 < StandardError; end
135
+ def test_4
136
+ subject(MyError2, Exception)
137
+
138
+ # EXPECTED
139
+ assert $inner_attempted
140
+ assert !$inner_succeeded
141
+
142
+ # WEIRD
143
+ assert $caught_in_inner
144
+
145
+ # BAD
146
+ assert !$raised_in_outer && $not_raised_in_outer
147
+ end
148
+
149
+ puts "when an exception to raise is Exception and the inner code does not catch Exception"
150
+ class MyError3 < Exception; end
151
+ def test_5
152
+ subject(MyError3, StandardError)
153
+
154
+ # EXPECTED
155
+ assert $inner_attempted
156
+ assert !$inner_succeeded
157
+ assert !$caught_in_inner
158
+ assert $raised_in_outer && !$not_raised_in_outer
159
+ end
160
+
161
+ puts "when an exception to raise is Exception and the inner code does catch Exception"
162
+ class MyError4 < Exception; end
163
+ def test_6
164
+ subject(MyError4, Exception)
165
+
166
+ # EXPECTED
167
+ assert $inner_attempted
168
+ assert !$inner_succeeded
169
+
170
+ # WEIRD
171
+ assert $caught_in_inner
172
+
173
+ # VERY BAD
174
+ assert !$raised_in_outer && $not_raised_in_outer
175
+ end
176
+
177
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sane_timeout
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - John Joseph Bachir
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-15 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Sane Timeout fixes Ruby standard lib Timeout
15
+ email:
16
+ - john@ganxy.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - LICENSE.txt
22
+ - Rakefile
23
+ - lib/sane_timeout.rb
24
+ - readme.md
25
+ - sane_timeout.gemspec
26
+ - test/test_sane_timeout.rb
27
+ - test/test_timeout.rb
28
+ homepage: https://github.com/jjb/sane_timeout
29
+ licenses: []
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubyforge_project:
48
+ rubygems_version: 1.8.23
49
+ signing_key:
50
+ specification_version: 3
51
+ summary: Sane Timeout fixes Ruby standard lib Timeout
52
+ test_files:
53
+ - test/test_sane_timeout.rb
54
+ - test/test_timeout.rb
55
+ has_rdoc: