async-pool 0.11.1 → 0.12.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: f51a25e4bec2c50dc1e0a3dd8b58dcea1deec49d964a1e96c2cb4e1ef4f6de91
4
- data.tar.gz: 7ae894d17e1b977e20910dab375cf898f76051af6b71a165332143550efbb57c
3
+ metadata.gz: 8261d789df6f1e5666e9994a6b977a9a51f7c196701fffebe2f26eca28aab213
4
+ data.tar.gz: 9db4443e7e225ed235c8700c2a89292bced23af536abf5d5a625520ccc470c5f
5
5
  SHA512:
6
- metadata.gz: d92167ce83dec22ac8224c10741bef3595884baeef9fb00c31ef71f7a3cced965d642965c4091902567476ba246cd6bfa1c79d16e3bc5e512e5d120184413c93
7
- data.tar.gz: 44d97c8ea249bbae96158774832e983f88ceb871979b04ab2626aa962705a3e54210e3edf19e235448a623384bc7382b73e854281fa095c18b97c2d73d067918
6
+ metadata.gz: e570ea6d402c540b87f604e84b5df47744a69d6eb99f423069b5cae560e6f8e8cd75102dc7e6c9c8514671099a00cd7596a6c2f4a0ab551857684b3cf27f2c02
7
+ data.tar.gz: 34c158ab5216a2c6a8c9fc930f6f0042fc19b05a71fe3659e4ebd06dc91094effb18c8c36c1c01c160d32d2cc306a6957496c6bf9d3f78553e8cfe5dbf939a0d
checksums.yaml.gz.sig CHANGED
Binary file
@@ -1,16 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2025, by Samuel Williams.
4
+ # Copyright, 2019-2026, by Samuel Williams.
5
5
  # Copyright, 2020, by Simon Perepelitsa.
6
6
  # Copyright, 2024, by Thomas Morgan.
7
7
  # Copyright, 2025, by Jean Boussier.
8
+ # Copyright, 2026, by William T. Nelson.
8
9
 
9
10
  require "console/logger"
10
11
 
11
12
  require "async"
12
13
  require "async/semaphore"
13
14
 
15
+ require "set"
14
16
  require "thread"
15
17
 
16
18
  module Async
@@ -26,9 +28,9 @@ module Async
26
28
  #
27
29
  # @parameter constructor [Proc] A block which creates a new resource.
28
30
  # @parameter limit [Integer | Nil] The maximum number of resources that this pool can have at any given time. If nil, the pool can have an unlimited number of resources.
29
- # @parameter concurrency [Integer] The maximum number of concurrent tasks that can be creating a new resource.
31
+ # @parameter concurrency [Integer] The maximum number of concurrent tasks that can be creating a new resource. Defaults to 1 to ensure the pool limit is enforced. Higher values may result in more resources being created than the limit under high load.
30
32
  # @parameter policy [Policy] The pool policy.
31
- def initialize(constructor, limit: nil, concurrency: (limit || 1), policy: nil, tags: nil)
33
+ def initialize(constructor, limit: nil, concurrency: 1, policy: nil, tags: nil)
32
34
  @constructor = constructor
33
35
  @limit = limit
34
36
 
@@ -40,12 +42,12 @@ module Async
40
42
 
41
43
  @tags = tags
42
44
 
43
- # All available resources:
44
- @resources = {}
45
+ # All allocated resources. Each resource is tracked by identity, irrespective of any value equality it may define:
46
+ @resources = {}.compare_by_identity
45
47
 
46
- # Resources which may be available to be acquired:
47
- # This list may contain false positives, or resources which were okay but have since entered a state which is unusuable.
48
- @available = []
48
+ # Resources which may be available to be acquired. Resources are compared by identity and acquired in insertion order. Adding an existing resource preserves its position; a fully utilized resource is removed and reinserted at the end when capacity becomes available again.
49
+ # This set may contain false positives, or resources which were okay but have since entered a state which is unusable.
50
+ @available = Set.new.compare_by_identity
49
51
 
50
52
  # Used to signal when a resource has been released:
51
53
  @mutex = Thread::Mutex.new
@@ -165,16 +167,31 @@ module Async
165
167
 
166
168
  # Make the resource resources and let waiting tasks know that there is something resources.
167
169
  def release(resource)
168
- processed = false
170
+ unless usage = decrement_usage(resource)
171
+ return false
172
+ end
169
173
 
170
- # A resource that is not good should also not be reusable.
171
174
  if resource.reusable?
