concurrent_monitor 0.0.1.ci.release → 0.9.0.rc1
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/async/monitor.rb +16 -7
- data/lib/concurrent_monitor/barrier.rb +9 -3
- data/lib/concurrent_monitor/future.rb +21 -3
- data/lib/concurrent_monitor/semaphore.rb +1 -1
- data/lib/concurrent_monitor/task.rb +27 -6
- data/lib/concurrent_monitor/timeout_clock.rb +3 -3
- data/lib/concurrent_monitor/timeout_watcher.rb +69 -0
- data/lib/concurrent_monitor/version.rb +1 -1
- data/lib/concurrent_monitor/wait_timeout.rb +1 -1
- data/lib/concurrent_monitor.rb +15 -4
- data/lib/thread/monitor.rb +23 -15
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6d2a561842bcf407eb933762b7d48f9033b7b4c7d45dc6c042055b689b1ed6b8
|
|
4
|
+
data.tar.gz: f2de6f5698c4392c16c089cd9556e3942ab947a9459942d4e3757d2872963a18
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 17403d800fd04ce45099243c267027755f18d24b7bb84f9fdd798c3bba4dab384978d40a76df8dba397b14978207d00a6e9c3d031bbb6dc286a81234c1a6f407
|
|
7
|
+
data.tar.gz: 68fff665dfa471b808bafbdf4b27fe6d423ea9a5ad0405330520f21a1fff6c3f3dbd8410a91a34c5f61ca4e77aa0da3def2081c7bb9c18f9c6f542182ed58ab4
|
data/lib/async/monitor.rb
CHANGED
|
@@ -10,12 +10,16 @@ module Async
|
|
|
10
10
|
class Monitor
|
|
11
11
|
# Common task interface over Async::Task
|
|
12
12
|
class Task < ConcurrentMonitor::Task
|
|
13
|
-
def initialize(
|
|
13
|
+
def initialize(name_arg = nil, name: name_arg, report_on_exception: true, &block)
|
|
14
14
|
super()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
if block_given?
|
|
16
|
+
Async(annotation: name, finished: report_on_exception ? nil : false) do |task|
|
|
17
|
+
@task = task
|
|
18
|
+
Fiber.current.concurrent_monitor_task = self
|
|
19
|
+
block.call(self)
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
@task = Async::Task.current
|
|
19
23
|
end
|
|
20
24
|
end
|
|
21
25
|
|
|
@@ -23,7 +27,12 @@ module Async
|
|
|
23
27
|
|
|
24
28
|
def current? = @task.current?
|
|
25
29
|
|
|
26
|
-
def value
|
|
30
|
+
def value
|
|
31
|
+
result = @task.wait
|
|
32
|
+
raise ConcurrentMonitor::TaskStopped, 'Task was stopped' if @task.stopped?
|
|
33
|
+
|
|
34
|
+
result
|
|
35
|
+
end
|
|
27
36
|
|
|
28
37
|
def stop
|
|
29
38
|
raise Async::Stop if current?
|
|
@@ -56,7 +65,7 @@ module Async
|
|
|
56
65
|
end
|
|
57
66
|
|
|
58
67
|
def current_task
|
|
59
|
-
Fiber.current.concurrent_monitor_task
|
|
68
|
+
Fiber.current.concurrent_monitor_task ||= Task.new
|
|
60
69
|
end
|
|
61
70
|
|
|
62
71
|
def new_monitor
|
|
@@ -43,7 +43,7 @@ module ConcurrentMonitor
|
|
|
43
43
|
# @param [:to_s] name
|
|
44
44
|
# @param [Boolean] report_on_exception
|
|
45
45
|
# @return [Task]
|
|
46
|
-
def async(
|
|
46
|
+
def async(name_arg = nil, name: name_arg, report_on_exception: false, &)
|
|
47
47
|
synchronize { monitor.async(name, report_on_exception:) { |t| run_task(t, &) }.tap { |t| tasks << t } }
|
|
48
48
|
end
|
|
49
49
|
|
|
@@ -74,8 +74,9 @@ module ConcurrentMonitor
|
|
|
74
74
|
return enum_for(:each).lazy unless block_given?
|
|
75
75
|
|
|
76
76
|
each_task do |t|
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
yield t.value
|
|
78
|
+
rescue TaskStopped
|
|
79
|
+
# skip stopped tasks
|
|
79
80
|
end
|
|
80
81
|
end
|
|
81
82
|
|
|
@@ -96,6 +97,11 @@ module ConcurrentMonitor
|
|
|
96
97
|
(block_given? ? yield(self) : self).tap { each(&:itself) }
|
|
97
98
|
end
|
|
98
99
|
|
|
100
|
+
# The value of a barrier is the array of values of all its tasks.
|
|
101
|
+
def value
|
|
102
|
+
to_a
|
|
103
|
+
end
|
|
104
|
+
|
|
99
105
|
# {#wait}, ensuring {#stop}
|
|
100
106
|
def wait!(&) = ensure_stop { wait(&) }
|
|
101
107
|
|
|
@@ -11,7 +11,8 @@ module ConcurrentMonitor
|
|
|
11
11
|
@error = nil
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
# Blocks until the future is completed or
|
|
14
|
+
# Blocks until the future is completed, then returns its value or raises its error.
|
|
15
|
+
#
|
|
15
16
|
# @return [Object] The value with which the future was fulfilled
|
|
16
17
|
# @raise [StandardError] If the future was rejected
|
|
17
18
|
def value
|
|
@@ -21,8 +22,13 @@ module ConcurrentMonitor
|
|
|
21
22
|
@value
|
|
22
23
|
end
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
# Wait until completed
|
|
26
|
+
# @overload wait(timeout, exception: nil)
|
|
27
|
+
# @param timeout [Numeric|nil]
|
|
28
|
+
# @param exception [Class<StandardError>]an error to raise if timeout
|
|
29
|
+
# @return [Boolean] true if completed, nil or raises exception if timed out
|
|
30
|
+
def wait(timeout_arg = nil, timeout: timeout_arg, exception: nil)
|
|
31
|
+
synchronize { condition.wait_until(timeout, exception:) { @completed } }
|
|
26
32
|
end
|
|
27
33
|
|
|
28
34
|
# Resolve the future with a block
|
|
@@ -41,6 +47,11 @@ module ConcurrentMonitor
|
|
|
41
47
|
complete! { @value = value }
|
|
42
48
|
end
|
|
43
49
|
|
|
50
|
+
# @return [Boolean] true if completed with a value, otherwise false
|
|
51
|
+
def fulfilled?
|
|
52
|
+
completed? && !@error
|
|
53
|
+
end
|
|
54
|
+
|
|
44
55
|
# Rejects this future with the given error
|
|
45
56
|
# @param error [Exception] The error to reject this future with
|
|
46
57
|
# @return [Boolean] true if the future was rejected, false if already completed
|
|
@@ -49,10 +60,17 @@ module ConcurrentMonitor
|
|
|
49
60
|
complete! { @error = error }
|
|
50
61
|
end
|
|
51
62
|
|
|
63
|
+
# @return [Boolean] false if not complete or completed with a value
|
|
64
|
+
# @return [StandardError] if completed with an error
|
|
65
|
+
def rejected?
|
|
66
|
+
completed? && @error
|
|
67
|
+
end
|
|
68
|
+
|
|
52
69
|
# @return [Boolean] true if this future has been completed
|
|
53
70
|
def completed?
|
|
54
71
|
synchronize { @completed }
|
|
55
72
|
end
|
|
73
|
+
alias resolved? completed?
|
|
56
74
|
|
|
57
75
|
# @return [Boolean] true if this future has not yet been completed
|
|
58
76
|
def pending?
|
|
@@ -37,7 +37,7 @@ module ConcurrentMonitor
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
# Start a task, blocking until the semaphore can be acquired
|
|
40
|
-
def async(
|
|
40
|
+
def async(name_arg = nil, name: name_arg, report_on_exception: true, &)
|
|
41
41
|
synchronize do
|
|
42
42
|
@condition.wait_while { @task_count >= @limit }
|
|
43
43
|
@task_count += 1
|
|
@@ -3,32 +3,53 @@
|
|
|
3
3
|
require_relative 'wait_timeout'
|
|
4
4
|
|
|
5
5
|
module ConcurrentMonitor
|
|
6
|
+
# Raised when calling #value on a stopped task
|
|
7
|
+
class TaskStopped < StandardError; end
|
|
8
|
+
|
|
6
9
|
# @abstract
|
|
7
10
|
# Common interface to underlying tasks
|
|
8
11
|
class Task
|
|
9
12
|
# @!method value
|
|
10
|
-
# Wait for task to complete and return its value
|
|
13
|
+
# Wait for task to complete and return its value, or raise its error
|
|
11
14
|
# @return [Object]
|
|
15
|
+
# @raise [TaskStopped] if the task was explicitly {#stop stopped} (Thread API)
|
|
12
16
|
# @raise [StandardError]
|
|
13
17
|
|
|
14
|
-
# Wait for task to complete
|
|
15
|
-
# @return [
|
|
18
|
+
# Wait for task to complete and return its value, or raise its error
|
|
19
|
+
# @return [Object]
|
|
20
|
+
# @return [nil] if the task was explicitly {#stop stopped} (Async::Task API)
|
|
21
|
+
# @raise [StandardError] if task raised an exception
|
|
22
|
+
# @see stopped?
|
|
16
23
|
def wait
|
|
17
24
|
value
|
|
18
|
-
|
|
25
|
+
rescue TaskStopped
|
|
26
|
+
nil
|
|
19
27
|
end
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
# Wait for task to complete and return self (Thread API)
|
|
30
|
+
# @return [self]
|
|
31
|
+
# @raise [TaskStopped] if task was stopped
|
|
32
|
+
# @raise [StandardError] if task raised an exception
|
|
33
|
+
def join
|
|
34
|
+
value
|
|
35
|
+
self
|
|
36
|
+
end
|
|
22
37
|
|
|
23
38
|
# @!method stop
|
|
24
39
|
# @return [self] after stopping
|
|
25
40
|
# @raise [Exception] if called from the current task
|
|
26
41
|
|
|
27
42
|
# @!method stopped?
|
|
43
|
+
# Check if task was stopped.
|
|
44
|
+
#
|
|
45
|
+
# A false result is ambiguous unless the calling task has called {#wait} (or rescued {TaskStopped} from a call to
|
|
46
|
+
# {#value}/{#join})
|
|
28
47
|
# @return [Boolean] true if the task has completed via stop.
|
|
29
48
|
|
|
30
49
|
# @!method alive?
|
|
31
|
-
#
|
|
50
|
+
# A true result is ambiguous unless the calling task has called {#wait} (or rescued {TaskStopped} from a call to
|
|
51
|
+
# {#value}/{#join})
|
|
52
|
+
# @return [Boolean] true if the task has not reached completion
|
|
32
53
|
|
|
33
54
|
# @!method current?
|
|
34
55
|
# @return [Boolean] true if this task is the current thread/fiber
|
|
@@ -23,12 +23,12 @@ module ConcurrentMonitor
|
|
|
23
23
|
# Create a TimeoutClock and wait until block is true. See {#wait_until}
|
|
24
24
|
# @example
|
|
25
25
|
# TimeoutClock.wait_until(60) { closed? || (sleep(1) && false)}
|
|
26
|
-
def wait_until(
|
|
26
|
+
def wait_until(timeout_arg = nil, timeout: timeout_arg, delay: nil, exception: nil, &)
|
|
27
27
|
timeout(timeout).wait_until(exception:, delay:, &)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
# Create a TimeoutClock and wait while block is true. See {#wait_while}
|
|
31
|
-
def wait_while(
|
|
31
|
+
def wait_while(timeout_arg = nil, timeout: timeout_arg, delay: nil, exception: nil, &)
|
|
32
32
|
timeout(timeout).wait_while(exception:, delay:, &)
|
|
33
33
|
end
|
|
34
34
|
end
|
|
@@ -86,7 +86,7 @@ module ConcurrentMonitor
|
|
|
86
86
|
return result
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
-
sleep([delay, self.remaining].min) if delay
|
|
89
|
+
sleep([delay, self.remaining || delay].min) if delay
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
raise exception, 'timed out' if exception
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConcurrentMonitor
|
|
4
|
+
# Shared timeout watcher that manages timeouts for multiple tasks efficiently.
|
|
5
|
+
# Caller is responsible for starting the watcher task.
|
|
6
|
+
class TimeoutWatcher
|
|
7
|
+
attr_reader :monitor
|
|
8
|
+
|
|
9
|
+
include ConcurrentMonitor
|
|
10
|
+
|
|
11
|
+
# @param monitor [ConcurrentMonitor::Mixin] the monitor for synchronization
|
|
12
|
+
def initialize(monitor:)
|
|
13
|
+
@monitor = monitor
|
|
14
|
+
@timeouts = {}
|
|
15
|
+
@condition = monitor.new_condition
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Wrap a block with timeout. Spawns the block in a new task, registers it for timeout,
|
|
19
|
+
# and ensures timeout is cancelled on completion.
|
|
20
|
+
# @param timeout [Numeric] seconds until timeout
|
|
21
|
+
# @yield block to execute
|
|
22
|
+
# @return [Task] the spawned task
|
|
23
|
+
def with_timeout(timeout_arg = nil, timeout: timeout_arg, **, &block)
|
|
24
|
+
async(**async) do |t|
|
|
25
|
+
watch(t, timeout: timeout) if timeout
|
|
26
|
+
block.call(t)
|
|
27
|
+
ensure
|
|
28
|
+
cancel_timeout(t) if timeout
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Cancel timeout for task
|
|
33
|
+
def cancel_timeout(task = current_task)
|
|
34
|
+
synchronize { @timeouts.delete(task) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Run the watcher loop. Caller should spawn this in a task.
|
|
38
|
+
# @example
|
|
39
|
+
# watcher = TimeoutWatcher.new(monitor: self)
|
|
40
|
+
# watcher_task = async(name: 'timeout_watcher') { watcher.run }
|
|
41
|
+
def run
|
|
42
|
+
until @stopped
|
|
43
|
+
synchronize do
|
|
44
|
+
@timeouts.delete_if { |task, clock| clock.expired?.tap { |expired| task.stop if expired } }
|
|
45
|
+
@condition.wait(@timeouts.values.map(&:remaining).compact.min)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def stop
|
|
51
|
+
synchronize do
|
|
52
|
+
@stopped = true
|
|
53
|
+
@condition.broadcast
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
# Register task to be stopped after timeout
|
|
60
|
+
def watch(task, timeout:)
|
|
61
|
+
clock = self.timeout(timeout)
|
|
62
|
+
|
|
63
|
+
synchronize do
|
|
64
|
+
@timeouts[task] = clock
|
|
65
|
+
@condition.broadcast
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -11,7 +11,7 @@ module ConcurrentMonitor
|
|
|
11
11
|
# @param [Exception|nil] exception an exception to raise on timeout
|
|
12
12
|
# @return [Object] the truthy return value of the block
|
|
13
13
|
# @return [nil] if a timeout occurs
|
|
14
|
-
def wait_until(
|
|
14
|
+
def wait_until(timeout_arg = nil, timeout: timeout_arg, exception: nil)
|
|
15
15
|
TimeoutClock.wait_until(timeout, exception:) { |remaining| yield || (wait(remaining) && false) }
|
|
16
16
|
end
|
|
17
17
|
|
data/lib/concurrent_monitor.rb
CHANGED
|
@@ -6,6 +6,7 @@ require_relative 'concurrent_monitor/condition_variable'
|
|
|
6
6
|
require_relative 'concurrent_monitor/queue'
|
|
7
7
|
require_relative 'concurrent_monitor/barrier'
|
|
8
8
|
require_relative 'concurrent_monitor/future'
|
|
9
|
+
require_relative 'concurrent_monitor/timeout_watcher'
|
|
9
10
|
require 'forwardable'
|
|
10
11
|
|
|
11
12
|
# A unified abstraction layer for synchronization and concurrency primitives that works
|
|
@@ -45,7 +46,7 @@ module ConcurrentMonitor
|
|
|
45
46
|
|
|
46
47
|
# @!attribute [rw] monitor
|
|
47
48
|
# @return [Async::Monitor,Thread::Monitor] should be set to an instance of Async::Monitor or Thread::Monitor
|
|
48
|
-
def_delegators :@monitor, :sync, :async, :current_task, :synchronize, :new_condition, :task_dump
|
|
49
|
+
def_delegators :@monitor, :sync, :async, :current_task, :synchronize, :new_condition, :new_monitor, :task_dump
|
|
49
50
|
|
|
50
51
|
# @!method new_monitor
|
|
51
52
|
# @return [Async::Monitor|Thread::Monitor] a new monitor of the same kind as the current one
|
|
@@ -83,9 +84,9 @@ module ConcurrentMonitor
|
|
|
83
84
|
# @param [Numeric|TimeoutClock] timeout
|
|
84
85
|
# @param [Class|StandardError|nil] exception if set the error to raise on timeout
|
|
85
86
|
# @return [Object] task result, nil if timed out without exception
|
|
86
|
-
def with_timeout(timeout, exception: nil, condition: new_condition, &block)
|
|
87
|
+
def with_timeout(timeout_arg, timeout: timeout_arg, exception: nil, condition: new_condition, **kw_async, &block)
|
|
87
88
|
done = false
|
|
88
|
-
task = async do |t|
|
|
89
|
+
task = async(**kw_async) do |t|
|
|
89
90
|
block.call(t)
|
|
90
91
|
ensure
|
|
91
92
|
synchronize do
|
|
@@ -95,7 +96,7 @@ module ConcurrentMonitor
|
|
|
95
96
|
end
|
|
96
97
|
|
|
97
98
|
begin
|
|
98
|
-
synchronize { condition.wait_until(timeout
|
|
99
|
+
synchronize { condition.wait_until(timeout:, exception:) { done } }
|
|
99
100
|
ensure
|
|
100
101
|
task.stop
|
|
101
102
|
end
|
|
@@ -123,6 +124,16 @@ module ConcurrentMonitor
|
|
|
123
124
|
Future.new(monitor:)
|
|
124
125
|
end
|
|
125
126
|
|
|
127
|
+
def new_semaphore(limit:, monitor: self)
|
|
128
|
+
Semaphore.new(monitor:, limit:)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Creates a new timeout watcher
|
|
132
|
+
# @return [TimeoutWatcher]
|
|
133
|
+
def new_timeout_watcher(monitor: self)
|
|
134
|
+
TimeoutWatcher.new(monitor:)
|
|
135
|
+
end
|
|
136
|
+
|
|
126
137
|
# @!attribute [rw] monitor
|
|
127
138
|
# @return [Thread::Monitor|Async::Monitor]
|
|
128
139
|
attr_accessor :monitor
|
data/lib/thread/monitor.rb
CHANGED
|
@@ -10,7 +10,7 @@ class Thread
|
|
|
10
10
|
class Monitor
|
|
11
11
|
# rubocop:disable Lint/InheritException
|
|
12
12
|
|
|
13
|
-
# Raised from when a task is stopped
|
|
13
|
+
# Raised from stop when a task is stopped
|
|
14
14
|
class Stop < Exception; end
|
|
15
15
|
|
|
16
16
|
# rubocop:enable Lint/InheritException
|
|
@@ -23,17 +23,24 @@ class Thread
|
|
|
23
23
|
# @!visibility private
|
|
24
24
|
# Common task interface over Thread
|
|
25
25
|
class Task < ConcurrentMonitor::Task
|
|
26
|
-
def initialize(
|
|
26
|
+
def initialize(name_arg = nil, name: name_arg, report_on_exception: true, &block)
|
|
27
27
|
super()
|
|
28
28
|
@stopped = nil
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
|
|
30
|
+
@thread =
|
|
31
|
+
if block_given?
|
|
32
|
+
Thread.new(self, name, report_on_exception, block) do |t, n, e, b|
|
|
33
|
+
run_thread(t, n, e, &b)
|
|
34
|
+
rescue Stop
|
|
35
|
+
@stopped = true
|
|
36
|
+
nil
|
|
37
|
+
ensure
|
|
38
|
+
@stopped ||= false
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
# this is the main thread
|
|
42
|
+
Thread.current.tap { |t| t.thread_variable_set(:concurrent_monitor_task, self) }
|
|
43
|
+
end
|
|
37
44
|
end
|
|
38
45
|
|
|
39
46
|
def alive? = @thread.alive?
|
|
@@ -41,12 +48,13 @@ class Thread
|
|
|
41
48
|
def current? = @thread == Thread.current
|
|
42
49
|
|
|
43
50
|
def value
|
|
44
|
-
|
|
51
|
+
result = @thread.value
|
|
52
|
+
raise ConcurrentMonitor::TaskStopped, 'Task was stopped' if @stopped
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
result
|
|
47
55
|
rescue Stop
|
|
48
56
|
@stopped = true
|
|
49
|
-
|
|
57
|
+
raise ConcurrentMonitor::TaskStopped, 'Task was stopped'
|
|
50
58
|
end
|
|
51
59
|
|
|
52
60
|
def stop
|
|
@@ -58,7 +66,7 @@ class Thread
|
|
|
58
66
|
self # race with alive?
|
|
59
67
|
end
|
|
60
68
|
|
|
61
|
-
# must call
|
|
69
|
+
# must call wait before @stopped will be true
|
|
62
70
|
def stopped?
|
|
63
71
|
!!@stopped
|
|
64
72
|
end
|
|
@@ -99,7 +107,7 @@ class Thread
|
|
|
99
107
|
end
|
|
100
108
|
|
|
101
109
|
def current_task
|
|
102
|
-
Thread.current.thread_variable_get(:concurrent_monitor_task)
|
|
110
|
+
Thread.current.thread_variable_get(:concurrent_monitor_task) || Task.new
|
|
103
111
|
end
|
|
104
112
|
|
|
105
113
|
def task_dump(io = $stderr)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: concurrent_monitor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.9.0.rc1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Grant Gardner
|
|
@@ -25,6 +25,7 @@ files:
|
|
|
25
25
|
- lib/concurrent_monitor/semaphore.rb
|
|
26
26
|
- lib/concurrent_monitor/task.rb
|
|
27
27
|
- lib/concurrent_monitor/timeout_clock.rb
|
|
28
|
+
- lib/concurrent_monitor/timeout_watcher.rb
|
|
28
29
|
- lib/concurrent_monitor/version.rb
|
|
29
30
|
- lib/concurrent_monitor/wait_timeout.rb
|
|
30
31
|
- lib/thread/monitor.rb
|