(* $Id: netsys_sem.mli 1760 2012-03-30 15:25:09Z gerd $ *)

(** {1 Generic anonymous semaphores} *)

(** This module purely exists to also support "kind of anonymous"
    sempahores on MacOS X (which only has named semaphores). On
    other OS, it is just a wrapper for the functions in
    {!Netsys_posix}.
 *)

val have_anon_semaphores : unit -> bool
  (** Returns [true] if anonymous semaphores are supported on this 
      system, either natively or emulated via named semaphores.
   *)

(** {b Constants.} *)

val sem_value_max : int
  (** The maximum value of a semaphore, but at most [max_int] *)

val sem_size : int
  (** The size of an anonymous semaphore in bytes ([sizeof(sem_t)]) *)

(** {b Types.} *)

type container
  (** The container of the semaphore is the shared memory object *)

type prefix = string
  (** A name starting with a slash. Must not contain further slashes *)

type anon_semaphore

type sem_open_flag = Netsys_posix.sem_open_flag =
  | SEM_O_CREAT
  | SEM_O_EXCL

(** {b Container functions.} *)

val container : prefix -> container
  (** [container prefix]: The prefix shall identify the container uniquely.
      Once can e.g. use the path of the shared memory object. The prefix
      is used to construct names for persistent objects.

      {b Note that containers have kernel persistence! They are not
      automatically deleted when the process finishes. Call [drop]
      to delete containers, or [create_container] to force their
      creation as fresh objects.}

      If the container does not exist yet, it is created. Otherwise the
      container is just opened.
   *)

val create_container : prefix -> container
  (** [create_container prefix]: Like [container], but the container is
      always created. A previous instance is first deleted.
   *)

val prefix : container -> prefix
  (** Return the prefix *)

val drop : container -> unit
  (** Drop the semaphores in this container, and delete the container.

      This function is a no-op if the OS supports anonymous semaphores
      directly (because in this case the deletion of the container will
      automatically destroy the semaphores).
   *)

(** Note that there is a general problem when a container is deleted
    or dropped by a process while it is still being used by another one.
    This will generally not generate errors, but can cause a lot of
    confusion, because the processes will partly access the same
    semaphores, and partly different semaphores.
 *)

val unlink : prefix -> unit
  (** Unlinks the identified container if it exists, and unlinks all
      possible named semaphores.
   *)

(** {b Semaphore functions.} *)

val sem_init : container -> Netsys_types.memory -> int -> bool -> int -> 
                 anon_semaphore
  (** [sem_init cont mem pos pshared init_value]: Initializes the memory
      at position [pos] to [pos + sem_size() - 1] as anonymous semaphore.
      If [pshared] the semaphore is shared between processes. 
      [init_value] is the initial non-negative value (max is 
      [sem_value_max]).
   *)

val sem_destroy : container -> anon_semaphore -> unit
  (** Destroys the anonymous semaphore *)

val as_sem : container -> Netsys_types.memory -> int -> anon_semaphore
  (** [as_sem mem pos]: Interprets the memory at position [pos]
      to [pos + sem_size() - 1] as anonymous semaphore.
      The memory region must already have been initialized.
   *)

val sem_getvalue : anon_semaphore -> int
  (** Returns the value of the semaphore. If the value is bigger than
      what can be represented as [int], an [EINVAL] error is returned.

      The returned value is non-negative - if the underlying POSIX
      function reports a negative value zero is returned instead.

      {b Unavailable on MacOS.}
   *)

val sem_post : anon_semaphore -> unit
  (** Unlocks the semaphore (increases the value by 1) *)

type sem_wait_behavior = Netsys_posix.sem_wait_behavior =
  | SEM_WAIT_BLOCK
  | SEM_WAIT_NONBLOCK

val sem_wait : anon_semaphore -> sem_wait_behavior -> unit
  (** Locks the semaphore (decreases the value by 1). If the semaphore
      value is already zero, and [SEM_WAIT_BLOCK] is given, the function
      waits until another process or thread posts. If [SEM_WAIT_NONBLOCK]
      the error [EAGAIN] is returned.

      [sem_wait] may be interrupted by signals.
   *)

module Debug : sig
  val enable : bool ref
end

(**/**)

val force_emulation : unit -> unit
