mirror of
https://github.com/zap-proto/ocaml-rpc.git
synced 2026-10-30 15:04:28 +00:00
346 lines
15 KiB
OCaml
346 lines
15 KiB
OCaml
(** Cap'n Proto core API for defining and using services. *)
|
|
|
|
include (module type of Capnp.BytesMessage)
|
|
(** @closed *)
|
|
|
|
module Exception = Capnp_rpc_proto.Exception
|
|
module Error = Capnp_rpc_proto.Error
|
|
|
|
module StructRef : sig
|
|
(** A promise for a response structure.
|
|
You can use the generated [_get_pipelined] functions on a promise to get
|
|
a promise for a capability inside the promise, and then pipeline messages
|
|
to that promise without waiting for the response struct to arrive. *)
|
|
|
|
type 'a t
|
|
(** An ['a t] is a reference to a response message (that may not have arrived yet)
|
|
with content type ['a]. *)
|
|
|
|
val inc_ref : 'a t -> unit
|
|
(** [inc_ref t] increases the reference count on [t] by one. *)
|
|
|
|
val dec_ref : 'a t -> unit
|
|
(** [dec_ref t] reduces the reference count on [t] by one.
|
|
When the count reaches zero, this result must never be used again.
|
|
If the results have not yet arrived when the count reaches zero, we send
|
|
a cancellation request (which may or may not succeed). As soon as the
|
|
results are available, they are released. *)
|
|
end
|
|
|
|
module Capability : sig
|
|
(** A capability is a reference to an object, or to a promise for an object.
|
|
You can invoke methods on a capability even while it is still only a
|
|
promise. *)
|
|
|
|
type +'a t
|
|
(** An ['a t] is a capability reference to a service of type ['a]. *)
|
|
|
|
val broken : Exception.t -> 'a t
|
|
(** [broken ex] is a broken capability, with problem [ex].
|
|
Any attempt to call methods on it will fail with [ex]. *)
|
|
|
|
val when_broken : (Exception.t -> unit) -> 'a t -> unit
|
|
(** [when_broken fn x] calls [fn problem] when [x] becomes broken.
|
|
If [x] is already broken, [fn] is called immediately.
|
|
If [x] can never become broken (e.g. it is a near ref), this does nothing.
|
|
If [x]'s ref-count reaches zero without [fn] being called, it will never
|
|
be called. *)
|
|
|
|
val when_released : 'a t -> (unit -> unit) -> unit
|
|
(** [when_released t fn] will call [fn ()] when [t]'s ref-count drops to zero.
|
|
This is used for caches, to remove entries when they become invalid.
|
|
For promises, [fn] will be transferred to the resolution if resolved.
|
|
For broken caps, this method does nothing (exceptions are never released). *)
|
|
|
|
val problem : 'a t -> Exception.t option
|
|
(** [problem t] is [Some ex] if [t] is broken, or [None] if it is still
|
|
believed to be healthy. Once a capability is broken, it will never
|
|
work again and any calls made on it will fail with exception [ex]. *)
|
|
|
|
val await_settled : 'a t -> (unit, Exception.t) result
|
|
(** [await_settled t] resolves once [t] is a "settled" (non-promise) reference.
|
|
If [t] is a near, far or broken reference, this returns immediately.
|
|
If it is currently a local or remote promise, it waits until it isn't.
|
|
[wait_until_settled] takes ownership of [t] until it returns (you must not
|
|
[dec_ref] it before then).
|
|
@return [Ok ()] on success, or [Error _] if [t] failed.
|
|
@since 1.2 *)
|
|
|
|
val await_settled_exn : 'a t -> unit
|
|
(** Like [await_settled], but raises an exception on error.
|
|
@since 1.2 *)
|
|
|
|
val equal : 'a t -> 'a t -> (bool, [`Unsettled]) result
|
|
(** [equal a b] indicates whether [a] and [b] designate the same settled service.
|
|
Returns [Error `Unsettled] if [a] or [b] is still a promise (and they therefore
|
|
may yet turn out to be equal when the promise resolves). *)
|
|
|
|
module Request : sig
|
|
type 'a t
|
|
(** An ['a t] is a builder for the out-going request's payload. *)
|
|
|
|
val create : ?message_size:int -> (Capnp.Message.rw Slice.t -> 'a) -> 'a t * 'a
|
|
(** [create init] is a fresh request payload and contents builder.
|
|
Use one of the generated [init_pointer] functions for [init].
|
|
@param message_size An estimate of the size of the payload. If this is too small,
|
|
additional segments will be allocated automatically, but this
|
|
is less efficient than getting the size right to start with. *)
|
|
|
|
val create_no_args : unit -> 'a t
|
|
(** [create_no_args ()] is a payload with no content. *)
|
|
|
|
val release : 'a t -> unit
|
|
(** Clear the exported refs, dropping their ref-counts. This is called automatically
|
|
when you send a message, but you might need it if you decide to abort. *)
|
|
end
|
|
|
|
val call : 't t -> ('t, 'a, 'b StructStorage.reader_t) Capnp.RPC.MethodID.t -> 'a Request.t -> 'b StructRef.t
|
|
(** [call target m req] invokes [target#m req] and returns a promise for the result.
|
|
Messages may be sent to the capabilities that will be in the result
|
|
before the result arrives - they will be pipelined to the service
|
|
responsible for resolving the promise. The caller must call [StructRef.dec_ref]
|
|
when finished with the result (consider using one of the [call_*] functions below
|
|
instead for a simpler interface). *)
|
|
|
|
val call_and_wait : 't t -> ('t, 'a, 'b StructStorage.reader_t) Capnp.RPC.MethodID.t ->
|
|
'a Request.t -> (('b StructStorage.reader_t * (unit -> unit)), [> `Capnp of Error.t]) result
|
|
(** [call_and_wait t m req] does [call t m req] and waits for the response.
|
|
This is simpler than using [call], but doesn't support pipelining
|
|
(you can't use any capabilities in the response in another message until the
|
|
response arrives).
|
|
On success, it returns [Ok (response, release_response_caps)].
|
|
Call [release_response_caps] when done with the results, to release any capabilities it might
|
|
contain that you didn't use (remembering that future versions of the protocol might add
|
|
new optional capabilities you don't know about yet).
|
|
If you don't need any capabilities from the result, consider using [call_for_value] instead.
|
|
Cancelling the fiber will send a cancel message to the target for remote calls. *)
|
|
|
|
val call_for_value : 't t -> ('t, 'a, 'b StructStorage.reader_t) Capnp.RPC.MethodID.t ->
|
|
'a Request.t -> ('b StructStorage.reader_t, [> `Capnp of Error.t]) result
|
|
(** [call_for_value t m req] is similar to [call_and_wait], but automatically
|
|
releases any capabilities in the response before returning. Use this if
|
|
you aren't expecting any capabilities in the response. *)
|
|
|
|
val call_for_value_exn : 't t -> ('t, 'a, 'b StructStorage.reader_t) Capnp.RPC.MethodID.t ->
|
|
'a Request.t -> 'b StructStorage.reader_t
|
|
(** Wrapper for [call_for_value] that turns errors into exceptions. *)
|
|
|
|
val call_for_unit : 't t -> ('t, 'a, 'b StructStorage.reader_t) Capnp.RPC.MethodID.t ->
|
|
'a Request.t -> (unit, [> `Capnp of Error.t]) result
|
|
(** Wrapper for [call_for_value] that ignores the result. *)
|
|
|
|
val call_for_unit_exn : 't t -> ('t, 'a, 'b StructStorage.reader_t) Capnp.RPC.MethodID.t ->
|
|
'a Request.t -> unit
|
|
(** Wrapper for [call_for_unit] that turns errors into exceptions. *)
|
|
|
|
val call_for_caps : 't t -> ('t, 'a, 'b StructStorage.reader_t) Capnp.RPC.MethodID.t ->
|
|
'a Request.t -> ('b StructRef.t -> 'c) -> 'c
|
|
(** [call_for_caps target m req extract] is a wrapper for [call] that passes the results promise to
|
|
[extract], which should extract any required capability promises from it.
|
|
In the common case where you want a single cap "foo" from the result, use
|
|
[call_for_caps target m req Results.foo_get_pipelined].
|
|
When the remote call finally returns, the result will be released automatically. *)
|
|
|
|
type 'a resolver
|
|
(** An ['a resolver] can be used to resolve a promise for an ['a]. It can only be used once. *)
|
|
|
|
val promise : unit -> 'a t * 'a resolver
|
|
(** [promise ()] returns a fresh local promise and a resolver for it.
|
|
Any calls made on the promise will be queued until it is resolved. *)
|
|
|
|
val resolve_ok : 'a resolver -> 'a t -> unit
|
|
(** [resolve_ok r x] resolves [r]'s promise to [x]. [r] takes ownership of [x]
|
|
(the caller must use [inc_ref] first if they want to continue using it). *)
|
|
|
|
val resolve_exn : 'a resolver -> Exception.t -> unit
|
|
(** [resolve_exn r x] breaks [r]'s promise with exception [x]. *)
|
|
|
|
val inc_ref : _ t -> unit
|
|
(** [inc_ref t] increases the ref-count on [t] by one. *)
|
|
|
|
val dec_ref : _ t -> unit
|
|
(** [dec_ref t] decreases the ref-count on [t] by one. When the count reaches zero,
|
|
the capability is released. This may involve sending a notification to a remote
|
|
peer. Any time you extract a capability from a struct or struct promise,
|
|
it must eventually be freed by calling [dec_ref] on it. *)
|
|
|
|
val with_ref : 'a t -> ('a t -> 'b) -> 'b
|
|
(** [with_ref t fn] runs [fn t] and then calls [dec_ref t] (whether [fn]
|
|
succeeds or not). *)
|
|
|
|
val pp : 'a t Fmt.t
|
|
end
|
|
|
|
module Sturdy_ref : sig
|
|
type +'a t
|
|
(** An off-line (persistent) capability reference.
|
|
|
|
A sturdy ref contains all the information necessary to get a live reference to a service:
|
|
|
|
- The network address of the hosting vat (e.g. TCP host and port)
|
|
- A way to authenticate the hosting vat (e.g. a fingerprint of the vat's public key)
|
|
- A way to identify the target service within the vat and prove permission to access it
|
|
(e.g. a "Swiss number")
|
|
*)
|
|
|
|
val connect : 'a t -> ('a Capability.t, Exception.t) result
|
|
(** [connect t] returns a live reference to [t]'s service. *)
|
|
|
|
val connect_exn : 'a t -> 'a Capability.t
|
|
(** [connect_exn] is a wrapper for [connect] that raises an exception on error. *)
|
|
|
|
val with_cap :
|
|
'a t ->
|
|
('a Capability.t -> ('b, [> `Capnp of Exception.t] as 'e) result) ->
|
|
('b, 'e) result
|
|
(** [with_cap t f] uses [connect t] to get a live-ref [x],
|
|
then does [Capability.with_ref x f]. *)
|
|
|
|
val with_cap_exn : 'a t -> ('a Capability.t -> 'b) -> 'b
|
|
(** [with_cap_exn t f] uses [connect_exn t] to get a live-ref [x],
|
|
then does [Capability.with_ref x f]. *)
|
|
|
|
val reader :
|
|
('a StructStorage.reader_t -> Capnp.MessageSig.ro Slice.t option) ->
|
|
'a StructStorage.reader_t -> Uri.t
|
|
(** [reader accessor] is a field accessor for reading a sturdy ref.
|
|
e.g. if [sr_get] is a generated field accessor for an AnyPointer field, then
|
|
[reader Reader.Struct.sr_get] is an accessor that treats it as a SturdyRef field.
|
|
todo: This should really return a sturdy ref, not a URI, but that requires a change
|
|
to the spec to add a sturdy ref cap-descriptor table entry type. *)
|
|
|
|
val builder :
|
|
('a StructStorage.builder_t -> Capnp.MessageSig.rw Slice.t) ->
|
|
'a StructStorage.builder_t -> _ t -> unit
|
|
(** [builder setter] converts a generated AnyPointer field setter [setter] to a SturdyRef
|
|
setter. Use it to add a SturdyRef to a message with [builder Params.sr_get params sr]. *)
|
|
|
|
val cast : 'a t -> 'b t
|
|
end
|
|
|
|
module Service : sig
|
|
(** Functions for service implementors. *)
|
|
|
|
type ('a, 'b) method_t = 'a -> (unit -> unit) -> 'b StructRef.t
|
|
(** An ('a, 'b) method_t is a method implementation that takes
|
|
a reader for the parameters and
|
|
a function to release the capabilities in the parameters,
|
|
and returns a promise for the results. *)
|
|
|
|
module Response : sig
|
|
type 'b t
|
|
(** An ['a t] is a builder for the out-going response. *)
|
|
|
|
val create : ?message_size:int -> (Capnp.Message.rw Slice.t -> 'a) -> 'a t * 'a
|
|
(** [create init] is a fresh response and contents builder.
|
|
Use one of the generated [init_pointer] functions for [init]. *)
|
|
|
|
val create_empty : unit -> 'a t
|
|
(** [empty ()] is an empty response. *)
|
|
|
|
val release : 'a t -> unit
|
|
(** Clear the exported refs, dropping their ref-counts. This is called automatically
|
|
when you send a message, but you might need it if you decide to abort. *)
|
|
end
|
|
|
|
val return : 'a Response.t -> 'a StructRef.t
|
|
(** [return r] wraps up a simple local result as a promise. *)
|
|
|
|
val return_empty : unit -> 'a StructRef.t
|
|
(** [return_empty ()] is a promise for a response with no payload. *)
|
|
|
|
val error : Error.t -> 'a StructRef.t
|
|
(** [error e] is a broken promise for a struct, with error [e]. *)
|
|
|
|
val fail : ?ty:Exception.ty -> ('a, Format.formatter, unit, 'b StructRef.t) format4 -> 'a
|
|
(** [fail msg] is an exception {!error} with reason [msg]. *)
|
|
end
|
|
|
|
(** Some aliases for common modules.
|
|
|
|
This is intended to be opened, as [open Capnp_rpc.Std]. *)
|
|
module Std : sig
|
|
module Sturdy_ref = Sturdy_ref
|
|
module Capability = Capability
|
|
module Service = Service
|
|
end
|
|
|
|
module Leak_handler = Leak_handler
|
|
|
|
(**/**)
|
|
|
|
module Untyped : sig
|
|
(** This module is only for use by the code generated by the capnp-ocaml
|
|
schema compiler. The generated code provides type-safe wrappers for
|
|
everything here. *)
|
|
|
|
open Stdint
|
|
|
|
type abstract_method_t
|
|
|
|
val abstract_method : ('a StructStorage.reader_t, 'b) Service.method_t -> abstract_method_t
|
|
|
|
val struct_field : 'a StructRef.t -> int -> 'b StructRef.t
|
|
|
|
val capability_field : 'a StructRef.t -> int -> 'b Capability.t
|
|
|
|
class type generic_service = object
|
|
method dispatch : interface_id:Uint64.t -> method_id:int -> abstract_method_t
|
|
method release : unit
|
|
method pp : Format.formatter -> unit
|
|
end
|
|
|
|
val local : #generic_service -> 'a Capability.t
|
|
|
|
val get_cap : Capnp.MessageSig.attachments -> Uint32.t -> _ Capability.t
|
|
val add_cap : Capnp.MessageSig.attachments -> _ Capability.t -> Uint32.t
|
|
val clear_cap : Capnp.MessageSig.attachments -> Uint32.t -> unit
|
|
|
|
val unknown_interface : interface_id:Uint64.t -> abstract_method_t
|
|
val unknown_method : interface_id:Uint64.t -> method_id:int -> abstract_method_t
|
|
end
|
|
|
|
module Private = Private
|
|
|
|
module Cast : sig
|
|
val cap_of_raw : Capnp_core.Core_types.cap -> 'a Capability.t
|
|
val cap_to_raw : 'a Capability.t -> Capnp_core.Core_types.cap
|
|
|
|
val sturdy_of_raw : Capnp_core.sturdy_ref -> 'a Sturdy_ref.t
|
|
val sturdy_to_raw : 'a Sturdy_ref.t -> Capnp_core.sturdy_ref
|
|
end
|
|
|
|
module Debug = Capnp_rpc_proto.Debug
|
|
|
|
(**/**)
|
|
|
|
module Persistence : sig
|
|
class type ['a] persistent = object
|
|
method save : ('a Sturdy_ref.t, Exception.t) result
|
|
end
|
|
|
|
val with_persistence :
|
|
('a #persistent) ->
|
|
('impl -> 'a Capability.t) ->
|
|
(#Untyped.generic_service as 'impl) ->
|
|
'a Capability.t
|
|
(** [with_persistence persist Service.Foo.local obj] is like [Service.Foo.local obj], but the
|
|
resulting service also handles the Cap'n Proto persistence protocol, using [persist]. *)
|
|
|
|
val with_sturdy_ref :
|
|
'a Sturdy_ref.t ->
|
|
('impl -> 'a Capability.t) ->
|
|
(#Untyped.generic_service as 'impl) ->
|
|
'a Capability.t
|
|
(** [with_sturdy_ref sr Service.Foo.local obj] is like [Service.Foo.local obj],
|
|
but responds to [save] calls by returning [sr]. *)
|
|
|
|
val save : 'a Capability.t -> (Uri.t, [> `Capnp of Error.t]) result
|
|
(** [save cap] calls the persistent [save] method on [cap].
|
|
Note that not all capabilities can be saved.
|
|
todo: this should return an ['a Sturdy_ref.t]; see {!Sturdy_ref.reader}. *)
|
|
|
|
val save_exn : 'a Capability.t -> Uri.t
|
|
(** [save_exn] is a wrapper for [save] that returns a failed thread on error. *)
|
|
end
|