Class: Async::Pool::Controller
- Inherits:
-
Object
- Object
- Async::Pool::Controller
- Defined in:
- lib/async/pool/controller.rb
Overview
A resource pool controller.
Instance Attribute Summary collapse
- #all allocated resources, and their associated usage.(allocatedresources) ⇒ Object readonly
-
#constructor ⇒ Object
readonly
Returns the value of attribute constructor.
-
#limit ⇒ Object
Returns the value of attribute limit.
-
#policy ⇒ Object
Returns the value of attribute policy.
-
#resources ⇒ Object
readonly
Returns the value of attribute resources.
-
#tags ⇒ Object
Returns the value of attribute tags.
- #The constructor used to create new resources.(constructorusedtocreatenewresources.) ⇒ Object readonly
- #The maximum number of concurrent tasks that can be creating a new resource.(maximumnumberofconcurrenttasksthatcanbecreatinganewresource.) ⇒ Object readonly
- #The maximum number of resources that this pool can have at any given time.(maximumnumberofresourcesthatthispoolcanhaveatanygiventime.) ⇒ Object readonly
- #The name of the pool.(nameofthepool.) ⇒ Object readonly
Class Method Summary collapse
-
.wrap(**options, &block) ⇒ Object
Create a new resource pool, using the given block to create new resources.
Instance Method Summary collapse
-
#acquire ⇒ Object
Acquire a resource from the pool.
-
#active? ⇒ Boolean
Whether the pool has any active resources.
-
#as_json ⇒ Object
Generate a JSON representation of the pool.
-
#available? ⇒ Boolean
Whether there are available resources, i.e.
-
#busy? ⇒ Boolean
Whether there are resources which are currently in use.
-
#close ⇒ Object
Drain the pool, clear all resources, and stop the gardener.
- #concurrency ⇒ Object
-
#concurrency=(value) ⇒ Object
Set the maximum number of concurrent tasks that can be creating a new resource.
-
#drain ⇒ Object
Drain the pool, closing all resources.
-
#empty? ⇒ Boolean
Whether the pool is empty.
-
#initialize(constructor, limit: nil, concurrency: 1, policy: nil, tags: nil) ⇒ Controller
constructor
Create a new resource pool.
-
#prune(retain = 0) ⇒ Object
Retire (and close) all unused resources.
-
#release(resource) ⇒ Object
Make the resource resources and let waiting tasks know that there is something resources.
-
#retire(resource) ⇒ Object
Retire a specific resource.
-
#size ⇒ Object
The number of resources in the pool.
- #The pool policy.=(poolpolicy. = (value)) ⇒ Object
-
#to_json ⇒ Object
Generate a JSON representation of the pool.
-
#to_s ⇒ Object
Generate a human-readable representation of the pool.
-
#wait ⇒ Object
deprecated
Deprecated.
Use #wait_until_free instead.
-
#wait_until_free ⇒ Object
Wait until the pool is not busy (no resources in use).
Constructor Details
#initialize(constructor, limit: nil, concurrency: 1, policy: nil, tags: nil) ⇒ Controller
Create a new resource pool.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/async/pool/controller.rb', line 33 def initialize(constructor, limit: nil, concurrency: 1, policy: nil, tags: nil) @constructor = constructor @limit = limit # This semaphore is used to limit the number of concurrent tasks which are creating new resources. @guard = Async::Semaphore.new(concurrency) @policy = policy @gardener = nil @tags = # All allocated resources. Each resource is tracked by identity, irrespective of any value equality it may define: @resources = {}.compare_by_identity # 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. # This set may contain false positives, or resources which were okay but have since entered a state which is unusable. @available = Set.new.compare_by_identity # Used to signal when a resource has been released: @mutex = Thread::Mutex.new @condition = Thread::ConditionVariable.new end |
Instance Attribute Details
#all allocated resources, and their associated usage.(allocatedresources) ⇒ Object (readonly)
101 |
# File 'lib/async/pool/controller.rb', line 101 attr :resources |
#constructor ⇒ Object (readonly)
Returns the value of attribute constructor.
58 59 60 |
# File 'lib/async/pool/controller.rb', line 58 def constructor @constructor end |
#limit ⇒ Object
Returns the value of attribute limit.
61 62 63 |
# File 'lib/async/pool/controller.rb', line 61 def limit @limit end |
#policy ⇒ Object
Returns the value of attribute policy.
98 99 100 |
# File 'lib/async/pool/controller.rb', line 98 def policy @policy end |
#resources ⇒ Object (readonly)
Returns the value of attribute resources.
101 102 103 |
# File 'lib/async/pool/controller.rb', line 101 def resources @resources end |
#tags ⇒ Object
Returns the value of attribute tags.
104 105 106 |
# File 'lib/async/pool/controller.rb', line 104 def @tags end |
#The constructor used to create new resources.(constructorusedtocreatenewresources.) ⇒ Object (readonly)
58 |
# File 'lib/async/pool/controller.rb', line 58 attr :constructor |
#The maximum number of concurrent tasks that can be creating a new resource.(maximumnumberofconcurrenttasksthatcanbecreatinganewresource.) ⇒ Object (readonly)
88 89 90 |
# File 'lib/async/pool/controller.rb', line 88 def concurrency @guard.limit end |
#The maximum number of resources that this pool can have at any given time.(maximumnumberofresourcesthatthispoolcanhaveatanygiventime.) ⇒ Object (readonly)
61 |
# File 'lib/async/pool/controller.rb', line 61 attr_accessor :limit |
#The name of the pool.(nameofthepool.) ⇒ Object (readonly)
104 |
# File 'lib/async/pool/controller.rb', line 104 attr_accessor :tags |
Class Method Details
.wrap(**options, &block) ⇒ Object
Create a new resource pool, using the given block to create new resources.
23 24 25 |
# File 'lib/async/pool/controller.rb', line 23 def self.wrap(**, &block) self.new(block, **) end |
Instance Method Details
#acquire ⇒ Object
Acquire a resource from the pool. If a block is provided, the resource will be released after the block has been executed.
156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/async/pool/controller.rb', line 156 def acquire resource = wait_for_resource return resource unless block_given? begin yield resource ensure release(resource) end end |
#active? ⇒ Boolean
Whether the pool has any active resources.
112 113 114 |
# File 'lib/async/pool/controller.rb', line 112 def active? !@resources.empty? end |
#as_json ⇒ Object
Generate a JSON representation of the pool.
73 74 75 76 77 78 79 80 |
# File 'lib/async/pool/controller.rb', line 73 def as_json(...) { limit: @limit, concurrency: @guard.limit, usage: @resources.size, availability_summary: self.availability_summary, } end |
#available? ⇒ Boolean
Whether there are available resources, i.e. whether #acquire can reuse an existing resource.
126 127 128 |
# File 'lib/async/pool/controller.rb', line 126 def available? @available.any? end |
#busy? ⇒ Boolean
Whether there are resources which are currently in use.
117 118 119 120 121 122 123 |
# File 'lib/async/pool/controller.rb', line 117 def busy? @resources.collect do |_, usage| return true if usage > 0 end return false end |
#close ⇒ Object
Drain the pool, clear all resources, and stop the gardener.
208 209 210 211 212 213 |
# File 'lib/async/pool/controller.rb', line 208 def close self.drain @available.clear @gardener&.stop end |
#concurrency ⇒ Object
88 89 90 |
# File 'lib/async/pool/controller.rb', line 88 def concurrency @guard.limit end |
#concurrency=(value) ⇒ Object
Set the maximum number of concurrent tasks that can be creating a new resource.
93 94 95 |
# File 'lib/async/pool/controller.rb', line 93 def concurrency= value @guard.limit = value end |
#drain ⇒ Object
Drain the pool, closing all resources.
198 199 200 201 202 203 204 205 |
# File 'lib/async/pool/controller.rb', line 198 def drain Console.debug(self, "Draining pool...", size: @resources.size) # Enumerate all existing resources and retire them: while resource = acquire_existing_resource retire(resource) end end |
#empty? ⇒ Boolean
Whether the pool is empty.
151 152 153 |
# File 'lib/async/pool/controller.rb', line 151 def empty? @resources.empty? end |
#prune(retain = 0) ⇒ Object
Retire (and close) all unused resources. If a block is provided, it should implement the desired functionality for unused resources.
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/async/pool/controller.rb', line 218 def prune(retain = 0) unused = [] # This code must not context switch: @resources.each do |resource, usage| if usage.zero? unused << resource end end # It's okay for this to context switch: unused.each do |resource| if block_given? yield resource else retire(resource) end break if @resources.size <= retain end # Update availability list: @available.clear @resources.each do |resource, usage| if usage < resource.concurrency and resource.reusable? @available << resource end end return unused.size end |
#release(resource) ⇒ Object
Make the resource resources and let waiting tasks know that there is something resources.
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/async/pool/controller.rb', line 169 def release(resource) unless usage = decrement_usage(resource) return false end if resource.reusable? @available.add(resource) else # The resource must not be acquired again, but it cannot be retired until all existing users have released it: @available.delete(resource) if usage.zero? retire(resource) resource = nil return true end end @mutex.synchronize{@condition.broadcast} # @policy.released(self, resource) resource = nil return true ensure retire(resource) if resource end |
#retire(resource) ⇒ Object
Retire a specific resource.
251 252 253 254 255 256 257 258 259 260 261 |
# File 'lib/async/pool/controller.rb', line 251 def retire(resource) Console.debug(self){"Retire #{resource}"} return false unless @resources.delete(resource) resource.close @mutex.synchronize{@condition.broadcast} return true end |
#size ⇒ Object
The number of resources in the pool.
107 108 109 |
# File 'lib/async/pool/controller.rb', line 107 def size @resources.size end |
#The pool policy.=(poolpolicy. = (value)) ⇒ Object
98 |
# File 'lib/async/pool/controller.rb', line 98 attr_accessor :policy |
#to_json ⇒ Object
Generate a JSON representation of the pool.
83 84 85 |
# File 'lib/async/pool/controller.rb', line 83 def to_json(...) as_json.to_json(...) end |
#to_s ⇒ Object
Generate a human-readable representation of the pool.
64 65 66 67 68 69 70 |
# File 'lib/async/pool/controller.rb', line 64 def to_s if @resources.empty? "\#<#{self.class}(#{usage_string})>" else "\#<#{self.class}(#{usage_string}) #{availability_summary.join(';')}>" end end |
#wait ⇒ Object
Use #wait_until_free instead.
Wait until a pool resource has been freed.
132 133 134 135 136 |
# File 'lib/async/pool/controller.rb', line 132 def wait @mutex.synchronize do @condition.wait(@mutex) end end |
#wait_until_free ⇒ Object
Wait until the pool is not busy (no resources in use).
139 140 141 142 143 144 145 146 147 148 |
# File 'lib/async/pool/controller.rb', line 139 def wait_until_free @mutex.synchronize do if busy? yield self if block_given? # Wait until the pool is not busy: @condition.wait(@mutex) while busy? end end end |