module Netmcore_heap:Shared heaps of structured values
These heaps live in
Netmcore_mempool-type shared memory pools,
and can contain an arbitrary number of Ocaml values. These values
can be mutable, but special care has to be taken when modifying them.
The first value pushed onto the heap is called the root element.
All other values must be (directly or indirectly) reachable from the
Heaps are created with a certain initial size. The heaps remain connected with the memory pool, and they are enlarged if necessary by allocating more blocks in the pool.
As the heaps are shared between processes, it must be taken care
that no reference is made from shared heaps to normal process-local
memory. These references would only be valid in the process creating
them, and following such references from other processes would probably
crash the program (or at least return wrong values). In order to ensure
this, it is strictly forbidden to directly manipulate mutable
data structures. The
Netmcore_heap.modify function has to be used,
as this function makes it possible to copy more values to the heap.
Unfortunately, there is nothing in the type system that would prevent
direct mutation. so this can only be ensured by the discipline of the
The values of the heap are also garbage-collected: If all allocated space is used and more values need to be added, it is first tried to get rid of old unreachable values. The garbarge collection is done by the process that happens to initiate the addition of the value that does no more fit onto the heap. During garbage collection, no other additions can be done, but read accesses are not prevented. The garbage collector does not move values (addresses remain unchanged).
The garabage collector only considers values as reachable that are
reachable via the root element. It is not sufficient when a value
is only reachable via a process-specific reference.
val create_heap :
Netmcore.res_id -> int -> 'a -> 'a heap
create_heap pool_id size root: Creates a new heap with
sizebytes in the pool identified by
pool_id. This ID must refer to a
root is copied to the new heap. This is done by
root and all values pointed to by
and storing these duplicates in the heap.
The possible types of value
root are restricted. In particular,
the following types are not supported (as
'a or as a component
nativeintwhich are specially supported)
x = [| |]do not work)
val minimum_size :
'a -> int
sizevalue one must pass to
create_heapat minimum to put this root element onto the heap.
val root :
'a heap -> 'a
val heap_of_descr :
Netmcore.res_id -> 'a descr -> 'a heap
heap_of_descr pool d: Get the heap for a descriptor. This assumes that the heap still exists.
Caveat: pushed values are not considered as roots, and thus they
need immediately be attached to the existing data structure.
Otherwise, the next push might trigger a garbage collection, and
the new value is deleted. If this is not possible, one can
pin instead (see below).
modify h mutate: This function locks the heap so that this process has exclusive write access to it for the duration of the
mutatefunction is immediately called back, and the argument of
mutateis the mutator
mthat allows one to push values onto the heap.
add m x from the body of
mutate one can create a copy
x that is stored in the heap.
val add :
mutator -> 'a -> 'a
val add_uniform_array :
mutator -> int -> 'a -> 'a array
add_uniform_array m n x: Pushes a new value with n elements onto the heap. Each index position of the array is initialized with the same copy of
val add_init_array :
mutator -> int -> (int -> 'a) -> 'a array
add_init_array m n f: Pushes a new value with n element onto the heap. The index position
kis inititialized by running
f kand pushing the copy of this onto the heap.
val add_some :
mutator -> 'a -> 'a option
add_some mut x: Returns
Some xwhere the O'Caml value representing
Someis allocated in the heap using
mut. It is assumed that
xis already a resident of the heap. This means
xis not copied!
val pin :
mutator -> 'a -> unit
pin m x: Pins a shared value
xso it cannot be deleted by the garbage collector. The value remains pinned for the lifetime of the mutator
m(i.e. the runtime of the
Pinning is relatively expensive if done in masses, and should be
avoided if possible.
val copy :
'a -> 'a
val with_value :
'a heap -> (unit -> 'b) -> ('b -> 'c) -> 'c
with_value h find process: Logically, this runs
process (find ())and returns the result. While
findis being executed, the heap is write-locked. This returns a value
processis being executed, the value
xis temporarily added to the set of reachable values, so that a parallely running garbage collection will not delete it.
x must reside in the heap!
find will cause a deadlock. Calling
process is allowed.
val with_value_2 :
'a heap -> (unit -> 'b * 'c) -> ('b * 'c -> 'd) -> 'd
val with_value_3 :
'a heap -> (unit -> 'b * 'c * 'd) -> ('b * 'c * 'd -> 'e) -> 'e
val with_value_4 :
'a heap ->
(unit -> 'b * 'c * 'd * 'e) -> ('b * 'c * 'd * 'e -> 'f) -> 'f
val with_value_5 :
'a heap ->
(unit -> 'b * 'c * 'd * 'e * 'f) -> ('b * 'c * 'd * 'e * 'f -> 'g) -> 'g
with_value, but a tuple of values can be passed down
val with_value_n :
'a heap -> (unit -> 'b list) -> ('b list -> 'c) -> 'c
with_value, but a list of values can be passed down
val destroy :
'a heap -> unit
val gc :
'a heap -> unit
val debug_info :
'a heap -> string
This example creates a heap that stores a single value. (This is
let shared_ref x = (* The shm version of [ref x] *) let r = ref x in let init_size = minimum_size r in let hp = create_heap pool_id init_size r in hp let deref sref = (* The shm version of [!] *) !(root sref) let assign sref x = (* The shm version of [:=] - however, a copy of x is done *) modify sref (fun add -> (root sref) := add x )