172
- processed = reuse(resource)
175
+ @available.add(resource)
176
+ else
177
+ # The resource must not be acquired again, but it cannot be retired until all existing users have released it:
178
+ @available.delete(resource)
179
+
180
+ if usage.zero?
181
+ retire(resource)
182
+ resource = nil
183
+ return true
184
+ end
173
185
  end
174
186
 
187
+ @mutex.synchronize{@condition.broadcast}
188
+
175
189
  # @policy.released(self, resource)
190
+
191
+ resource = nil
192
+ return true
176
193
  ensure
177
- retire(resource) unless processed
194
+ retire(resource) if resource
178
195
  end
179
196
 
180
197
  # Drain the pool, closing all resources.
@@ -234,7 +251,7 @@ module Async
234
251
  def retire(resource)
235
252
  Console.debug(self){"Retire #{resource}"}
236
253
 
237
- @resources.delete(resource)
254
+ return false unless @resources.delete(resource)
238
255
 
239
256
  resource.close
240
257
 
@@ -281,27 +298,6 @@ module Async
281
298
  # @resources.count{|resource, usage| usage == 0}
282
299
  # end
283
300
 
284
- def reuse(resource)
285
- Console.debug(self){"Reuse #{resource}"}
286
-
287
- usage = @resources[resource]
288
-
289
- if usage.nil? || usage.zero?
290
- raise "Trying to reuse unacquired resource: #{resource}!"
291
- end
292
-
293
- # If the resource was fully utilized, it now becomes available:
294
- if usage == resource.concurrency
295
- @available.push(resource)
296
- end
297
-
298
- @resources[resource] = usage - 1
299
-
300
- @mutex.synchronize{@condition.broadcast}
301
-
302
- return true
303
- end
304
-
305
301
  def wait_for_resource
306
302
  # If we fail to create a resource (below), we will end up waiting for one to become resources.
307
303
  until resource = available_resource
@@ -321,7 +317,7 @@ module Async
321
317
 
322
318
  # Make the resource available if it can be used multiple times:
323
319
  if resource.concurrency > 1
324
- @available.push(resource)
320
+ @available.add(resource)
325
321
  end
326
322
  end
327
323
 
@@ -340,12 +336,23 @@ module Async
340
336
 
341
337
  return resource
342
338
  rescue Exception
343
- reuse(resource) if resource
339
+ release(resource) if resource
344
340
  raise
345
341
  end
346
342
 
347
343
  private
348
344
 
345
+ def decrement_usage(resource)
346
+ usage = @resources[resource]
347
+ return unless usage
348
+
349
+ if usage.zero?
350
+ raise "Trying to reuse unacquired resource: #{resource}!"
351
+ end
352
+
353
+ return @resources[resource] = usage - 1
354
+ end
355
+
349
356
  # Acquire an existing resource with zero usage.
350
357
  # If there are resources that are in use, wait until they are released.
351
358
  def acquire_existing_resource
@@ -362,24 +369,24 @@ module Async
362
369
  end
363
370
 
364
371
  def acquire_or_create_resource
365
- while resource = @available.last
372
+ while resource = @available.first
366
373
  if usage = @resources[resource] and usage < resource.concurrency
367
374
  if resource.viable?
368
375
  usage = (@resources[resource] += 1)
369
376
 
370
377
  if usage == resource.concurrency
371
378
  # The resource is used up to it's limit:
372
- @available.pop
379
+ @available.delete(resource)
373
380
  end
374
381
 
375
382
  return resource
376
383
  else
377
- retire(resource)
378
- @available.pop
384
+ @available.delete(resource)
385
+ retire(resource) if usage.zero?
379
386
  end
380
387
  else
381
388
  # The resource has been removed already, so skip it and remove it from the availability list.
382
- @available.pop
389
+ @available.delete(resource)
383
390
  end
384
391
  end
385
392
 
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2025, by Samuel Williams.
4
+ # Copyright, 2019-2026, by Samuel Williams.
5
5
 
6
6
  module Async
7
7
  module Pool
8
- VERSION = "0.11.1"
8
+ VERSION = "0.12.0"
9
9
  end
10
10
  end
data/license.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # MIT License
2
2
 
3
- Copyright, 2019-2025, by Samuel Williams.
3
+ Copyright, 2019-2026, by Samuel Williams.
4
4
  Copyright, 2020, by Simon Perepelitsa.
5
5
  Copyright, 2021, by Olle Jonsson.
6
6
  Copyright, 2024, by Thomas Morgan.
7
7
  Copyright, 2025, by Jean Boussier.
8
+ Copyright, 2026, by William T. Nelson.
8
9
 
9
10
  Permission is hereby granted, free of charge, to any person obtaining a copy
