timeout 0.2.0 → 0.3.2
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/lib/timeout.rb +107 -37
- data/timeout.gemspec +4 -4
- metadata +4 -9
- data/.github/workflows/test.yml +0 -22
- data/.gitignore +0 -9
- data/Rakefile +0 -17
- data/bin/console +0 -7
- data/bin/setup +0 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 201c519af67a105d6bd6e83e6f983cb7ab41c2968be1433ab9421498a1a1cfc3
|
|
4
|
+
data.tar.gz: 0d9b5e39dda0333010013508b000eb95c584f5959932665b5fa7c3cb23d64112
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 754ad7872462dcc555b91c1c589662e3e11f0bdbd2057b336e813e20743bb24e9ac74604c1dafea0159690f9cd480b57039fa878b5d523fb3662499560f99f2a
|
|
7
|
+
data.tar.gz: 17a3815d1a1fd685e2b24a3b41717292b9e6c5e370156d6083788fb4594179a29ec9933df106b955a1bd1cfb479097aac462b161d429b55c4cea240927f62583
|
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,7 +23,7 @@
|
|
|
23
23
|
# Copyright:: (C) 2000 Information-technology Promotion Agency, Japan
|
|
24
24
|
|
|
25
25
|
module Timeout
|
|
26
|
-
VERSION = "0.2
|
|
26
|
+
VERSION = "0.3.2"
|
|
27
27
|
|
|
28
28
|
# Raised by Timeout.timeout when the block times out.
|
|
29
29
|
class Error < RuntimeError
|
|
@@ -50,9 +50,98 @@ module Timeout
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
# :stopdoc:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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 = GET_TIME.call(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
|
+
watcher = 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 = GET_TIME.call(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
|
+
ThreadGroup::Default.add(watcher) unless watcher.group.enclosed?
|
|
124
|
+
watcher.name = "Timeout stdlib thread"
|
|
125
|
+
watcher.thread_variable_set(:"\0__detached_thread__", true)
|
|
126
|
+
watcher
|
|
127
|
+
end
|
|
128
|
+
private_class_method :create_timeout_thread
|
|
129
|
+
|
|
130
|
+
def self.ensure_timeout_thread_created
|
|
131
|
+
unless @timeout_thread and @timeout_thread.alive?
|
|
132
|
+
TIMEOUT_THREAD_MUTEX.synchronize do
|
|
133
|
+
unless @timeout_thread and @timeout_thread.alive?
|
|
134
|
+
@timeout_thread = create_timeout_thread
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# We keep a private reference so that time mocking libraries won't break
|
|
141
|
+
# Timeout.
|
|
142
|
+
GET_TIME = Process.method(:clock_gettime)
|
|
143
|
+
private_constant :GET_TIME
|
|
144
|
+
|
|
56
145
|
# :startdoc:
|
|
57
146
|
|
|
58
147
|
# Perform an operation in a block, raising an error if it takes longer than
|
|
@@ -83,51 +172,32 @@ module Timeout
|
|
|
83
172
|
def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+
|
|
84
173
|
return yield(sec) if sec == nil or sec.zero?
|
|
85
174
|
|
|
86
|
-
message ||= "execution expired"
|
|
175
|
+
message ||= "execution expired"
|
|
87
176
|
|
|
88
177
|
if Fiber.respond_to?(:current_scheduler) && (scheduler = Fiber.current_scheduler)&.respond_to?(:timeout_after)
|
|
89
178
|
return scheduler.timeout_after(sec, klass || Error, message, &block)
|
|
90
179
|
end
|
|
91
180
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
181
|
+
Timeout.ensure_timeout_thread_created
|
|
182
|
+
perform = Proc.new do |exc|
|
|
183
|
+
request = Request.new(Thread.current, sec, exc, message)
|
|
184
|
+
QUEUE_MUTEX.synchronize do
|
|
185
|
+
QUEUE << request
|
|
186
|
+
CONDVAR.signal
|
|
187
|
+
end
|
|
95
188
|
begin
|
|
96
|
-
x = Thread.current
|
|
97
|
-
y = Thread.start {
|
|
98
|
-
Thread.current.name = from
|
|
99
|
-
begin
|
|
100
|
-
sleep sec
|
|
101
|
-
rescue => e
|
|
102
|
-
x.raise e
|
|
103
|
-
else
|
|
104
|
-
x.raise exception, message
|
|
105
|
-
end
|
|
106
|
-
}
|
|
107
189
|
return yield(sec)
|
|
108
190
|
ensure
|
|
109
|
-
|
|
110
|
-
y.kill
|
|
111
|
-
y.join # make sure y is dead.
|
|
112
|
-
end
|
|
191
|
+
request.finished
|
|
113
192
|
end
|
|
114
193
|
end
|
|
194
|
+
|
|
115
195
|
if klass
|
|
116
|
-
|
|
117
|
-
bl.call(klass)
|
|
118
|
-
rescue klass => e
|
|
119
|
-
message = e.message
|
|
120
|
-
bt = e.backtrace
|
|
121
|
-
end
|
|
196
|
+
perform.call(klass)
|
|
122
197
|
else
|
|
123
|
-
|
|
198
|
+
backtrace = Error.catch(&perform)
|
|
199
|
+
raise Error, message, backtrace
|
|
124
200
|
end
|
|
125
|
-
level = -caller(CALLER_OFFSET).size-2
|
|
126
|
-
while THIS_FILE =~ bt[level]
|
|
127
|
-
bt.delete_at(level)
|
|
128
|
-
end
|
|
129
|
-
raise(e, message, bt)
|
|
130
201
|
end
|
|
131
|
-
|
|
132
202
|
module_function :timeout
|
|
133
203
|
end
|
data/timeout.gemspec
CHANGED
|
@@ -21,10 +21,10 @@ Gem::Specification.new do |spec|
|
|
|
21
21
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
22
22
|
spec.metadata["source_code_uri"] = spec.homepage
|
|
23
23
|
|
|
24
|
-
spec.files
|
|
25
|
-
`git ls-files -z
|
|
24
|
+
spec.files = Dir.chdir(__dir__) do
|
|
25
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
26
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features|rakelib)/|\.(?:git|travis|circleci)|appveyor|Rakefile)})
|
|
27
|
+
end
|
|
26
28
|
end
|
|
27
|
-
spec.bindir = "exe"
|
|
28
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
29
29
|
spec.require_paths = ["lib"]
|
|
30
30
|
end
|
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.2
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yukihiro Matsumoto
|
|
8
8
|
autorequire:
|
|
9
|
-
bindir:
|
|
9
|
+
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2023-02-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Auto-terminate potentially long-running operations in Ruby.
|
|
14
14
|
email:
|
|
@@ -17,14 +17,9 @@ executables: []
|
|
|
17
17
|
extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
|
19
19
|
files:
|
|
20
|
-
- ".github/workflows/test.yml"
|
|
21
|
-
- ".gitignore"
|
|
22
20
|
- Gemfile
|
|
23
21
|
- LICENSE.txt
|
|
24
22
|
- README.md
|
|
25
|
-
- Rakefile
|
|
26
|
-
- bin/console
|
|
27
|
-
- bin/setup
|
|
28
23
|
- lib/timeout.rb
|
|
29
24
|
- timeout.gemspec
|
|
30
25
|
homepage: https://github.com/ruby/timeout
|
|
@@ -49,7 +44,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
49
44
|
- !ruby/object:Gem::Version
|
|
50
45
|
version: '0'
|
|
51
46
|
requirements: []
|
|
52
|
-
rubygems_version: 3.
|
|
47
|
+
rubygems_version: 3.5.0.dev
|
|
53
48
|
signing_key:
|
|
54
49
|
specification_version: 4
|
|
55
50
|
summary: Auto-terminate potentially long-running operations in Ruby.
|
data/.github/workflows/test.yml
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
name: ubuntu
|
|
2
|
-
|
|
3
|
-
on: [push, pull_request]
|
|
4
|
-
|
|
5
|
-
jobs:
|
|
6
|
-
build:
|
|
7
|
-
name: build (${{ matrix.ruby }} / ${{ matrix.os }})
|
|
8
|
-
strategy:
|
|
9
|
-
matrix:
|
|
10
|
-
ruby: [ 2.7, 2.6, 2.5, 2.4, head ]
|
|
11
|
-
os: [ ubuntu-latest, macos-latest ]
|
|
12
|
-
runs-on: ${{ matrix.os }}
|
|
13
|
-
steps:
|
|
14
|
-
- uses: actions/checkout@v2
|
|
15
|
-
- name: Set up Ruby
|
|
16
|
-
uses: ruby/setup-ruby@v1
|
|
17
|
-
with:
|
|
18
|
-
ruby-version: ${{ matrix.ruby }}
|
|
19
|
-
- name: Install dependencies
|
|
20
|
-
run: bundle install
|
|
21
|
-
- name: Run test
|
|
22
|
-
run: rake test
|
data/.gitignore
DELETED
data/Rakefile
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
require "bundler/gem_tasks"
|
|
2
|
-
require "rake/testtask"
|
|
3
|
-
|
|
4
|
-
Rake::TestTask.new(:test) do |t|
|
|
5
|
-
t.libs << "test/lib"
|
|
6
|
-
t.ruby_opts << "-rhelper"
|
|
7
|
-
t.test_files = FileList["test/**/test_*.rb"]
|
|
8
|
-
end
|
|
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
|
-
|
|
17
|
-
task :default => :test
|
data/bin/console
DELETED