async-pool 0.3.2 → 0.3.7

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: 60d511b499cd1be338be2e27e2f2a3266152a751174694fa9ce57df81137c59e
4
- data.tar.gz: b8c2ef5e6bf6ed4150f4149c411dcaad6ff60da9736b83b0f63dc0524bc973a7
3
+ metadata.gz: 36dfa0c90392d3f6e3582b1bdd3e2fdb7667ddccb601fb4dd34df3e8aa3baa2e
4
+ data.tar.gz: bea9381873eed77a2fd9dcb4d90190e9fa3ebbbba0f38c5628ee369f5da58a97
5
5
  SHA512:
6
- metadata.gz: f6277156f05069cae282cbb2152d2ffa1fa4270c233bf4d356dc3530bfb5b7ad99e3916a578565a59b7348d803a927a449db2f76836d480a8409ff530009d92f
7
- data.tar.gz: edd54a4824cdb509ba987b53d4a49149fd655e160c96ca24c32e9d83e3ebca26b13100b215ac3d410f597ff0dfdab491f31b825eb22e41078054f8b0f3d917b4
6
+ metadata.gz: 7ece710c9d7ec8fcb62b92ae9fb19775de4f781f886a3b9e285f08a0a9483599bc15e6b5d727df7697202f1b11381cba5ebb731d84ad9236abc4c29d8293a508
7
+ data.tar.gz: 02d545782a0b63b7ba74ce19a450ab7851886548eb71805c2fd9e5b71f8daf838c5dfbd5013b6bec2b639a2f630a5dfc846baaf9b28581cf53cb26d9b975290d
@@ -18,7 +18,7 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require 'async/logger'
21
+ require 'console/logger'
22
22
 
23
23
  require 'async'
24
24
  require 'async/notification'
@@ -32,9 +32,13 @@ module Async
32
32
  end
33
33
 
34
34
  def initialize(constructor, limit: nil)
35
+ # All available resources:
35
36
  @resources = {}
36
37
 
38
+ # Resources which may be available to be acquired:
39
+ # This list may contain false positives, or resources which were okay but have since entered a state which is unusuable.
37
40
  @available = []
41
+
38
42
  @notification = Async::Notification.new
39
43
 
40
44
  @limit = limit
@@ -45,7 +49,7 @@ module Async
45
49
  @gardener = nil
46
50
  end
47
51
 
48
- # @attr [Hash<Resource, Integer>] all allocated resources, and their associated usage.
52
+ # @attribute [Hash(Resource, Integer)] all allocated resources, and their associated usage.
49
53
  attr :resources
50
54
 
51
55
  def size
@@ -66,6 +70,11 @@ module Async
66
70
  return false
67
71
  end
68
72
 
73
+ # Whether there are available resources, i.e. whether {#acquire} can reuse an existing resource.
74
+ def available?
75
+ @available.any?
76
+ end
77
+
69
78
  # Wait until a pool resource has been freed.
70
79
  def wait
71
80
  @notification.wait
@@ -98,6 +107,8 @@ module Async
98
107
  end
99
108
 
100
109
  def close
110
+ @available.clear
111
+
101
112
  @resources.each_key(&:close)
102
113
  @resources.clear
103
114
 
@@ -119,7 +130,9 @@ module Async
119
130
  unused = []
120
131
 
121
132
  @resources.each do |resource, usage|
122
- unused << resource if usage.zero?
133
+ if usage.zero?
134
+ unused << resource
135
+ end
123
136
  end
124
137
 
125
138
  unused.each do |resource|
@@ -132,11 +145,19 @@ module Async
132
145
  break if @resources.size <= retain
133
146
  end
134
147
 
148
+ # Update availability list:
149
+ @available.clear
150
+ @resources.each do |resource, usage|
151
+ if usage < resource.concurrency and resource.reusable?
152
+ @available << resource
153
+ end
154
+ end
155
+
135
156
  return unused.size
136
157
  end
137
158
 
138
159
  def retire(resource)
139
- Async.logger.debug(self) {"Retire #{resource}"}
160
+ Console.logger.debug(self) {"Retire #{resource}"}
140
161
 
141
162
  @resources.delete(resource)
142
163
 
@@ -150,7 +171,7 @@ module Async
150
171
  def start_gardener
151
172
  return if @gardener
152
173
 
153
- Async(transient: true) do |task|
174
+ Async(transient: true, annotation: "#{self.class} Gardener") do |task|
154
175
  @gardener = task
155
176
 
156
177
  Task.yield
@@ -170,11 +191,42 @@ module Async
170
191
  end.join(";")
171
192
  end
172
193
 
194
+ def usage
195
+ @resources.count{|resource, usage| usage > 0}
196
+ end
197
+
198
+ def free
199
+ @resources.count{|resource, usage| usage == 0}
200
+ end
201
+
202
+ # @returns [Boolean] Whether the number of available resources is excessive and we should retire some.
203
+ def overflowing?
204
+ if @resources.any?
205
+ (self.free.to_f / @resources.size) > 0.5
206
+ end
207
+ end
208
+
173
209
  def reuse(resource)
