Library
Module
Module type
Parameter
Class
Class type
A resource that can be acquire
d to obtain Handle
s that should be release
d by the user later. Most resources can be acquired multiple times by multiple concurrent async jobs. What exactly happens when you acquire
depends on the specific resource.
Possible examples of resources:
- File (acquire opens a file)
- Tcp endpoints (acquire creates a connection)
- Entitlements (the motivating use case)
- Semaphore (acquire is blocking and takes a lock)
- Slot (like semaphore, but acquire is non-blocking and fails)
Definitions:
It may be useful think to think about resources and dependencies between them in terms of *activation*s. Activation of resource x
is a pair of events (and the corresponding period of time): (i) x
has been successfully acquired (to obtain a handle h
) and (ii) Handle.release h
has been called.
We say that activation a
is enclosed into activation b
whenever a
begins before b
and a
ends after b
module Raw : sig ... end
type ('a, 'e) t = ('a, 'e) Raw.t
val create :
acquire:(unit -> ('a, 'e) Core.Result.t Async_kernel.Deferred.t) ->
release:('a -> unit Async_kernel.Deferred.t) ->
('a, 'e) t
val with_ :
('a, 'e) t ->
f:('a -> 'b Async_kernel.Deferred.t) ->
('b, 'e) Core.Result.t Async_kernel.Deferred.t
Access a resource without having to deal with Handle.t explicitly. The resource is acquired before f
is called and released after f
returns a result or raises an error to the enclosing monitor.
f
should not use 'a
after it raises or returns, whichever happens first.
Bind corresponds to resource dependency: when acquiring x >>= f
, resource x
will be acquired, then f
is going to be evaluated, then the result of f
is going to be acquired. Releases will be done in the opposite order.
include Core.Monad.S2 with type ('a, 'e) t := ('a, 'e) t
module Let_syntax : sig ... end
module Monad_infix : sig ... end
val return : 'a -> ('a, _) t
val fail : 'e -> ('a, 'e) t
The idea is the following: If you try to acquire a shared resource that's already been acquired, but not yet released then, instead of acquiring it again, you use the value acquired earlier. You only release the underlying resource when all handles to the shared resource get released.
More precisely, if y = shared x
and x
is exclusively used here then: (i) every activation of y
is enclosed into an activation of x
; (ii) at any time there is at most one activation of x
; (iii) activations of x
are as short as possible otherwise.
Beware shared
is not referentially transparent in that acquire (shared x)
followed by acquire (shared x)
will acquire x
twice, so you always want to bind the result to a variable: let y = shared x in ... (* acquire y multiple times *)
As an example of what you can do with this, in dart
library shared
lets us:
- Coalesce multiple requests for the same entitlement by the same user into one.
- Only establish up to one connection to dart server per user.
In general, it might be helpful when:
- If acquiring the original resource is costly;
- If acquiring
x
multiple times concurrently is not safe; - If multiple acquirings of
x
would block each other; - <your idea>.
module Memo (Key : Core.Hashable) : sig ... end
val delayed_release : ('a, 'e) t -> delay:Core.Time.Span.t -> ('a, 'e) t
Delay all release
s by the given amount and don't wait for them