sane_timeout 0.1

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.
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: