36 #include <boost/version.hpp>
40 #include <type_traits>
46 static util::log::Logger &getIPCRingBufferLogger() {
47 static util::log::LoggerPtr logger =
48 util::log::make_logger(
"IPCRingBuffer");
56 static IPCRingBuffer::abi_level_type SHM_SOURCE_ABI_LEVEL = 0;
62 #if (BOOST_VERSION > 106300)
64 "Using an untested Boost version - inspect the Boost Interprocess release notes/changelog to see if any ABI breaks affect us."
68 #if (BOOST_VERSION < 105400)
70 "Boost Interprocess pre-1.54 on Win32 is ABI-incompatible with newer Boost due to changed bootstamp function."
77 static_assert(std::is_same<IPCRingBuffer::BackendType,
78 ipc::SharedMemoryBackendType>::value,
79 "The typedefs IPCRingBuffer::BackendType and "
80 "ipc::SharedMemoryBackendType must remain in sync!");
82 std::is_same<IPCRingBuffer::value_type, OSVR_ImageBufferElement>::value,
83 "The ring buffer's individual byte type must match the image buffer "
86 namespace bip = boost::interprocess;
90 template <
typename T,
typename ManagedMemory>
91 using ipc_deleter_type =
92 typename ManagedMemory::template deleter<T>::type;
98 template <
typename T,
typename ManagedMemory>
99 inline void destroy_unique_instance(ManagedMemory &shm) {
100 auto result = shm.template find<T>(bip::unique_instance);
102 shm.template destroy<T>(bip::unique_instance);
109 static size_t computeRequiredSpace(IPCRingBuffer::Options
const &
opts) {
110 size_t alignedEntrySize = opts.getEntrySize() + opts.getAlignment();
111 size_t dataSize = alignedEntrySize * (opts.getEntries() + 1);
113 static const size_t BOOKKEEPING_SIZE =
114 (
sizeof(detail::Bookkeeping) +
115 (
sizeof(detail::ElementData) * opts.getEntries())) *
117 return dataSize + BOOKKEEPING_SIZE;
120 class SharedMemorySegmentHolder {
122 SharedMemorySegmentHolder() : m_bookkeeping(nullptr) {}
123 virtual ~SharedMemorySegmentHolder(){};
125 detail::Bookkeeping *getBookkeeping() {
return m_bookkeeping; }
127 virtual uint64_t getSize()
const = 0;
128 virtual uint64_t getFreeMemory()
const = 0;
131 detail::Bookkeeping *m_bookkeeping;
134 template <
typename ManagedMemory>
135 class SegmentHolderBase :
public SharedMemorySegmentHolder {
137 typedef ManagedMemory managed_memory_type;
139 virtual uint64_t getSize()
const {
return m_shm->get_size(); }
140 virtual uint64_t getFreeMemory()
const {
141 return m_shm->get_free_memory();
145 unique_ptr<managed_memory_type> m_shm;
147 template <
typename ManagedMemory>
148 class ServerSharedMemorySegmentHolder
149 :
public SegmentHolderBase<ManagedMemory> {
151 typedef SegmentHolderBase<ManagedMemory> Base;
152 ServerSharedMemorySegmentHolder(IPCRingBuffer::Options
const &opts)
153 : m_name(opts.getName()) {
154 getIPCRingBufferLogger().debug() <<
"Creating segment, name "
155 << opts.getName() <<
", size "
156 << computeRequiredSpace(opts);
158 removeSharedMemory();
161 Base::m_shm.reset(
new ManagedMemory(
162 bip::create_only, opts.getName().c_str(),
163 computeRequiredSpace(opts)));
164 }
catch (bip::interprocess_exception &e) {
165 getIPCRingBufferLogger().error()
166 <<
"Failed to create shared memory segment "
167 << opts.getName() <<
" with exception: " << e.what();
171 Base::m_bookkeeping =
172 detail::Bookkeeping::construct(*Base::m_shm, opts);
175 virtual ~ServerSharedMemorySegmentHolder() {
176 detail::Bookkeeping::destroy(*Base::m_shm);
177 removeSharedMemory();
180 void removeSharedMemory() {
181 ipc::device_type<ManagedMemory>::remove(m_name.c_str());
188 template <
typename ManagedMemory>
189 class ClientSharedMemorySegmentHolder
190 :
public SegmentHolderBase<ManagedMemory> {
192 typedef SegmentHolderBase<ManagedMemory> Base;
193 ClientSharedMemorySegmentHolder(
194 IPCRingBuffer::Options
const &opts) {
195 getIPCRingBufferLogger().debug() <<
"Finding segment, name "
198 Base::m_shm.reset(
new ManagedMemory(
199 bip::open_only, opts.getName().c_str()));
200 }
catch (bip::interprocess_exception &e) {
201 getIPCRingBufferLogger().error()
202 <<
"Failed to open shared memory segment "
203 << opts.getName() <<
" with exception: " << e.what();
206 Base::m_bookkeeping = detail::Bookkeeping::find(*Base::m_shm);
209 virtual ~ClientSharedMemorySegmentHolder() {}
215 template <
typename ManagedMemory>
216 inline unique_ptr<SharedMemorySegmentHolder>
217 constructMemorySegment(IPCRingBuffer::Options
const &opts,
219 unique_ptr<SharedMemorySegmentHolder> ret;
222 new ServerSharedMemorySegmentHolder<ManagedMemory>(opts));
225 new ClientSharedMemorySegmentHolder<ManagedMemory>(opts));
227 if (
nullptr == ret->getBookkeeping()) {
230 getIPCRingBufferLogger().debug()
231 <<
"size: " << ret->getSize()
232 <<
", free: " << ret->getFreeMemory();
240 : m_buf(nullptr), m_seq(0), m_data(
std::move(data)) {
242 m_buf = m_data->buffer;
244 m_data->shm = std::move(shm);
248 IPCRingBuffer::BufferReadProxy::BufferReadProxy(
250 : m_buf(nullptr), m_seq(0), m_data(
std::move(data)) {
251 if (
nullptr != m_data) {
252 m_buf = m_data->buffer;
254 m_data->shm = std::move(shm);
258 IPCRingBuffer::smart_pointer_type
260 return smart_pointer_type(m_data, m_buf);
263 IPCRingBuffer::Options::Options()
264 : m_shmBackend(ipc::DEFAULT_MANAGED_SHM_ID) {}
266 IPCRingBuffer::Options::Options(std::string
const &name)
267 : m_name(ipc::make_name_safe(name)),
268 m_shmBackend(ipc::DEFAULT_MANAGED_SHM_ID) {}
270 IPCRingBuffer::Options::Options(std::string
const &name,
272 : m_name(ipc::make_name_safe(name)), m_shmBackend(backend) {}
274 IPCRingBuffer::Options &
276 m_name = ipc::make_name_safe(name);
280 IPCRingBuffer::Options &
283 m_alignment = alignment;
287 IPCRingBuffer::Options &
293 IPCRingBuffer::Options &
295 m_entrySize = entrySize;
298 class IPCRingBuffer::Impl {
300 Impl(unique_ptr<SharedMemorySegmentHolder> &&segment,
302 : m_seg(
std::move(segment)), m_bookkeeping(nullptr), m_opts(opts) {
303 m_bookkeeping = m_seg->getBookkeeping();
304 m_opts.setEntries(m_bookkeeping->getCapacity());
305 m_opts.setEntrySize(m_bookkeeping->getBufferLength());
308 detail::IPCPutResultPtr
put() {
309 return m_bookkeeping->produceElement();
312 detail::IPCGetResultPtr
get(sequence_type num) {
313 detail::IPCGetResultPtr ret;
314 auto boundsLock = m_bookkeeping->getSharableLock();
315 auto elt = m_bookkeeping->getBySequenceNumber(num, boundsLock);
316 if (
nullptr != elt) {
317 auto readerLock = elt->getSharableLock();
318 auto buf = elt->getBuf(readerLock);
320 ret.reset(
new detail::IPCGetResult{buf, std::move(readerLock),
327 detail::IPCGetResultPtr ret;
328 auto boundsLock = m_bookkeeping->getSharableLock();
329 auto elt = m_bookkeeping->back(boundsLock);
330 if (
nullptr != elt) {
331 auto readerLock = elt->getSharableLock();
332 auto buf = elt->getBuf(readerLock);
334 ret.reset(
new detail::IPCGetResult{
335 buf, std::move(readerLock),
336 m_bookkeeping->backSequenceNumber(boundsLock),
nullptr});
341 Options const &getOpts()
const {
return m_opts; }
344 unique_ptr<SharedMemorySegmentHolder> m_seg;
345 detail::Bookkeeping *m_bookkeeping;
354 unique_ptr<SharedMemorySegmentHolder> segment;
356 switch (opts.getBackend()) {
358 case ipc::BASIC_MANAGED_SHM_ID:
360 constructMemorySegment<ipc::basic_managed_shm>(opts, doCreate);
363 #ifdef OSVR_HAVE_WINDOWS_SHM
364 case ipc::WINDOWS_MANAGED_SHM_ID:
365 segment = constructMemorySegment<ipc::windows_managed_shm>(
370 #ifdef OSVR_HAVE_XSI_SHM
371 case ipc::SYSV_MANAGED_SHM_ID:
373 constructMemorySegment<ipc::sysv_managed_shm>(opts, doCreate);
378 getIPCRingBufferLogger().error()
379 <<
"Unsupported/unrecognized shared memory backend: "
380 << int(opts.getBackend());
387 unique_ptr<Impl> impl(
new Impl(std::move(segment), opts));
388 ret.reset(
new IPCRingBuffer(std::move(impl)));
393 return SHM_SOURCE_ABI_LEVEL;
397 return m_constructorHelper(opts,
true);
400 return m_constructorHelper(opts,
false);
403 IPCRingBuffer::IPCRingBuffer(unique_ptr<Impl> &&impl)
404 : m_impl(
std::move(impl)) {}
409 return m_impl->getOpts().getBackend();
413 return m_impl->getOpts().getName();
417 return m_impl->getOpts().getEntrySize();
421 return m_impl->getOpts().getEntries();
425 return BufferWriteProxy(m_impl->put(), shared_from_this());
431 std::memcpy(proxy.get(), data, len);
432 return proxy.getSequenceNumber();
436 return BufferReadProxy(m_impl->get(num), shared_from_this());
440 return BufferReadProxy(m_impl->getLatest(), shared_from_this());
Options & setAlignment(alignment_type alignment)
Sets the alignment for each entry, which must be a power of 2 (rounded up to the nearest if it's not)...
Options & setName(std::string const &name)
sets the name, after sanitizing the input string.
BufferReadProxy get(sequence_type num)
Gets access to an element in the buffer by sequence number: returns a proxy object that behaves mostl...
BufferReadProxy getLatest()
Gets access to the most recent element in the buffer: returns a proxy object that behaves mostly like...
Header for basic internal log reference. To actually log to the produced loggers, include
shared_ptr< IPCRingBuffer > IPCRingBufferPtr
Pointer type for holding a shared memory ring buffer.
static IPCRingBufferPtr find(Options const &opts)
Named constructor, for use by client processes: accesses an IPC ring buffer using the options structu...
uint16_t getEntries() const
Returns the total capacity, in number of buffer entries, of this ring buffer.
~IPCRingBuffer()
Destructor.
static abi_level_type getABILevel()
Gets an integer representing a unique arrangement of the internal shared memory layout, such that if two processes try to communicate with different ABI levels, they will (likely) not succeed and thus should not try.
uint32_t sequence_type
The sequence number is automatically incremented with each "put" into the buffer. Note that...
BackendType getBackend() const
Returns an integer identifying the IPC backend used.
Header defining the types placed into shared memory for an IPCRingBuffer.
BufferWriteProxy(BufferWriteProxy const &)=delete
not copyable
std::string const & getName() const
Returns the name string used to create or find this ring buffer.
Options & setEntries(entry_count_type entries)
Sets the number of entries in the ring buffer.
Options & setEntrySize(entry_size_type entrySize)
Sets the size of each entry in the ring buffer.
Header to include for OSVR-internal usage of the logging mechanism: provides the needed definition of...
smart_pointer_type getBufferSmartPointer() const
Gets a smart pointer to the buffer that shares ownership of the underlying resources of this object...
uint32_t getEntrySize() const
Returns the size of each individual buffer entry, in bytes.
unsigned char OSVR_ImageBufferElement
Type for raw buffer access to image data.
BufferWriteProxy put()
Gets a proxy object for putting data in the next element in the buffer. You're responsible for doing ...
static IPCRingBufferPtr create(Options const &opts)
Named constructor, for use by server processes: creates a shared memory ring buffer given the options...