(* $Id: netmcore_condition.mli 1715 2012-02-20 14:47:01Z gerd $ *)
(** Condition variables *)
(** Condition variables are here defined as values that reside in shared heaps
({!Netmcore_heap}), for example in the header field of
{!Netmcore_array} or somewhere else in heap-allocated
data structures.
In order to ensure that the condition variable is in the heap, the
special function [create_condition] must be used to initialize it
there. As [create_condition] requires a mutator as argument, this is
only possible by calling [create_condition] from the callback of
{!Netmcore_heap.modify}.
Condition variables are special values, and cannot be copied or moved.
Condition variables are implemented on top of semaphores. Compared to
the [pthreads] version of condition variables, the user needs here to
allocate special [wait_entry] slots, one for each process. An entry
can be used for all condition variables a process needs to wait for.
(Actually, such entries also exist in typical [pthreads] implementations,
but are hidden from the user in the thread control block. We just
don't have here a place where we could allocate process-specific
shared memory.)
Since Ocamlnet-3.5, there are also special wait entries [wait_entry_e]
which can be used to wait from within a running Unixqueue. For each
such wait entry, however, a named pipe needs to be allocated.
*)
type condition
(** The condition variable *)
type wait_entry
(** Each process that wants to [wait] needs a [wait_entry]. These entries
can be used for several condition variables, so typically each process
has only one entry for each heap.
*)
type wait_entry_e
(** A special kind of [wait_entry] for intergration into an event
loop
*)
type wait_set
(** A set of [wait_entry], for easier management. This set can e.g. be
stored side by side with the condition variable(s). It is important
that the [wait_set] resides in the same shared heap as the
condition variable.
*)
val dummy_condition : unit -> condition
(** A dummy condition is non-functional, but can be used to put something
into [condition]-typed variables
*)
val dummy_wait_set : unit -> wait_set
(** A dummy [wait_set] is non-functional, but can be used to put something
into [wait_set]-typed variables
*)
val create_condition : Netmcore_heap.mutator -> condition
(** [create m]: Creates a condition variable, and
pushes it to the heap, using the mutator [m].
After being pushed to the heap, the variable can be used. It is
nonsense to copy it outside the heap.
*)
val create_wait_set : Netmcore_heap.mutator -> wait_set
(** Creates a [wait_set] in the heap designated by the mutator *)
val alloc_wait_entry : Netmcore_heap.mutator -> wait_set -> wait_entry
(** Allocates a [wait_entry] *)
val free_wait_entry : Netmcore_heap.mutator -> wait_set -> wait_entry -> unit
(** Frees a [wait_entry] *)
val alloc_wait_entry_e : Netmcore_heap.mutator -> wait_set -> string ->
wait_entry_e
(** [alloc_wait_entry_e mut set filename]: Allocates a new wait entry
with notification via named pipe. The [filename] must refer to an
existing named pipe.
*)
val free_wait_entry_e : Netmcore_heap.mutator -> wait_set -> wait_entry_e ->
unit
(** Frees a [wait_entry_e]. The named pipe is deleted. *)
val wait : wait_entry -> condition -> Netmcore_mutex.mutex -> unit
(** [wait we c m] atomically unlocks the mutex [m] and suspends the
calling process on the condition variable [c]. The process will
restart after the condition variable [c] has been signalled.
The mutex [m] is locked again before [wait] returns.
At the time of calling, the [wait_entry] [we] must not be used to
manage another [wait]. When allocating a separate [wait_entry]
per process this problem does not occur.
*)
val wait_e : ?debug_name:string ->
wait_entry_e -> condition -> Netmcore_mutex.mutex ->
Unixqueue.event_system ->
(unit -> 'a Uq_engines.engine) ->
'a Uq_engines.engine
(** Like [wait], but the suspension is done by waiting on a
named pipe event (i.e. "nonblocking suspension"):
[wait_e we c m esys cont] atomically unlocks the mutex [m] and suspends
the calling engine on the condition variable [c]. The engine will
restart after the condition variable [c] has been signalled.
The mutex [m] is locked again, at which time [cont] is called.
The result of [cont()] is the result of [wait_e].
At the time of calling, the [wait_entry_e] [we] must not be used to
manage another [wait_e]. When allocating a separate [wait_entry_e]
per process (or thread within the process) this problem does not occur.
*)
(* UNCLEAR what this means:
Another important restriction: There must be at most one
active [wait_e] per process or thread.
*)
val signal : condition -> unit
(** [signal c] restarts one of the processes waiting on the
condition variable [c].
*)
val broadcast : condition -> unit
(** [broadcast c] restarts all processes waiting on the
condition variable [c].
*)
val pipe_name : wait_entry_e -> string
(** Returns the name of the pipe *)
val destroy_condition : condition -> unit
val destroy_wait_set : wait_set -> unit
(** Destroys these objects *)
module Debug : sig
val enable : bool ref
end