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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75133743aeaeab2c6bee303c2d2497055d9d5563e7b4ccebb3bf6cae0660fe13
4
- data.tar.gz: 247147e3ce15590265ac8b9a55d82008c4f031a7d503748e727263b8734384e4
3
+ metadata.gz: 33a430430c05e8d4c974bd33e7ddef4c5c312135837374b0ed5610421347eee4
4
+ data.tar.gz: 8bd7329c95eb067c6338930dfcf577aca8e0738c315c2bc902bb2e8883397812
5
5
  SHA512:
6
- metadata.gz: 5aa8a66212a37bf79160fbcf29f6ca523180512a3a9de16f058db725514aaf2c3dab8ed40605787ccc407feb26c44e90df77b7f079bf8e2ffa36412a44098383
7
- data.tar.gz: 5d844e30908ce0c0a448ab0cc33eb7238acd5079defe643a644544896aab7c004ed662ca969f29198834d87a5976e79c8f4e1425d7b4e73fb89834151b154461
6
+ metadata.gz: 8b54fe315d31d27ea0761ebf54779e4c651ab82386a90b818534b6ae6d08f9d16b9fd5e38fc000326dc81cbbab6ef04719c3deffcde74fa179c9e3bf419ac883
7
+ data.tar.gz: 0c71b6f55af4b0b436ec4b5f7ba227d2d55761922af1249d6075403715697c2628e3bbef06f99562e4502472bc5740b5fc52469bcfc1f8fc194db28588bda595
@@ -10,14 +10,7 @@ rvm:
10
10
  - 2.3
11
11
  - 2.4
12
12
  - 2.5
13
- - jruby-head
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 2.0.1
16
+ - gem install bundler -v 1.7.13
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # AsyncResourcePool
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/async_resource_pool.svg)](https://rubygems.org/gems/async_resource_pool)
3
4
  [![Build Status](https://travis-ci.com/senid231/async_resource_pool.svg?branch=master)](https://travis-ci.com/senid231/async_resource_pool)
4
5
 
5
6
  TODO:
@@ -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 = '0.1.0'
8
+ spec.version = Async::ResourcePool::VERSION
8
9
  spec.authors = ['Denis Talakevich']
9
10
  spec.email = ['senid231@gmail.com']
10
11
 
@@ -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
- class ResourcePool
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
@@ -0,0 +1,5 @@
1
+ module Async
2
+ module ResourcePool
3
+ VERSION = '0.2.0'
4
+ end
5
+ 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.1.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-13 00:00:00.000000000 Z
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: