(* $Id$ *)

(** Shared arrays *)

(** This type of array is to some degree comparable with [Array], but
    there are a few extensions:
    - There is a so-called header. The header can have any type, and
      is typically used for managing concurrent access (e.g. mutexes).
      The header also lives in shared memory.
    - The arrays can be grown in size
 *)

type ('e,'h) sarray
  (** Arrays where the elements have type ['e] and the header has
      type ['h]
   *)

type ('e,'h) sarray_descr
  (** The marshallable descriptor of a shared array *)

val create : Netmcore.res_id -> 'e array -> 'h -> ('e,'h) sarray
  (** [create pool_id a h]:
      Creates a shared array by deeply copying a normal array [a]
      and using the copy of [h] as header
   *)

val make : Netmcore.res_id -> int -> 'e -> 'h -> ('e,'h) sarray
  (** [make pool_id n x h]:
      Creates a shared array of the passed number of elements, 
      copies the element [x], and initializes each element of the new array
      with the single copy of [x]. The value [h] is copied and used
      as header.
   *)

val init : Netmcore.res_id -> int -> (int -> 'e) -> 'h -> ('e,'h) sarray
  (** [init pool_id n f h]:
      Creates a shared array of the passed number of elements, 
      and for getting the element at position [k] the function
      [f k] is run, and the copy of the result is written to the
      position. The header is set to the copy of [h].
   *)

val grow : ('e,_) sarray -> int -> 'e -> unit
  (** [grow sa n x]: Grows the array to [n] elements. The new elements
      are initialized to a (single) copy of [x].

      If [n] is smaller than the current length, the function will do
      nothing, and keep the length.
   *)

val set : ('e,_) sarray -> int -> 'e -> unit
  (** [set sa k x]: Sets the [k-th] element of the array [sa] to a
      deep copy of [x].
   *)

val get_ro : ('e,_) sarray -> int -> 'e
  (** [get_ro sa k]: Gets the [k]-th element of the shared array [sa].
      Note that there is no guarantee that this value still exists if
      it is returned, and a parallely running [set] changes this element.
      If such values are accessed the program may crash!
   *)

val get_p : ('e,_) sarray -> int -> ('e -> 'a) -> 'a
  (** [get_p sa k f]: Gets the [k]-th element of the shared array [sa]
      and call [f] with this element, and returns the result of [f].
      During the execution of [f] the requested element cannot be
      garbage collected.
   *)

val get_c : ('e,_) sarray -> int -> 'e
  (** [get_c sa k]: Gets a copy of the [k]-th element of the shared array
      [sæ]
   *)

val length : (_,_) sarray -> int
  (** Returns the length *)

val header : (_,'h) sarray -> 'h
  (** Returns the header *)

val deref : ('e,_) sarray -> 'e array
  (** Returns the raw array in shared memory for unprotected access *)

val heap : (_,_) sarray -> Obj.t Netmcore_heap.heap
  (** Return the backing heap structure *)

val descr_of_sarray : ('e,'h) sarray -> ('e,'h) sarray_descr
  (** Returns the descriptor *)

val sarray_of_descr : Netmcore.res_id -> ('e,'h) sarray_descr -> ('e,'h) sarray
  (** Look up the buffer for this descriptor *)



(** {3 Mutating header fields}

    Special care has to be taken when mutating header fields. The header
    must completely live in the same heap. For adding new values, one
    has to use {!Netmcore_heap.modify}. Example for a header of type:

    {[
    type header =
      { mutable n : int;
        mutable name : string
      }
    ]}

    Here, the field [n] can be directly assigned because an [int]
    is always an unboxed value. So,

    {[
    h.n <- new_value
    ]}

    is legal. However, strings are heap-allocated. For an assignment
    to [name] we need to use {!Netmcore_heap.modify}, as in

    {[
    Netmcore_heap.modify
      (Netmcore_array.heap sa)
      (fun mutator ->
        h.name <- Netmcore_heap.add mutator new_value
      )
    ]}

    The function {!Netmcore_heap.add} pushes a copy of the [new_value]
    to the heap, and this allows us to do the assignment.

    During {!Netcore_heap.modify} certain operations are prohibited
    because they would cause a deadlock:
    - [grow]
    - [set]
    - [get_p]
    - [get_c]

    (This may be relaxed in a future version.)
 *)
