(* $Id: netshm.mli 1159 2007-11-29 13:19:21Z gerd $ *) (** Shared memory for O'Caml programs using multi-processing *) (** {1 Shared Memory Descriptors} *) type shm_descr (** A shared memory descriptor refers to a shared memory object. * Such a descriptor must only be opened once for every process. *) type shm_type = [ `POSIX (** POSIX shared memory (system calls [shm_open] and [mmap]) *) | `File (** File-based, but not necessarily shared memory (system call [mmap]) *) ] val supported_types : shm_type list (** The types supported for this OS *) type shm_name = [ `POSIX of string (** This name refers to a POSIX shared memory object. The name * must start with a slash, and must not contain further slashes, * e.g. ["/my_shm"]. *) | `File of string (** This is the name of an arbitrary file used to store the * data. Note that this is not shared memory unless the file * system is RAM-based. *) ] (** A [shm_name] is a name for a shared memory object. *) val shm_type_of_name : shm_name -> shm_type val open_shm : shm_name -> Unix.open_flag list -> int -> shm_descr (** Opens the shared memory object. * * For [POSIX_shm] not all open flags can be specified. The flags * are limited to [O_RDONLY], [O_RDWR], [O_CREAT], [O_EXCL] and * [O_TRUNC]. *) val create_unique_shm : shm_name -> int -> shm_descr (** Create a shared memory object under a name that is derived * from the passed [shm_name]. The actual name can be queried with * [name_of_shm] (below). * * For [POSIX] and [File] names, deriving works by replacing * the 'X' letters in the file name by random digits and letters. * For example, if the name is [File "/my/directory/ocaml.XXXXXX"] * six random characters are generated and replace the 'X' letters * such that the file is unique. * * The integer is the file permission. *) val name_of_shm : shm_descr -> shm_name (** Returns the name of an object *) val close_shm : shm_descr -> unit (** Closes the object. The object remains existent and can be * opened again. *) val unlink_shm : shm_name -> unit (** Removes the name permanently from the system *) val chmod_shm : shm_descr -> int -> unit val chown_shm : shm_descr -> int -> int -> unit (** Set file permission bits, user and group ownership of the object *) type locking_method = [ `No_locking (** Do not use locking at all *) | `Record_locking (** Use record locking as provided by [Unix.lockf]. This type of * locking is compatible with shared memory types [POSIX] and [File]. *) ] (** The locking method is used to ensure that parallel read and write * operations to the memory object do not interfer with each other. * If [No_locking] is selected, such protection is not done - this * is ok if only read accesses occur or if the user can ensure that * never a write access is done in parallel with another access. * The locking method must be compatible with the type of shared memory. *) val best_locking_method : shm_type -> locking_method (** Return the best locking method other than [No_locking] *) (** {1 Shared Memory Tables} *) (** This is a quite basic data structure implemented for shared memory: * hash tables with [int32] as key and one-dimensional [int32] * bigarray as values. *) (** The semantics resembles the [Hashtbl] of stdlib *) type shm_table type int32_array = (int32, Bigarray.int32_elt, Bigarray.c_layout) Bigarray.Array1.t exception Corrupt_file of string (** Raised when a violation of the object format is detected *) exception Deadlock (** Raised when a deadlock situation was detected. Deadlocks can occur * when the [group] function is used *) val manage : ?pagesize:int -> ?init:int -> locking_method -> shm_descr -> shm_table (** Starts managing an open shared memory object as [shm_table]. If * the object is empty, it is automatically enlarged to the * minimum size and initialized. If the object is non-empty it is * expected that it already contains a valid [shm_table] structure. * * The object automatically grows in size when new elements are * added to the object. By removing elements, however, the * object is never shrinked. Unused memory is held back for later * reallocation by the same [shm_table]. * * By default, the table uses a page size of 256 bytes. The page size is * the unit of memory allocation. The parameter [pagesize] overrides * this default. The page size must be at least 160 and divisible by 4. * The page size must be the same when the table was created. * * By passing [init] it is enforced that the table is re-initialized * (deleted and again created). The argument of [init] is a hint * for the number of elements the table will contain. The data * structure is then created such that this many elements can be added * without needing expensive data reorganization. * * Special note for using [shm_table] with multiple processes: Every * process must create its own [shm_descr], and every process must * call [manage] to manage it. It is not sufficient to just fork * a new process, and to keep using the already existing * [shm_descr] or [shm_table] in the subprocess. (This doesn't work * because the underlying file descriptor would be shared.) *) val group : shm_table -> ('a -> 'b ) -> 'a -> 'b (** Execute a sequence of operations in a group: * * {[ * let r = * group table * (fun arg -> * operation1; operation2; ...) * arg * ]} * * Operations can be any reading or writing functions from below. The * effect is that the locking requirements of the operations are merged, * so that no operation of another process can interfer with the grouped * sequence. Note, however, that this gives no real atomicity as there * is no way to roll back half-executed sequences. * * Groups can be nested. * * An example of [group] is to set a binding depending on the previous * value of the binding. Here, we add 1: * * {[ * let add_one table = * group table * (fun key -> * let ba = * try find table key * with Not_found -> * Bigarray.Array1.create Bigarray.int32 Bigarray.c_layout 1 in * ba.{ 0 } <- Int32.succ ba.{ 0 }; * replace table key ba * ) * ]} * * Grouping protects the update from modifications done by other processes * at the same time. In particular, without grouping it can happen that * another process also modifies the same value between [find] and * [replace]. *) val add : shm_table -> int32 -> int32_array -> unit (** [add tbl key value]: Adds the binding of [key] to [value] to the * table. Previous bindings are not removed, but simply hidden. *) val find : shm_table -> int32 -> int32_array (** [find tbl key]: Finds the current binding of [key] in [tbl] or * raises [Not_found] if no such binding exists. *) val find_all : shm_table -> int32 -> int32_array list (** [find_all tbl key] returns the list of all data * associated with [key] in [tbl]. * The current binding is returned first, then the previous * bindings, in reverse order of introduction in the table. *) val mem : shm_table -> int32 -> bool (** [mem tbl key] checks if [key] is bound in [tbl]. *) val remove : shm_table -> int32 -> unit (** [remove tbl key] removes the current binding of [key] in [tbl], * restoring the previous binding if it exists. * It does nothing if [key] is not bound in [tbl]. *) val replace : shm_table -> int32 -> int32_array -> unit (** [replace tbl key value] replaces the current binding of [key] * in [tbl] by a binding of [key] to [value]. If [key] is unbound in [tbl], * a binding of [key] to [value] is added to [tbl]. *) (* val rename : shm_table -> int32 -> int32 -> unit (** [rename tbl key1 key2]: Changes the key of the current binding of * [key1] such that it becomes the current binding of [key2]. Raises * [Not_found] if [key1] is unbound. *) *) (* val swap : shm_table -> int32 -> int32 -> unit (** [swap tbl key1 key2]: determins the current bindings of [key1] and * [key2], and renames them to [key2] and [key1], respectively. * Raises [Not_found] if [key1] or [key2] is unbound. *) *) val iter : (int32 -> int32_array -> unit) -> shm_table -> unit (** [iter f tbl] applies [f] to all bindings in table [tbl]. * [f] receives the key as first argument, and the associated value * as second argument. Each binding is presented exactly once to [f]. * The order in which the bindings are passed to [f] is unspecified. * However, if the table contains several bindings for the same key, * they are passed to [f] in reverse order of introduction, that is, * the most recent binding is passed first. * * While the iteration is in progress, the table is read-locked. * That means you cannot modify it during the iteration. *) val iter_keys : (int32 -> unit) -> shm_table -> unit (** [iter_keys f tbl] applies [f] to all keys in table [tbl]. If there * are several bindings for a key, [f] is only called once. * * While the iteration is in progress, the table is locked. * That means you cannot modify it during the iteration. *) val fold : (int32 -> int32_array -> 'a -> 'a) -> shm_table -> 'a -> 'a (** [fold f tbl init] computes * [(f kN dN ... (f k1 d1 init)...)], * where [k1 ... kN] are the keys of all bindings in [tbl], * and [d1 ... dN] are the associated values. * Each binding is presented exactly once to [f]. * The order in which the bindings are passed to [f] is unspecified. * However, if the table contains several bindings for the same key, * they are passed to [f] in reverse order of introduction, that is, * the most recent binding is passed first. * * While the iteration is in progress, the table is locked. * That means you cannot modify it during the iteration. *) val length : shm_table -> int (** [length tbl] returns the number of bindings in [tbl]. * Multiple bindings are counted multiply, so [length] * gives the number of times [iter] calls its first argument. *) (** {1 Enhanced API to shared memory tables} *) exception Next exception Break val read_blocks : shm_table -> int32 -> (int32_array option -> unit) -> unit (** [find_blocks tbl key f]: The values may be stored in several * disk blocks. This interface allows one to access the values block by * block. As [find_all], all bindings for [key] in [tbl] are determined * in reverse order, i.e. the newest binding first, the oldest last. * For every binding [value], the function [f] is invoked in a sequence * [f (Some v1)], [f (Some v2)], ..., [f (Some vN)], [f None] * where * [value] is the array concatenation of [v1], [v2], ..., [vN]. * The function [f] may raise the exception [Next] to go * directly to the start of the next binding of [key]. * The exception [Break] stops the iteration immediately. * * Note that the [int32_array] fragments [vK] point to shared memory. * Any assignment would modify the shared memory object directly! * The binding is at that time, however, only read-locked, so this * should be avoided. *) type write_op = [ `Remove_binding ] (* Future extensions: * - `Truncate_binding of int *) type ctrl_op = [ `Nop | write_op ] val write_blocks : shm_table -> write_op list -> int32 -> (int32_array option -> ctrl_op) -> unit (** [write_blocks tbl ops key f]: Like [read_blocks] this function iterates * over the blocks of all bindings for [key]. For every binding [value], * the function [f] is invoked in a sequence * [f (Some v1)], [f (Some v2)], ..., [f (Some vN)], [f None]. * Unlike [read_blocks] the function [f] returns a value. * The last non-[`Nop] result value in this sequence determines the * modification to carry out for the binding: * * - [`Remove_binding]: The whole binding is removed from the table. * * If all invocations of [f] just return [`Nop], no further modification * is done. * * The modifications must be announced in the [ops] argument. It is * not allowed that [f] returns a value not being a member of [ops] * (except [`Nop]). * * It is possible to raise the special exceptions [Next] and [Break] * just as for [read_blocks]. * * Note that the [int32_array] fragments [vK] point to shared memory. * Any assignment will modify the shared memory object directly! * The binding is at that time write-locked, so such assignments * are protected against concurrent writes. *) (* debug stuff *) val dump : shm_table -> unit val bigarray : int array -> int32_array val memory : shm_table -> (int32, Bigarray.int32_elt, Bigarray.c_layout) Bigarray.Array2.t