Pool
Pool does the housekeeping of managing a limited set of resources which are shared by ‘users’. All resources in the Pool are meant to be the same kind of object.
An example is an audio sketch driven by TensorFlow. We might want to allocate a sound oscillator per detected human body. A naive implementation would be to make an oscillator for each detected body. However, because poses appear/disappear unpredictably, it’s a lot of extra work to maintain the binding between pose and oscillator.
Instead, we might use the Pool to allocate oscillators to poses. This will allow us to limit resources and clean up automatically if they haven’t been used for a while.
Basic usage
Resources can be added manually with addResource()
, or automatically by providing a generate()
function in the Pool options. They can then be accessed via a user key. This is meant to associated with a single ‘user’ of a resource.
To access a resource value, call useValue()
with a userKey. The pool will try to allocate a resource to this particular ‘user’ if it can, or return the same resource the ‘user’ was allocated last time.
First set up the pool with options .
Add resources to the pool
Now the pool is primed with resources and ready to be used.
To request a resource you need to supply an id to associate the resource’s ‘user’ with. For example, if you want to each pointer to have a resource associated with it, you might use the pointer’s id
property. That way, if the same pointer requests a resource it will be given back the resource it had before.
The basic idea is that a user has exclusive control over a resource until it ‘releases’ it, or the Pool claims it back in the event of the pool being depleted.
Do not hold on to the reference to a pool value. Ideally you request it from the Pool whenever it’s used, or at least in the context of a short-running function. This is because the Pool might want to claim it back.
In-action
Manual resources
For example, if we are want to associate sound oscillators with TensorFlow poses. Since pose data has an id
property, we can use that as the ‘user key’.
When we get a chunk of data (which can contain one or more poses), we get an associated oscillator for each pose. If a pose with the same id previously used a resource, it will get that back again. That way we get a consistent mapping of poses and oscillators.
Dynamic resources
In this example, we will generate and dispose Pool resources on-demand. The Pool will create them (with the generate()
function we provide) and automatically free them when they are unused. In this way, the Pool acts as a way of mapping keys to ad-hoc resources.
First we create the Pool, providing generate and free functions which are responsible for creating and destroying resources. Here HTML elements are the resources being managed.
Now we can use resources from the pool, for example assigning a HTML element per key down. In this case, we’re associating the letter pressed with a resource.
This pattern is implemented in the demo below. Resources are allocated based on the letter. So if you press the same letter, you won’t see it twice, since it’s resource stays alive. If you type lots of different characters, older ones will disappear. And the Pool cleans up automatically if a resource hasn’t been used for a short time.
Advanced
Creating
Create a Pool and provide some options
Overview of options, all of which are optional.
option | info |
---|---|
capacity | Maximum number of resources. Defaults to 0, no limit |
capacityPerResource | Maximum number of users per resource. Defaults to 0, no limit |
debug | If true, additional logging will be printed |
free | A function that takes a single value. Call when a resource is removed from pool. Meant for cleaning up a value, where necessary. |
fullPolicy | ’error’ (throws an error when pool is full) or ‘evictOldestUser’, removing oldest user of a resource. Defaults to ‘error’ |
generate | A function that returns a value. Used for generating resources on demand |
resourcesWithoutUsersExpireAfterMs | If provided, an unused resource will be removed after this period |
userExpireAfterMs | If provided, a user will be marked as expired if it hasn’t been updated |
Accessing resources
A resource can be accessed by a user key, returning a PoolUser instance.
As described earlier, the user key is some unique reference for ‘owner’ of that pool resource. The idea is that if the same logical owner accesses the resource again, it always is using the same key. As far as the Pool is concerned, a different key means a different user, thus allocating a different resource.
When using resources managed by the Pool, it is important that all access to them happens via the Pool. Don’t cache references to resources, always access them via use()
or useValue()
.
The PoolUser instance returned by use()
has a disposed event handler. This allows you to be notified if you have lost ownership of a resource. It is also called if the resource itself has been cleaned up.
Resources can be manually released:
When releasing, the resource is freed for use under a different key. If there are no more users of a resource and the Pool option resourcesWithoutUsersExpireAfterMs
is set, the resource will be freed.