timeout 0.1.0 → 0.3.0
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/.github/dependabot.yml +6 -0
- data/.github/workflows/test.yml +6 -8
- data/Rakefile +7 -0
- data/lib/timeout.rb +111 -50
- data/timeout.gemspec +11 -8
- metadata +8 -7
- data/lib/timeout/version.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d65cc31f39a22fdc316cc89f8f8e4adb95131700972fca8f65cb79ab5c844858
|
4
|
+
data.tar.gz: 9cb854b05e02484a179c83f56ddb3d85b885806148fd1350223d5406cc20c706
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d88ad9e1965efed376d164a1dd992f3aa423aab9bf3f6c0fdc482affc9895991ff542831ce6d8159771fa000d5e3e5c0679fedbfa73f94fe8c6548fe9f387714
|
7
|
+
data.tar.gz: '00529ed0c665d1f24c466461ab4c9298d79db2a00f9d12993e1cda402543006f3cf9e88c6f5a36286bef5b97fbaa4f0894cd4b1715b5691bb883bb7e48f37d4f'
|
data/.github/workflows/test.yml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
name:
|
1
|
+
name: test
|
2
2
|
|
3
3
|
on: [push, pull_request]
|
4
4
|
|
@@ -6,19 +6,17 @@ jobs:
|
|
6
6
|
build:
|
7
7
|
name: build (${{ matrix.ruby }} / ${{ matrix.os }})
|
8
8
|
strategy:
|
9
|
+
fail-fast: false
|
9
10
|
matrix:
|
10
|
-
ruby: [ 2.7, 2.6, 2.5, 2.4, head ]
|
11
|
+
ruby: [ '3.0', 2.7, 2.6, 2.5, 2.4, head, jruby, truffleruby-head ]
|
11
12
|
os: [ ubuntu-latest, macos-latest ]
|
12
13
|
runs-on: ${{ matrix.os }}
|
13
14
|
steps:
|
14
|
-
- uses: actions/checkout@
|
15
|
+
- uses: actions/checkout@v3
|
15
16
|
- name: Set up Ruby
|
16
17
|
uses: ruby/setup-ruby@v1
|
17
18
|
with:
|
18
19
|
ruby-version: ${{ matrix.ruby }}
|
19
|
-
|
20
|
-
run: |
|
21
|
-
gem install bundler --no-document
|
22
|
-
bundle install
|
20
|
+
bundler-cache: true # 'bundle install' and cache gems
|
23
21
|
- name: Run test
|
24
|
-
run: rake test
|
22
|
+
run: bundle exec rake test
|
data/Rakefile
CHANGED
@@ -7,4 +7,11 @@ Rake::TestTask.new(:test) do |t|
|
|
7
7
|
t.test_files = FileList["test/**/test_*.rb"]
|
8
8
|
end
|
9
9
|
|
10
|
+
task :sync_tool do
|
11
|
+
require 'fileutils'
|
12
|
+
FileUtils.cp "../ruby/tool/lib/core_assertions.rb", "./test/lib"
|
13
|
+
FileUtils.cp "../ruby/tool/lib/envutil.rb", "./test/lib"
|
14
|
+
FileUtils.cp "../ruby/tool/lib/find_executable.rb", "./test/lib"
|
15
|
+
end
|
16
|
+
|
10
17
|
task :default => :test
|
data/lib/timeout.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# frozen_string_literal:
|
1
|
+
# frozen_string_literal: true
|
2
2
|
# Timeout long-running blocks
|
3
3
|
#
|
4
4
|
# == Synopsis
|
@@ -23,6 +23,8 @@
|
|
23
23
|
# Copyright:: (C) 2000 Information-technology Promotion Agency, Japan
|
24
24
|
|
25
25
|
module Timeout
|
26
|
+
VERSION = "0.3.0"
|
27
|
+
|
26
28
|
# Raised by Timeout.timeout when the block times out.
|
27
29
|
class Error < RuntimeError
|
28
30
|
attr_reader :thread
|
@@ -30,6 +32,7 @@ module Timeout
|
|
30
32
|
def self.catch(*args)
|
31
33
|
exc = new(*args)
|
32
34
|
exc.instance_variable_set(:@thread, Thread.current)
|
35
|
+
exc.instance_variable_set(:@catch_value, exc)
|
33
36
|
::Kernel.catch(exc) {yield exc}
|
34
37
|
end
|
35
38
|
|
@@ -38,18 +41,97 @@ module Timeout
|
|
38
41
|
if self.thread == Thread.current
|
39
42
|
bt = caller
|
40
43
|
begin
|
41
|
-
throw(
|
44
|
+
throw(@catch_value, bt)
|
42
45
|
rescue UncaughtThrowError
|
43
46
|
end
|
44
47
|
end
|
45
|
-
|
48
|
+
super
|
46
49
|
end
|
47
50
|
end
|
48
51
|
|
49
52
|
# :stopdoc:
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
+
CONDVAR = ConditionVariable.new
|
54
|
+
QUEUE = Queue.new
|
55
|
+
QUEUE_MUTEX = Mutex.new
|
56
|
+
TIMEOUT_THREAD_MUTEX = Mutex.new
|
57
|
+
@timeout_thread = nil
|
58
|
+
private_constant :CONDVAR, :QUEUE, :QUEUE_MUTEX, :TIMEOUT_THREAD_MUTEX
|
59
|
+
|
60
|
+
class Request
|
61
|
+
attr_reader :deadline
|
62
|
+
|
63
|
+
def initialize(thread, timeout, exception_class, message)
|
64
|
+
@thread = thread
|
65
|
+
@deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
|
66
|
+
@exception_class = exception_class
|
67
|
+
@message = message
|
68
|
+
|
69
|
+
@mutex = Mutex.new
|
70
|
+
@done = false # protected by @mutex
|
71
|
+
end
|
72
|
+
|
73
|
+
def done?
|
74
|
+
@mutex.synchronize do
|
75
|
+
@done
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def expired?(now)
|
80
|
+
now >= @deadline
|
81
|
+
end
|
82
|
+
|
83
|
+
def interrupt
|
84
|
+
@mutex.synchronize do
|
85
|
+
unless @done
|
86
|
+
@thread.raise @exception_class, @message
|
87
|
+
@done = true
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def finished
|
93
|
+
@mutex.synchronize do
|
94
|
+
@done = true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
private_constant :Request
|
99
|
+
|
100
|
+
def self.create_timeout_thread
|
101
|
+
Thread.new do
|
102
|
+
requests = []
|
103
|
+
while true
|
104
|
+
until QUEUE.empty? and !requests.empty? # wait to have at least one request
|
105
|
+
req = QUEUE.pop
|
106
|
+
requests << req unless req.done?
|
107
|
+
end
|
108
|
+
closest_deadline = requests.min_by(&:deadline).deadline
|
109
|
+
|
110
|
+
now = 0.0
|
111
|
+
QUEUE_MUTEX.synchronize do
|
112
|
+
while (now = Process.clock_gettime(Process::CLOCK_MONOTONIC)) < closest_deadline and QUEUE.empty?
|
113
|
+
CONDVAR.wait(QUEUE_MUTEX, closest_deadline - now)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
requests.each do |req|
|
118
|
+
req.interrupt if req.expired?(now)
|
119
|
+
end
|
120
|
+
requests.reject!(&:done?)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
private_class_method :create_timeout_thread
|
125
|
+
|
126
|
+
def self.ensure_timeout_thread_created
|
127
|
+
unless @timeout_thread and @timeout_thread.alive?
|
128
|
+
TIMEOUT_THREAD_MUTEX.synchronize do
|
129
|
+
unless @timeout_thread and @timeout_thread.alive?
|
130
|
+
@timeout_thread = create_timeout_thread
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
53
135
|
# :startdoc:
|
54
136
|
|
55
137
|
# Perform an operation in a block, raising an error if it takes longer than
|
@@ -71,62 +153,41 @@ module Timeout
|
|
71
153
|
# ensure to prevent the handling of the exception. For that reason, this
|
72
154
|
# method cannot be relied on to enforce timeouts for untrusted blocks.
|
73
155
|
#
|
156
|
+
# If a scheduler is defined, it will be used to handle the timeout by invoking
|
157
|
+
# Scheduler#timeout_after.
|
158
|
+
#
|
74
159
|
# Note that this is both a method of module Timeout, so you can <tt>include
|
75
160
|
# Timeout</tt> into your classes so they have a #timeout method, as well as
|
76
161
|
# a module method, so you can call it directly as Timeout.timeout().
|
77
|
-
def timeout(sec, klass = nil, message = nil) #:yield: +sec+
|
162
|
+
def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+
|
78
163
|
return yield(sec) if sec == nil or sec.zero?
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
164
|
+
|
165
|
+
message ||= "execution expired"
|
166
|
+
|
167
|
+
if Fiber.respond_to?(:current_scheduler) && (scheduler = Fiber.current_scheduler)&.respond_to?(:timeout_after)
|
168
|
+
return scheduler.timeout_after(sec, klass || Error, message, &block)
|
169
|
+
end
|
170
|
+
|
171
|
+
Timeout.ensure_timeout_thread_created
|
172
|
+
perform = Proc.new do |exc|
|
173
|
+
request = Request.new(Thread.current, sec, exc, message)
|
174
|
+
QUEUE_MUTEX.synchronize do
|
175
|
+
QUEUE << request
|
176
|
+
CONDVAR.signal
|
177
|
+
end
|
83
178
|
begin
|
84
|
-
x = Thread.current
|
85
|
-
y = Thread.start {
|
86
|
-
Thread.current.name = from
|
87
|
-
begin
|
88
|
-
sleep sec
|
89
|
-
rescue => e
|
90
|
-
x.raise e
|
91
|
-
else
|
92
|
-
x.raise exception, message
|
93
|
-
end
|
94
|
-
}
|
95
179
|
return yield(sec)
|
96
180
|
ensure
|
97
|
-
|
98
|
-
y.kill
|
99
|
-
y.join # make sure y is dead.
|
100
|
-
end
|
181
|
+
request.finished
|
101
182
|
end
|
102
183
|
end
|
184
|
+
|
103
185
|
if klass
|
104
|
-
|
105
|
-
bl.call(klass)
|
106
|
-
rescue klass => e
|
107
|
-
bt = e.backtrace
|
108
|
-
end
|
186
|
+
perform.call(klass)
|
109
187
|
else
|
110
|
-
|
188
|
+
backtrace = Error.catch(&perform)
|
189
|
+
raise Error, message, backtrace
|
111
190
|
end
|
112
|
-
level = -caller(CALLER_OFFSET).size-2
|
113
|
-
while THIS_FILE =~ bt[level]
|
114
|
-
bt.delete_at(level)
|
115
|
-
end
|
116
|
-
raise(e, message, bt)
|
117
191
|
end
|
118
|
-
|
119
192
|
module_function :timeout
|
120
193
|
end
|
121
|
-
|
122
|
-
def timeout(*args, &block)
|
123
|
-
warn "Object##{__method__} is deprecated, use Timeout.timeout instead.", uplevel: 1
|
124
|
-
Timeout.timeout(*args, &block)
|
125
|
-
end
|
126
|
-
|
127
|
-
# Another name for Timeout::Error, defined for backwards compatibility with
|
128
|
-
# earlier versions of timeout.rb.
|
129
|
-
TimeoutError = Timeout::Error
|
130
|
-
class Object
|
131
|
-
deprecate_constant :TimeoutError
|
132
|
-
end
|
data/timeout.gemspec
CHANGED
@@ -1,25 +1,28 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
name = File.basename(__FILE__, ".gemspec")
|
4
|
+
version = ["lib", Array.new(name.count("-")+1, "..").join("/")].find do |dir|
|
5
|
+
break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
|
6
|
+
/^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
|
7
|
+
end rescue nil
|
5
8
|
end
|
6
9
|
|
7
10
|
Gem::Specification.new do |spec|
|
8
|
-
spec.name =
|
9
|
-
spec.version =
|
11
|
+
spec.name = name
|
12
|
+
spec.version = version
|
10
13
|
spec.authors = ["Yukihiro Matsumoto"]
|
11
14
|
spec.email = ["matz@ruby-lang.org"]
|
12
15
|
|
13
16
|
spec.summary = %q{Auto-terminate potentially long-running operations in Ruby.}
|
14
17
|
spec.description = %q{Auto-terminate potentially long-running operations in Ruby.}
|
15
18
|
spec.homepage = "https://github.com/ruby/timeout"
|
16
|
-
spec.
|
19
|
+
spec.licenses = ["Ruby", "BSD-2-Clause"]
|
17
20
|
|
18
21
|
spec.metadata["homepage_uri"] = spec.homepage
|
19
22
|
spec.metadata["source_code_uri"] = spec.homepage
|
20
23
|
|
21
24
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
22
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
+
`git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
23
26
|
end
|
24
27
|
spec.bindir = "exe"
|
25
28
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timeout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yukihiro Matsumoto
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Auto-terminate potentially long-running operations in Ruby.
|
14
14
|
email:
|
@@ -17,6 +17,7 @@ executables: []
|
|
17
17
|
extensions: []
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
|
+
- ".github/dependabot.yml"
|
20
21
|
- ".github/workflows/test.yml"
|
21
22
|
- ".gitignore"
|
22
23
|
- Gemfile
|
@@ -26,15 +27,15 @@ files:
|
|
26
27
|
- bin/console
|
27
28
|
- bin/setup
|
28
29
|
- lib/timeout.rb
|
29
|
-
- lib/timeout/version.rb
|
30
30
|
- timeout.gemspec
|
31
31
|
homepage: https://github.com/ruby/timeout
|
32
32
|
licenses:
|
33
|
+
- Ruby
|
33
34
|
- BSD-2-Clause
|
34
35
|
metadata:
|
35
36
|
homepage_uri: https://github.com/ruby/timeout
|
36
37
|
source_code_uri: https://github.com/ruby/timeout
|
37
|
-
post_install_message:
|
38
|
+
post_install_message:
|
38
39
|
rdoc_options: []
|
39
40
|
require_paths:
|
40
41
|
- lib
|
@@ -49,8 +50,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
49
50
|
- !ruby/object:Gem::Version
|
50
51
|
version: '0'
|
51
52
|
requirements: []
|
52
|
-
rubygems_version: 3.
|
53
|
-
signing_key:
|
53
|
+
rubygems_version: 3.3.7
|
54
|
+
signing_key:
|
54
55
|
specification_version: 4
|
55
56
|
summary: Auto-terminate potentially long-running operations in Ruby.
|
56
57
|
test_files: []
|
data/lib/timeout/version.rb
DELETED