Message Passing
Poplar has a kernel object called a Channel
for providing first-class message passing support to userspace.
Channels move packets, called "messages", which contain a stream of bytes, and optionally one or more handles that
are transferred from the sending task to the receiving task.
Ptah
Channels can move arbitrary bytes, but Poplar also includes a layer on top of of Channels called Ptah, which consists of a data model and wire format suitable for encoding data which can be serialized and deserialized from any sensible language without too much difficulty.
Ptah is heavily inspired by Serde, and the first implementation of Ptah was actually a Serde
data format.
Unfortunately, it made properly handling Poplar handles very difficult - when a handle is sent over a channel, it
needs to be put into a separate array, and the in-line data replaced by an index into that array. When the message
travels over a task boundary, the kernel examines and replaces each handle in this array with a handle to the same
kernel object in the new task. This effectively means we need to add a new Handle
type to our data model, which
is not easily possible with Serde (and would make it incompatible with standard Serde anyway).
The Ptah Data Model
The Ptah data model maps pretty well to the Rust type system, and relatively closely to the Serde data model. Key
differences are some stronger guarantees about the encoding of types such as enums (the data model only needs to
fit a single wire format, and so can afford to be less flexible than Serde's), and the lack of a few types -
unit
-based types, and the statically-sized version of seq
and map
- tuple
and struct
. Ptah is not a
self-describing format (i.e. the types you're trying to deserialize is fully known), so the elements of structs and
tuples can simply be serialized in the order they appear, and then deserialized in order at the other end.
- Primitive types
bool
u8
,u16
,u32
,u64
,u128
i8
,i16
,i32
,i64
,i128
f32
,f64
char
string
- Encoded as a
seq
ofu8
, but with the additional requirement that it is valid UTF-8 - Not null terminated, as
seq
includes explicit length
- Encoded as a
option
- Encoded in the same way as an enum, but separately for the benefit of languages without proper enums
- Either
None
orSome({value})
enum
- Include a tag, and optionally some data
- Represent a Rust
enum
, or a tagged union in languages without proper enums - The data is encoded separately to the tag, and can be of any other Ptah type:
- Rust tuple variants (e.g.
E::A(u8, u32)
) are represented bytuple
- Rust struct variants (e.g.
E::B { foo: u8, bar: u32 }
) are represented bystruct
- Rust tuple variants (e.g.
seq
- A variable-length sequence of values, mapping to many types such as
Vec<T>
.
- A variable-length sequence of values, mapping to many types such as
map
- A variable-length series of key-value pairings, mapping to collections like
BTreeMap<K, V>
.
- A variable-length series of key-value pairings, mapping to collections like
handle
- This is the type that means we need our own data model in the first place
- These are encoded out-of-line of the rest of the data, so that the Poplar kernel can introspect into them, if it needs to