Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Safety of std::tuple in cross-process shared memory

525 views
Skip to first unread message

Joe Mason

unread,
Jul 30, 2024, 10:11:17 AM7/30/24
to chromium-dev, cxx, Oliver Li
ReadOnlySharedMemoryMapping::GetMemoryAs<T>() has an assert that T is trivially copyable:

     static_assert(std::is_trivially_copyable_v<T>,
                  "Copying non-trivially-copyable object across memory spaces "
                  "is dangerous");

std::tuple<int, double, bool> fails this. Is it actually dangerous to copy a tuple of scalars over shared memory? (Assuming all processes that access the memory use the same compiler, compiler options and stdlib, so that they use the same memory layout for the class.)

I assume the assert is being over-careful here because, in general, a non-trivial copy constructor might have dangerous side effects, but better double-check.

If a tuple is safe to use, any objection to my loosening the static_assert to allow std::tuple<Ts...> as long as each type in Ts is trivially copyable?

Thanks,
Joe

Peter Kasting

unread,
Jul 30, 2024, 10:55:48 AM7/30/24
to Joe Mason, chromium-dev, cxx, Oliver Li
I am skeptical that would be safe if the tuple copy constructor is not trivial, unless there's a bug in libc++.

PK

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAH%3DT95QCFoCuT%3D7zfj1hRaytGRUzq7Vwo-2x3S%3Dg1knnMVaJrg%40mail.gmail.com.

Joe Mason

unread,
Jul 30, 2024, 11:11:29 AM7/30/24
to John Stiles, chromium-dev, cxx, Oliver Li
True, but in this case I'm writing a generic wrapper class that takes a templated type list, and uses a tuple as an implementation detail. Being able to write the tuple directly to shared memory would save a lot of work.

A higher level question: SharedMemoryMapping has GetMemoryAsSpan(), which can interpret the memory as a list of objects of the same type. If I have a set of objects of different types, each of which is safe to copy across shared memory, what's the best way to put them in the same memory region without having to calculate offsets and alignments for each object manually?

On Tue, Jul 30, 2024 at 10:34 AM John Stiles <johns...@google.com> wrote:
In general, my understanding was that we shouldn't be using such a tuple anyway: https://google.github.io/styleguide/cppguide.html#Structs_vs._Tuples 

"Prefer to use a struct instead of a pair or a tuple whenever the elements can have meaningful names."

In my experience, we can almost always make a struct with names that are more meaningful than get<0> or get<2>. In this case, it also solves your issue because such a struct is trivially copyable.



--

Kyle Charbonneau

unread,
Jul 30, 2024, 12:11:08 PM7/30/24
to Joe Mason, John Stiles, chromium-dev, cxx, Oliver Li
If I have a set of objects of different types, each of which is safe to copy across shared memory, what's the best way to put them in the same memory region without having to calculate offsets and alignments for each object manually?

You should be able to define a struct that contains the types and use that in place of std::tuple. If the struct contains all trivially copyable types with no user defined copy constructor it should be trivially copyable.

Joe Mason

unread,
Jul 30, 2024, 1:12:36 PM7/30/24
to Peter Kasting, chromium-dev, cxx, Oliver Li
Ah, now that I think about it, std::tuple COULD be implemented using pointers instead of offsets internally, which would be unsafe if mapped in a different process.

John Admanski

unread,
Jul 30, 2024, 1:14:28 PM7/30/24
to Kyle Charbonneau, Joe Mason, John Stiles, chromium-dev, cxx, Oliver Li
If you want to share something via shared memory, you probably want to only do that with structures that are safely memcpy-able or byte-serializable, and is_trivially_copyable is the best way to enforce that AFAIK.

I think one could make an argument that std::tuple should be implemented in such a way that particular operations on it are trivial if those same operations are trivial on all of its members, but unfortunately the standard does not currently require this.

John Stiles

unread,
Aug 2, 2024, 2:45:55 PM8/2/24
to Joe Mason, chromium-dev, cxx, Oliver Li
In general, my understanding was that we shouldn't be using such a tuple anyway: https://google.github.io/styleguide/cppguide.html#Structs_vs._Tuples 

"Prefer to use a struct instead of a pair or a tuple whenever the elements can have meaningful names."

In my experience, we can almost always make a struct with names that are more meaningful than get<0> or get<2>. In this case, it also solves your issue because such a struct is trivially copyable.



On Tue, Jul 30, 2024 at 10:11 AM 'Joe Mason' via cxx <c...@chromium.org> wrote:
--

John Stiles

unread,
Aug 2, 2024, 2:45:55 PM8/2/24
to cxx, Joe Mason, oliv...@chromium.org, chromium-dev
In general, my understanding was that we shouldn't be using such a tuple anyway: https://google.github.io/styleguide/cppguide.html#Structs_vs._Tuples 

"Prefer to use a struct instead of a pair or a tuple whenever the elements can have meaningful names."

In my experience, we can almost always make a struct with names that are more meaningful than get<0> or get<2>. In this case, it also solves your issue because such a struct is trivially copyable.


John Admanski

unread,
Aug 2, 2024, 3:02:35 PM8/2/24
to John Stiles, cxx, Joe Mason, oliv...@chromium.org, chromium-dev
They're writing generic code that just has a list of type parameters, though, so that's actually an exceptional case that usually does work better with a tuple than with a struct. That's usually the only situation where I use them.

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.

Roland McGrath

unread,
Aug 2, 2024, 3:49:17 PM8/2/24
to Joe Mason, cxx
It's not infeasible to write your own tuple-like template type that accepts only trivially-copyable member types and yields a trivially-copyable tuple-like type.  It's specifically allowed to write your own partial specializations for std::tuple_size, std::tuple_element, and std::get, and that makes most of the tupley stuff (including `auto [x, y, z] = ...` bindings) work just like std::tuple.  But it is some hassle, even if you don't try to have all the exact corners of the std::tuple semantics.  It might be more tractable just to say your "generic" facility is generic for any single trivially-copyable type and require users of that facility to write their own trivial struct types to pack multiple things together.

Joe Mason

unread,
Aug 6, 2024, 1:11:24 PM8/6/24
to Roland McGrath, cxx
The problem with having every user write a trivial struct type is that members may have additional surprising requirements beyond trivially copyable. (The only one I know is that `std::atomic<T>` needs to be `is_lock_free` to actually be atomic over shared memory, but there might be other corner cases.) So I was hoping to pass a list of types, and assert that they're all safe to use over shared memory.

One option would be to have the user pass a struct and a list of member types to validate, but that's error prone.

In the meantime I have a prototype that builds a tuple-like struct from the list of types, but I'm still debating whether this is too much effort just for safety validation.

Adam Rice

unread,
Aug 7, 2024, 5:13:19 AM8/7/24
to Joe Mason, Roland McGrath, cxx
Is it safe to put struct padding in shared memory? I seem to remember that compilers could be very lax about what they do and do not copy into struct padding. In which case the same would apply to a tuple type, unless it included checks that the total size was equal to the sum of the sizes of the contained types.

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.

Roland McGrath

unread,
Aug 7, 2024, 1:59:41 PM8/7/24
to Adam Rice, Joe Mason, cxx
`std::has_unique_object_representations_v<T> || std::is_floating_point_v<T>` is a sufficient test for "has no padding bytes that could leak information".

Joe Mason

unread,
Aug 7, 2024, 2:33:17 PM8/7/24
to Roland McGrath, Adam Rice, cxx
Wouldn't that fail for a struct containing several floats without padding between them?
Reply all
Reply to author
Forward
0 new messages