1
(* $Id: netplex_sharedvar.mli 1591 2011-05-05 16:43:45Z gerd $ *)
3
(** Netplex-wide variables *)
5
(** This plugin allows to have Netplex-global variables that can be read
6
and written by all components. These variables are useful to communicate
7
names and other small pieces of information across the whole Netplex.
8
For instance, one component could allocate a shared memory object, and
9
put its name into a variable to make it known to other components.
11
This implementation works in both multi-processing and
12
multi-threading netplex environments. It is, however, not very
13
fast, because the variables live in the controller, and the
14
access operations are realized by RPC's. It is good
15
enough when these operations are only infrequently called, e.g. in
16
the post-start and pre-finish processor callbacks.
18
Furthermore, note that it is unwise to put large values into
19
variables when using them in multi-processing contexts. The controller
20
process is also the parent process of all [fork]ed children, and
21
when a lot of memory is allocated in the controller, all
22
this memory needs to be copied when the [fork] is done. As workaround,
23
put such values into temporary files, and only pass the names of the
24
files around via variables.
26
Variables come in two flavors:
28
- Encapsulated variables (see {!Netplex_encap})
30
A string variable cannot be accessed as encapsulated variable, and
33
The latter kind is useful to safely store structured ocaml values in
36
More documentation can also be found here:
37
{!Netplex_advanced.sharedvars}
42
exception Sharedvar_type_mismatch of string
43
(** The (dynamically typed) variable has the wrong type (string/exn) *)
45
exception Sharedvar_no_permission of string
46
(** It is not allowed to set the value *)
48
exception Sharedvar_not_found of string
49
(** The variable does not exist. Only used by [Make_var_type] *)
51
exception Sharedvar_null
52
(** The initial value of a shared exception variable *)
56
(** To enable shared variables, call the controller's [add_plugin] method
57
with this object as argument. This can e.g. be done in the
58
[post_add_hook] of the processor.
63
(** The folloing functions can all be invoked in container
64
contexts. In controller context, access is limited to [get_value].
66
If called from the wrong context the exception
67
{!Netplex_cenv.Not_in_container_thread} is raised.
70
val create_var : ?own:bool -> ?ro:bool -> ?enc:bool -> string -> bool
71
(** Create the variable with the passed name with an empty string
72
(or the exception [Sharedvar_null]) as
73
initial value. If the creation is possible (i.e. the variable did
74
not exist already), the function returns [true], otherwise
75
the already existing variable is left modified, and [false] is
76
passed back. By default, the variable can be modified and deleted
77
by any other container. Two options allow you to change that:
79
- [own]: If true, the created variable is owned by the calling
80
socket service. Only the caller can delete it, and when the
81
last component of the socket service terminates, the variable is
82
automatically deleted. The deletion happens after the
83
[post_finish_hook] is executed, so the variable is still accessible
85
- [ro]: if true, only the owner can set the value
86
- [enc]: if true, the variable stores encapsulated values, otherwise
90
Variable names are global to the whole netplex system. By convention,
91
these names are formed like ["service_name.local_name"], i.e. they
92
are prefixed by the socket service to which they refer.
95
val delete_var : string -> bool
96
(** [delete_var name]: Deletes the variable [name]. Returns [true] if
97
the deletion could be carried out, and [false] when the variable
98
does not exist, or the container does not have permission to delete
102
val set_value : string -> string -> bool
103
(** [set_value name value]: Sets the variable [name] to [value]. This
104
is only possible when the variable exists, and is writable.
105
Returns [true] if the function is successful, and [false] when
106
the variable does not exist.
108
Raises [Sharedvar_no_permission] if the variable cannot be modified.
110
Raises [Sharedvar_type_mismatch] if the variable is not a string
114
val set_enc_value : string -> encap -> bool
115
(** [set_enc_value name value]: Sets the variable [name] to [value].
116
Return value as for [set_value].
118
Raises [Sharedvar_no_permission] if the variable cannot be modified.
120
Raises [Sharedvar_type_mismatch] if the variable is not encapsulated
123
val get_value : string -> string option
124
(** [get_value name]: Gets the value of the variable [name]. If the
125
variable does not exist, [None] is returned.
127
Raises [Sharedvar_type_mismatch] if the variable is not a string
131
val get_enc_value : string -> encap option
132
(** [get_enc_value name]: Gets the value of the variable [name]. If the
133
variable does not exist, [None] is returned.
135
Raises [Sharedvar_type_mismatch] if the variable is not encapsulated
138
val wait_for_value : string -> string option
139
(** [wait_for_value name]: If the variable exists and [set_value] has
140
already been called at least once, the current value is returned.
141
If the variable exists, but [set_value] has not yet been called at all,
142
the function waits until [set_value] is called, and returns the value
143
set then. If the variable does not exist, the function immediately
146
An ongoing wait is interrupted when the variable is deleted. In this
147
case [None] is returned.
150
val wait_for_enc_value : string -> encap option
151
(** Same for encapsulated variables *)
154
val get_lazily : string -> (unit -> string) -> string option
155
(** [get_lazily name f]: Uses the variable [name] to ensure that [f]
156
is only invoked when [get_lazily] is called for the first time,
157
and that the value stored in the variable is returned the
158
next times. This works from whatever component [get_lazily]
161
If [f()] raises an exception, the exception is suppressed, and
162
[None] is returned as result of [get_lazily]. Exceptions are not
163
stored in the variable, so the next time [get_lazily] is called
164
it is again tried to compute the value of [f()]. If you want to
165
catch the exception this must done in the body of [f].
167
No provisions are taken to delete the variable. If [delete_var]
168
is called by user code (which is allowed at any time), and
169
[get_lazily] is called again, the lazy value will again be computed.
172
val get_enc_lazily : string -> (unit -> encap) -> encap option
173
(** Same for encapsulated values *)
175
val dump : string -> Netlog.level -> unit
176
(** Dumps the access counter of this variable to {!Netlog}. The
177
string argument "*" dumps all variables.
180
module Make_var_type(T:Netplex_cenv.TYPE) :
181
Netplex_cenv.VAR_TYPE with type t = T.t
182
(** Creates a module with [get] and [set] functions to access variables
183
of type [T.t]. Call it like
187
Make_var_type(struct type t = foo end)
190
and use [Foo_var.get] and [Foo_var.set] to access the shared
191
variables of type [foo]. These functions can also raise the exception
192
[Sharedvar_not_found] (unlike the primitive accessors above).
194
The variable must have been created with [enc:true], e.g.
197
let ok = create_var ~enc:true "name"
204
Here, one randomly chosen container computes [precious_value], and
205
makes it available to all others, so the other container can simply
206
grab the value. This is similar to what [get_lazily] does internally:
209
let get_precious_value() =
210
let container = Netplex_cenv.self_cont() in
211
let var_name = "my_service.precious" in
212
if Netplex_sharedvar.create_var var_name then (
214
try ... (* some costly computation *)
216
ignore(Netplex_sharedvar.delete_var var_name);
218
let b = Netplex_sharedvar.set_value var_name precious_value in
223
match Netplex_sharedvar.wait_for_value var_name with
225
| None -> failwith "get_precious_value"
226
(* or do plan B, e.g. compute the value *)
230
We don't do anything here for deleting the value when it is no longer
231
needed. Finding a criterion for that is very application-specific.
232
If the variable can be thought as being another service endpoint
233
of a socket service, it is a good idea to acquire the ownership
234
(by passing [~own:true] to [create_var]), so the variable is automatically
235
deleted when the socket service stops.
237
Of course, the plugin must be enabled, e.g. by overriding the
238
[post_add_hook] processor hook:
241
method post_add_hook sockserv ctrl =
242
ctrl # add_plugin Netplex_sharedvar.plugin