concurrent-resource-map-0.2.0.0: Concurrent resource map

Safe HaskellSafe
LanguageHaskell2010

Data.ConcurrentResourceMap

Synopsis

Documentation

data ConcurrentResourceMap m v Source #

A map of shared resources r keyed by k.

class ResourceMap m where Source #

Resource maps should implement this small set of operations that we expect maps to have.

This allows you to use whatever fast underlying map type you'd like, depending on your resources.

Associated Types

type Key m :: * Source #

Methods

empty :: m v Source #

delete :: Key m -> m v -> m v Source #

insert :: Key m -> v -> m v -> m v Source #

lookup :: Key m -> m v -> Maybe v Source #

newResourceMap :: ResourceMap m => IO (ConcurrentResourceMap m r) Source #

Create an empty resource map.

withInitialisedResource Source #

Arguments

:: ResourceMap m 
=> ConcurrentResourceMap m r

Resource map. Create with newResourceMap.

-> Key m

Key for the resource. This allows you to have many of the same type of resource but separated: for example, one group of threads could be holding onto a logging handle to syslog while another could be holding a handle to a file.

-> (r -> IO ())

Destroy the resource if it was initialised. Ran by last alive user when it's exiting. Unlike initialisation, this _is_ ran in masked context. If this action fails (by throwing an exception itself), the resource will be assumed to be uninitialised and the exception will be re-thrown.

Therefore, if your cleanup can fail in a way that you have to know about/recover from, you should catch exceptions coming out out withSharedResource. As you get reference to the resource in the act, you're able to store it/monitor it yourself and decide to take any appropriate actions in the future such as blocking other threads from running initialisation again until you've cleaned up the resource yourself.

-> (Maybe r -> IO a)

Run an action with the resource. Note that the availability of this resource only ensures that the user-given initialisers/destructors have been ran appropriate number of times: it of course makes no guarantees as to what the resource represents. For example, if it's a ProcessHandle or a database connection, there's no guarantee that the process is alive or that the database connection is still available. For resources that can dynamically fail, you should implement some sort of monitoring yourself.

-> IO a 

This is like withSharedResource but will only execute the user action if the resource already exists. This is useful if you create your resources in one place but would like to use them conditionally in another place if they are still alive.

Action is given Nothing if the resource does not exist or is not initialised.

withSharedResource Source #

Arguments

:: ResourceMap m 
=> ConcurrentResourceMap m r

Resource map. Create with newResourceMap.

-> Key m

Key for the resource. This allows you to have many of the same type of resource but separated: for example, one group of threads could be holding onto a logging handle to syslog while another could be holding a handle to a file.

-> IO r

Initialise resource. Only ran if the resource is not yet initialised. Does not run in masked context so if you need to stop async exceptions, you should use mask yourself. If the action fails (throws an exception), the user fails and we enter cleanup.

-> (r -> IO ())

Destroy the resource if it was initialised. Ran by last alive user when it's exiting. Unlike initialisation, this _is_ ran in masked context. If this action fails (by throwing an exception itself), the resource will be assumed to be uninitialised and the exception will be re-thrown.

Therefore, if your cleanup can fail in a way that you have to know about/recover from, you should catch exceptions coming out out withSharedResource. As you get reference to the resource in the act, you're able to store it/monitor it yourself and decide to take any appropriate actions in the future such as blocking other threads from running initialisation again until you've cleaned up the resource yourself.

-> (r -> IO a)

Run an action with the initialised resource. Note that the availability of this resource only ensures that the user-given initialisers/destructors have been ran appropriate number of times: it of course makes no guarantees as to what the resource represents. For example, if it's a ProcessHandle or a database connection, there's no guarantee that the process is alive or that the database connection is still available. For resources that can dynamically fail, you should implement some sort of monitoring yourself.

-> IO a 

Use a resource that can be accessed concurrently via multiple threads but is only initialised and destroyed on as-needed basis. If number of users falls to 0, the resource is destroyed. If a new user joins and resource is not available, it's created.

Calls to withSharedResource can even be nested if you need access to resources with different keys in the same map. Calling withSharedResource in a nested matter on same resource key should have no real adverse effects either.