174
- Async.logger.debug(self) {"Reuse #{resource}"}
210
+ Console.logger.debug(self) {"Reuse #{resource}"}
211
+ usage = @resources[resource]
175
212
 
176
- @resources[resource] -= 1
177
- @available.push(resource)
213
+ if usage.zero?
214
+ raise "Trying to reuse unacquired resource: #{resource}!"
215
+ end
216
+
217
+ # We retire resources when adding to the @available list would overflow our pool:
218
+ if usage == 1
219
+ if overflowing?
220
+ return retire(resource)
221
+ end
222
+ end
223
+
224
+ # If the resource was fully utilized, it now becomes available:
225
+ if usage == resource.concurrency
226
+ @available.push(resource)
227
+ end
228
+
229
+ @resources[resource] = usage - 1
178
230
 
179
231
  @notification.signal
180
232
  end
@@ -185,7 +237,7 @@ module Async
185
237
  @notification.wait
186
238
  end
187
239
 
188
- Async.logger.debug(self) {"Wait for resource -> #{resource}"}
240
+ Console.logger.debug(self) {"Wait for resource -> #{resource}"}
189
241
 
190
242
  # if resource.concurrency > 1
191
243
  # @notification.signal
@@ -194,6 +246,7 @@ module Async
194
246
  return resource
195
247
  end
196
248
 
249
+ # @returns [Object] A new resource in a "used" state.
197
250
  def create_resource
198
251
  self.start_gardener
199
252
 
@@ -201,37 +254,56 @@ module Async
201
254
  if resource = @constructor.call
202
255
  @resources[resource] = 1
203
256
 
204
- @available.push(resource) if resource.concurrency > 1
257
+ # Make the resource available if it can be used multiple times:
258
+ if resource.concurrency > 1
259
+ @available.push(resource)
260
+ end
205
261
  end
206
262
 
207
263
  return resource
208
264
  end
209
265
 
266
+ # @returns [Object] An existing resource in a "used" state.
210
267
  def available_resource
268
+ resource = nil
269
+
211
270
  @guard.acquire do
212
- while resource = @available.last
213
- if usage = @resources[resource] and usage < resource.concurrency
214
- if resource.viable?
215
- @resources[resource] += 1
216
-
217
- return resource
218
- else
219
- retire(resource)
271
+ resource = get_resource
272
+ end
273
+
274
+ return resource
275
+ rescue Exception
276
+ reuse(resource) if resource
277
+ raise
278
+ end
279
+
280
+ private def get_resource
281
+ while resource = @available.last
282
+ if usage = @resources[resource] and usage < resource.concurrency
283
+ if resource.viable?
284
+ usage = (@resources[resource] += 1)
285
+
286
+ if usage == resource.concurrency
287
+ # The resource is used up to it's limit:
220
288
  @available.pop
221
289
  end
290
+
291
+ return resource
222
292
  else
293
+ retire(resource)
223
294
  @available.pop
224
295
  end
225
- end
226
-
227
- if @limit.nil? or @resources.size < @limit
228
- Async.logger.debug(self) {"No resources resources, allocating new one..."}
229
-
230
- return create_resource
296
+ else
297
+ # The resource has been removed already, so skip it and remove it from the availability list.
298
+ @available.pop
231
299
  end
232
300
  end
233
301
 
234
- return nil
302
+ if @limit.nil? or @resources.size < @limit
303
+ Console.logger.debug(self) {"No available resources, allocating new one..."}
304
+
305
+ return create_resource
306
+ end
235
307
  end
236
308
  end
237
309
  end
@@ -18,7 +18,7 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require 'async/logger'
21
+ require 'console/logger'
22
22
 
23
23
  require 'async/notification'
24
24
  require 'async/semaphore'
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module Pool
23
- VERSION = "0.3.2"
23
+ VERSION = "0.3.7"
24
24
  end
25
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-pool
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-20 00:00:00.000000000 Z
11
+ date: 2021-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -108,8 +108,8 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '3.6'
111
- description:
112
- email:
111
+ description:
112
+ email:
113
113
  executables: []
114
114
  extensions: []
115
115
  extra_rdoc_files: []
@@ -123,7 +123,7 @@ licenses:
123
123
  - MIT
124
124
  metadata:
125
125
  funding_uri: https://github.com/sponsors/ioquatix/
126
- post_install_message:
126
+ post_install_message:
127
127
  rdoc_options: []
128
128
  require_paths:
129
129
  - lib
@@ -131,15 +131,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
131
131
  requirements:
132
132
  - - ">="
133
133
  - !ruby/object:Gem::Version
134
- version: '0'
134
+ version: '2.5'
135
135
  required_rubygems_version: !ruby/object:Gem::Requirement
136
136
  requirements:
137
137
  - - ">="
138
138
  - !ruby/object:Gem::Version
139
139
  version: '0'
140
140
  requirements: []
141
- rubygems_version: 3.1.2
142
- signing_key:
141
+ rubygems_version: 3.3.0.dev
142
+ signing_key:
143
143
  specification_version: 4
144
144
  summary: A singleplex and multiplex resource pool for implementing robust clients.
145
145
  test_files: []