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...