How to specify a type is plain (IPC purpose)

I'd like to wrap a set of plain types acquired from the shared memory, via a trait bound Plain, but I find it hard to do this.

To clarify, say a type T is "plain" if:

  1. it contains no pointers nor references;
  2. it has no destructor, i.e. T: !Drop;
  3. it contains no UnsafeCells;
  4. &T can be safely transmuted from a &[u8], where the &[u8] has the length of T's size and is properly aligned to T, and T's internal padding should be guaranteed to be zero;
  5. &mut T can be safely transmuted from a zero-filled &mut [u8], where the length and alignment of &mut [u8] takes after 5.

Generally, such a plain type T will be processed as following:

  1. create a page of shared memory, and allocate as layout of T in the page;
  2. fill the allocated bytes zero, and transmute such &mut [u8] into &mut T;
  3. write arbitrary valid value T into the mutable slice &mut T and freeze it (i.e. no longer mutable);
  4. another process open this shared page, and find the corresponding location of &[u8] (By a inter-process communication, the new process doesn't know the actual type inside, but knows only the offset from the page and the length of bytes);
  5. it transmutes the &[u8] into &T and read its value.

EDIT: it is considered as a specialization of a general type. For T that is not "plain", we serialize T to and deserialize T from a [u8] instead of transmuting, which means that the shared memory layer must be type-erased.

Is bytemuck::Pod the trait you want? I think bytemuck is a fairly standard crate.

3 Likes
  1. There's no way to detect pointers, but : 'static will forbid any non-'static borrows (yet allow 'static ones). However you don't really need that, because references cannot be null, so it's covered by the other rules.

  2. !Drop doesn't mean what you think it means, as compiler-generated drop glue does not mean a type implements Drop. What you do mean can be approximated with : Copy. However, that may be a stronger bound than you would like.

  3. Barring UnsafeCell becoming Copy, : Copy gives you this one as well.

  4. bytemuck has various traits for this one (and for #5).

2 Likes

Thank you! It looks very near to the solution, but the problem is that it forbids padding. However, our "plain" types can have padding, though the paddings are always zero.

It might be possible to adding paddings manually by a proc-macro.

#[repr(C, align(4))]
pub struct Foo {
    pub a: u16,
    __pad1: [u8; 2],
    pub b: i32,
    pub c: [u8; 3],
    __pad2: [u8; 1],
}

But it couldn't be a easy way since the proc macro has no type info, and doesn't know the size and align of each fields (except the primitives).

  1. 'static can barrier references but not raw pointers. However, it makes no sense to pass any pointers across different processes.
  2. Oh, yes, I forgot the compiler-generated drop glue. But indeed, : Copy is too strong, because the data transported via the shared memory is usually very large that we should avoid every copy of it during transportation.
  3. Right, but we would like to avoid : Copy.
  4. Looks great. I believe it can solve at least part of the problems.

Instead of u8, use

#[repr(u8)]
enum Pad {
    Zero = 0,
}

Then it has to be 0, and you'll have a niche.

4 Likes

Sadly there are no Destructorless or NoInteriorMutability or CopyEligible traits.

Perhaps you could have Copy on an inner type, but not the exposed type. Or have a test that implements copy but don't do so normally.

1 Like

Yes, Having Copy on an inner type sounds feasible.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.