async_resource_pool 0.1.0 → 0.2.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/.travis.yml +2 -9
- data/README.md +1 -0
- data/async_resource_pool.gemspec +2 -1
- data/lib/async/resource_pool.rb +3 -100
- data/lib/async/resource_pool/errors.rb +26 -0
- data/lib/async/resource_pool/simple.rb +145 -0
- data/lib/async/resource_pool/version.rb +5 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 33a430430c05e8d4c974bd33e7ddef4c5c312135837374b0ed5610421347eee4
|
4
|
+
data.tar.gz: 8bd7329c95eb067c6338930dfcf577aca8e0738c315c2bc902bb2e8883397812
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b54fe315d31d27ea0761ebf54779e4c651ab82386a90b818534b6ae6d08f9d16b9fd5e38fc000326dc81cbbab6ef04719c3deffcde74fa179c9e3bf419ac883
|
7
|
+
data.tar.gz: 0c71b6f55af4b0b436ec4b5f7ba227d2d55761922af1249d6075403715697c2628e3bbef06f99562e4502472bc5740b5fc52469bcfc1f8fc194db28588bda595
|
data/.travis.yml
CHANGED
@@ -10,14 +10,7 @@ rvm:
|
|
10
10
|
- 2.3
|
11
11
|
- 2.4
|
12
12
|
- 2.5
|
13
|
-
-
|
14
|
-
- ruby-head
|
15
|
-
- rbx-3
|
16
|
-
matrix:
|
17
|
-
allow_failures:
|
18
|
-
- rvm: ruby-head
|
19
|
-
- rvm: jruby-head
|
20
|
-
- rvm: rbx-3
|
13
|
+
- 2.6
|
21
14
|
before_install:
|
22
15
|
- gem update --system
|
23
|
-
- gem install bundler -v
|
16
|
+
- gem install bundler -v 1.7.13
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# AsyncResourcePool
|
2
2
|
|
3
|
+
[](https://rubygems.org/gems/async_resource_pool)
|
3
4
|
[](https://travis-ci.com/senid231/async_resource_pool)
|
4
5
|
|
5
6
|
TODO:
|
data/async_resource_pool.gemspec
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'async/resource_pool/version'
|
4
5
|
|
5
6
|
Gem::Specification.new do |spec|
|
6
7
|
spec.name = 'async_resource_pool'
|
7
|
-
spec.version =
|
8
|
+
spec.version = Async::ResourcePool::VERSION
|
8
9
|
spec.authors = ['Denis Talakevich']
|
9
10
|
spec.email = ['senid231@gmail.com']
|
10
11
|
|
data/lib/async/resource_pool.rb
CHANGED
@@ -1,105 +1,8 @@
|
|
1
1
|
require 'async'
|
2
|
+
require 'async/resource_pool/version'
|
3
|
+
require 'async/resource_pool/simple'
|
2
4
|
|
3
5
|
module Async
|
4
|
-
|
5
|
-
VERSION = '0.1.0'
|
6
|
-
|
7
|
-
class Error < StandardError
|
8
|
-
end
|
9
|
-
|
10
|
-
class DoesNotOwnError < Error
|
11
|
-
def initialize
|
12
|
-
super('current fiber does not own this resource')
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class AlreadyOwnError < Error
|
17
|
-
def initialize
|
18
|
-
super('current fiber already own this resource')
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
class TimeoutError < Error
|
23
|
-
def initialize(timeout)
|
24
|
-
super("timeout #{timeout} seconds was elapsed")
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def initialize(limit)
|
29
|
-
@limit = limit
|
30
|
-
@owners = []
|
31
|
-
@waiters = []
|
32
|
-
end
|
33
|
-
|
34
|
-
def acquire(timeout = nil)
|
35
|
-
raise AlreadyOwnError.new if already_acquired?
|
36
|
-
|
37
|
-
unless can_be_acquired?
|
38
|
-
timeout.nil? ? wait : wait_with_timeout(timeout)
|
39
|
-
end
|
40
|
-
|
41
|
-
@owners.push(Fiber.current)
|
42
|
-
|
43
|
-
if block_given?
|
44
|
-
begin
|
45
|
-
yield
|
46
|
-
ensure
|
47
|
-
release
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
nil
|
52
|
-
end
|
53
|
-
|
54
|
-
def release
|
55
|
-
raise DoesNotOwnError.new unless already_acquired?
|
56
|
-
@owners.delete(Fiber.current)
|
57
|
-
wakeup
|
58
|
-
end
|
59
|
-
|
60
|
-
def already_acquired?
|
61
|
-
@owners.include?(Fiber.current)
|
62
|
-
end
|
63
|
-
|
64
|
-
def can_be_acquired?
|
65
|
-
@owners.size < @limit
|
66
|
-
end
|
67
|
-
|
68
|
-
def info
|
69
|
-
{
|
70
|
-
waiters: @waiters.size,
|
71
|
-
owners: @owners.size,
|
72
|
-
limit: @limit
|
73
|
-
}
|
74
|
-
end
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
def wakeup
|
79
|
-
return if @waiters.empty?
|
80
|
-
fiber = @waiters.shift
|
81
|
-
fiber.resume if fiber.alive?
|
82
|
-
end
|
83
|
-
|
84
|
-
def wait
|
85
|
-
@waiters.push(Fiber.current)
|
86
|
-
Async::Task.yield
|
87
|
-
end
|
88
|
-
|
89
|
-
def wait_with_timeout(timeout)
|
90
|
-
fiber = Fiber.current
|
91
|
-
@waiters.push(Fiber.current)
|
92
|
-
|
93
|
-
Async::Task.current.with_timeout(timeout) do |timer|
|
94
|
-
begin
|
95
|
-
Async::Task.yield
|
96
|
-
timer.cancel
|
97
|
-
rescue Async::TimeoutError => _
|
98
|
-
@waiters.delete(fiber)
|
99
|
-
raise TimeoutError.new(timeout)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
6
|
+
module ResourcePool
|
104
7
|
end
|
105
8
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Async
|
2
|
+
module ResourcePool
|
3
|
+
|
4
|
+
class Error < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
class DoesNotOwnError < Error
|
8
|
+
def initialize
|
9
|
+
super('current fiber does not own this resource')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class AlreadyOwnError < Error
|
14
|
+
def initialize
|
15
|
+
super('current fiber already own this resource')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class TimeoutError < Error
|
20
|
+
def initialize(timeout)
|
21
|
+
super("timeout #{timeout} seconds was elapsed")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require_relative 'errors'
|
3
|
+
|
4
|
+
module Async
|
5
|
+
module ResourcePool
|
6
|
+
class Simple
|
7
|
+
|
8
|
+
# @param limit [Integer] - max number of acquired resources, must be greater then 0.
|
9
|
+
# @param wakeup_strategy [Symbol] - can be :immediately or :next_loop.
|
10
|
+
def initialize(limit, wakeup_strategy = :immediately)
|
11
|
+
raise ArgumentError, 'limit must be greater than 0' if limit <= 0
|
12
|
+
unless [:immediately, :next_loop].include?(wakeup_strategy)
|
13
|
+
raise ArgumentError, 'wakeup_strategy must be :immediately or :next_loop'
|
14
|
+
end
|
15
|
+
|
16
|
+
@limit = limit
|
17
|
+
@wakeup_strategy = wakeup_strategy
|
18
|
+
@owners = []
|
19
|
+
@waiters = []
|
20
|
+
@thread_mutex = Mutex.new
|
21
|
+
end
|
22
|
+
|
23
|
+
# Acquires resource for current fiber if available otherwise yield to reactor.
|
24
|
+
# Will be resumed once resource available.
|
25
|
+
# Will raise Async::ResourcePool::TimeoutError if timeout not nil and resource isn't available after timeout.
|
26
|
+
# Will raise Async::ResourcePool::AlreadyOwnError if resource already acquired.
|
27
|
+
# @param timeout [Integer|Float] - timeout in seconds (default nil).
|
28
|
+
def acquire(timeout = nil)
|
29
|
+
raise Async::ResourcePool::AlreadyOwnError.new if already_acquired?
|
30
|
+
|
31
|
+
unless acquire_if_available
|
32
|
+
timeout.nil? ? Async::Task.yield : wait_with_timeout(timeout)
|
33
|
+
@thread_mutex.synchronize { @owners.push(Fiber.current) }
|
34
|
+
end
|
35
|
+
|
36
|
+
if block_given?
|
37
|
+
begin
|
38
|
+
yield
|
39
|
+
ensure
|
40
|
+
release
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
# Acquires resource for current fiber if resource available.
|
48
|
+
# Will raise Async::ResourcePool::AlreadyOwnError if resource already acquired.
|
49
|
+
# @return [True|False] returns true if resource was acquired.
|
50
|
+
def try_acquire
|
51
|
+
raise Async::ResourcePool::AlreadyOwnError.new if already_acquired?
|
52
|
+
|
53
|
+
if acquire_if_available
|
54
|
+
true
|
55
|
+
else
|
56
|
+
@waiters.delete(Fiber.current)
|
57
|
+
false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Releases resource for current fiber.
|
62
|
+
# Will resume first fiber that waits for resource immediately if wakeup_strategy == :immediately
|
63
|
+
# Will resume first fiber that waits for resource in next reactor loop if wakeup_strategy == :next_loop
|
64
|
+
# Will raise Async::ResourcePool::DoesNotOwnError if fiber does not own resource.
|
65
|
+
def release
|
66
|
+
raise Async::ResourcePool::DoesNotOwnError.new unless already_acquired?
|
67
|
+
@owners.delete(Fiber.current)
|
68
|
+
wakeup
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [True|False] returns true if resource already acquired by fiber
|
72
|
+
def already_acquired?
|
73
|
+
@owners.include?(Fiber.current)
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [True|False] returns true if pool has available resource
|
77
|
+
def can_be_acquired?
|
78
|
+
@owners.size < @limit
|
79
|
+
end
|
80
|
+
|
81
|
+
# @return [Hash] represents current state of resource pool.
|
82
|
+
# waiters - how many fibers waits for resource.
|
83
|
+
# owners - how many fibers own resource.
|
84
|
+
# limit - maximum of resources that can be owned simultaneously.
|
85
|
+
def info
|
86
|
+
{
|
87
|
+
waiters: @waiters.size,
|
88
|
+
owners: @owners.size,
|
89
|
+
limit: @limit
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def wakeup
|
96
|
+
return if @waiters.empty?
|
97
|
+
fiber = @waiters.shift
|
98
|
+
return unless fiber.alive?
|
99
|
+
|
100
|
+
if @wakeup_strategy == :immediately
|
101
|
+
fiber.resume
|
102
|
+
else
|
103
|
+
Async::Task.current.reactor << fiber
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def wakeup_fiber(fiber)
|
108
|
+
if @wakeup_strategy == :immediately
|
109
|
+
fiber.resume
|
110
|
+
else
|
111
|
+
Async::Task.current.reactor << fiber
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def acquire_if_available
|
116
|
+
fiber = Fiber.current
|
117
|
+
|
118
|
+
@thread_mutex.synchronize do
|
119
|
+
if can_be_acquired?
|
120
|
+
@owners.push(fiber)
|
121
|
+
true
|
122
|
+
else
|
123
|
+
@waiters.push(fiber)
|
124
|
+
false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def wait_with_timeout(timeout)
|
130
|
+
fiber = Fiber.current
|
131
|
+
|
132
|
+
Async::Task.current.with_timeout(timeout) do |timer|
|
133
|
+
begin
|
134
|
+
Async::Task.yield
|
135
|
+
timer.cancel
|
136
|
+
rescue Async::TimeoutError => _
|
137
|
+
@waiters.delete(fiber)
|
138
|
+
raise Async::ResourcePool::TimeoutError.new(timeout)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async_resource_pool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Talakevich
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -85,6 +85,9 @@ files:
|
|
85
85
|
- bin/console
|
86
86
|
- bin/setup
|
87
87
|
- lib/async/resource_pool.rb
|
88
|
+
- lib/async/resource_pool/errors.rb
|
89
|
+
- lib/async/resource_pool/simple.rb
|
90
|
+
- lib/async/resource_pool/version.rb
|
88
91
|
- lib/async_resource_pool.rb
|
89
92
|
homepage: https://github.com/senid231/async_resource_pool
|
90
93
|
licenses:
|