Hi everyone,
We are currently (with @Lyrm) trying to define a simple message-passing data structure that exposes two main functions put_and_wait and send_and_clear. This is our current code :
open! Core
open! Await
module Shared : sig @@ portable
type ('msg : immutable_data) t
val create : unit -> 'a t
val send_and_wait : 'a t -> 'a -> unit
val recv_clear : 'a t -> 'a
end = struct
type ('msg : immutable_data, 'k) inner = {
data : ('msg option ref, 'k) Capsule.Data.t;
mutex : 'k Mutex.t;
cond : 'k Mutex.Condition.t;
}
type 'msg t = P : ('msg, 'k) inner -> 'msg t [@@unboxed]
let create () =
let (P { data; mutex }) = Capsule.With_mutex.create (fun () -> ref None) in
P { data; mutex; cond = Mutex.Condition.create () }
let send_and_wait (P t) msg =
Await_blocking.with_await Terminator.never ~f:(fun await ->
Mutex.with_key await t.mutex ~f:(fun key ->
let new_msg = Some msg in
let #((), key) =
Capsule.Expert.Key.access key ~f:(fun access ->
let value = Capsule.Data.unwrap ~access t.data in
value := new_msg)
in
Mutex.Condition.signal t.cond;
let key = Mutex.Condition.wait await t.cond ~lock:t.mutex key in
let rec waiting_loop key =
let #(is_unchanged, key) =
Capsule.Expert.Key.access key ~f:(fun access ->
phys_equal !(Capsule.Data.unwrap ~access t.data) new_msg)
in
if is_unchanged then
let key = Mutex.Condition.wait await t.cond ~lock:t.mutex key in
waiting_loop key [@nontail]
else #((), key)
in
waiting_loop key [@nontail])
[@nontail])
let recv_clear (P t) =
Await_blocking.with_await Terminator.never ~f:(fun await ->
(Mutex.with_key await t.mutex ~f:(fun key ->
let rec loop key =
let #(value, key) =
Capsule.Expert.Key.access key ~f:(fun access ->
{
Modes.Aliased.aliased =
!(Capsule.Data.unwrap ~access t.data);
})
in
match value.aliased with
| None ->
let key =
Mutex.Condition.wait await t.cond ~lock:t.mutex key
in
loop key [@nontail]
| Some value -> #({ Modes.Aliased.aliased = value }, key)
in
let #(value, key) = loop key in
let #((), key) =
Capsule.Expert.Key.access key ~f:(fun access ->
Capsule.Data.unwrap ~access t.data := None)
in
Mutex.Condition.signal t.cond;
#(value, key)))
.aliased)
end
We would obviously like this structure to be portable so that it can be used in parallel. We have added a @@portable annotation at the top of the module signature. However, we still get a nonportable error on the values of type Shared.t when calling Shared.recv_clear for example. Do we need to annotate the type of t in the .mli?