10
11
  of this software and associated documentation files (the "Software"), to deal
data/readme.md CHANGED
@@ -14,17 +14,37 @@ Please see the [project documentation](https://socketry.github.io/async-pool/) f
14
14
 
15
15
  Please see the [project releases](https://socketry.github.io/async-pool/releases/index) for all releases.
16
16
 
17
- ### v0.11.0
17
+ ### v0.12.0
18
+
19
+ - Use identity-based tracking for pooled resources and preserve insertion order when selecting available resources.
20
+
21
+ ### v0.11.2
18
22
 
19
23
  ## Contributing
20
24
 
21
25
  We welcome contributions to this project.
22
26
 
23
- 1. Fork it.
27
+ 1. Fork the repository.
24
28
  2. Create your feature branch (`git checkout -b my-new-feature`).
25
- 3. Commit your changes (`git commit -am 'Add some feature'`).
29
+ 3. Commit your changes (`git commit -am 'Add some feature.'`).
26
30
  4. Push to the branch (`git push origin my-new-feature`).
27
- 5. Create new Pull Request.
31
+ 5. Create a new pull request.
32
+
33
+ ### Running Tests
34
+
35
+ To run the test suite:
36
+
37
+ ``` bash
38
+ $ bundle exec sus
39
+ ```
40
+
41
+ ### Making Releases
42
+
43
+ To make a new release:
44
+
45
+ ``` bash
46
+ $ bundle exec bake gem:release:patch # or minor or major
47
+ ```
28
48
 
29
49
  ### Developer Certificate of Origin
30
50
 
data/releases.md CHANGED
@@ -1,3 +1,7 @@
1
1
  # Releases
2
2
 
3
- ## v0.11.0
3
+ ## v0.12.0
4
+
5
+ - Use identity-based tracking for pooled resources and preserve insertion order when selecting available resources.
6
+
7
+ ## v0.11.2
data.tar.gz.sig CHANGED
@@ -1,2 +1,4 @@
1
- %��HJ.�j��a��h^�%|���yt��sM�P\�V]�)?�i��
2
- K<K4җzSVk ����b5�ş�$�����&`V��:0���ao���o<��bB�!>�7m id��5�����2vٲ�󖨄V��$7oi\�2蓬���=h�����m�@K\m+��c�p��xM�x��*��j}�%(+��ll�!&y/of9��Nۅ���HĬ�")
1
+ g�_r=GUwx:��]�<r1��W��ef4p�#׫
2
+ BUE b��6�ϵ_�!n�!�K0��X�x�W������|�����u�1�U�p�LL�����'[��,�$�!?��
3
+ �X��������#rq�U��Ci�5���߾!��ӏF���Rس�N?,�G��7�\�)(�4��=F�5���m�񖶼Z���B/7B�8h�&v~�]���Yw�.�K��s'(� �b�??�,�g�~�D�R[-K���#!�
4
+ Ҁ1��=%\F����H����٣h�"@����_߶Έ��ч�2�ϊv�dm"F7
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-pool
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.1
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -9,6 +9,7 @@ authors:
9
9
  - Olle Jonsson
10
10
  - Simon Perepelitsa
11
11
  - Thomas Morgan
12
+ - William T. Nelson
12
13
  bindir: bin
13
14
  cert_chain:
14
15
  - |
@@ -72,6 +73,8 @@ homepage: https://github.com/socketry/async-pool
72
73
  licenses:
73
74
  - MIT
74
75
  metadata:
76
+ bug_tracker_uri: https://github.com/socketry/async-pool/issues
77
+ changelog_uri: https://github.com/socketry/async-pool/blob/main/releases.md
75
78
  documentation_uri: https://socketry.github.io/async-pool/
76
79
  funding_uri: https://github.com/sponsors/ioquatix/
77
80
  source_code_uri: https://github.com/socketry/async-pool.git
@@ -82,14 +85,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
82
85
  requirements:
83
86
  - - ">="
84
87
  - !ruby/object:Gem::Version
85
- version: '3.2'
88
+ version: '3.3'
86
89
  required_rubygems_version: !ruby/object:Gem::Requirement
87
90
  requirements:
88
91
  - - ">="
89
92
  - !ruby/object:Gem::Version
90
93
  version: '0'
91
94
  requirements: []
92
- rubygems_version: 3.6.9
95
+ rubygems_version: 4.0.10
93
96
  specification_version: 4
94
97
  summary: A singleplex and multiplex resource pool for implementing robust clients.
95
98
  test_files: []
metadata.gz.sig CHANGED
Binary file