Click here to Skip to main content
15,883,535 members
Articles / Programming Languages / C++

InterprocessSingleton - Convenient Template to Use Shared Memory Objects

Rate me:
Please Sign up or sign in to vote.
4.33/5 (2 votes)
2 Feb 2009CPOL2 min read 32.7K   523   31  
This article describes basic concepts and code of C++ template which provides simple access to shared memory objects
#ifndef _INTERPROCESS_H_
#define _INTERPROCESS_H_

#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <sstream>

#include "singleton.hpp"

/// Class incapsulates shared memory segment
class SharedMemorySegment
{
    // Segment name
    std::string m_strSegmentName;
    // Boost segment object to access shared memory
    std::auto_ptr< boost::interprocess::shared_memory_object > m_ptrSegment;
    // Boost mapped region to map shared memory to address space
    std::auto_ptr< boost::interprocess::mapped_region > m_ptrRegion;
    
    // This method resizes shared memory segment to specified size. If bExactSize is true, than greater size segment will be shrinked.
    void ResizeSegment( int nSize, bool bExactSize );
    
public:
    /// Class constructor
    /// @param strSegmentName   Shared memory segment name
    SharedMemorySegment( const std::string& strSegmentName );

    /// Method resizes and returns pointer to memory segment
    /// @param nSize        Needed neegment size
    /// @param bExactSize   Set it to TRUE if you need segment of exactly nSize bytes
    void* GetMemory( int nSize, bool bExactSize = false );

    /// Method returns current segment size
    int GetSize();
    /// Method returns segment name
    std::string GetSegmentName();
    /// Method removes segment
    void Remove();
};

/// Serializer class which is used to store data in shared memory
template < class T >
class ObjectSerializer
{
    // Revision id of current object
    int m_nRevId;
    // Current object instance
    T m_obj;
    // Stream stores serialized object data
    std::stringstream m_stream;
    // Flag indicates when reserialization in stream needed
    bool m_bNeedUpdateStream;
    
    // Method updates stream
    void UpdateStream()
    {
        if( !m_bNeedUpdateStream )
            return;
   
        m_stream.str( "" );
        boost::archive::binary_oarchive ar( m_stream );
        int nNewRevId = m_nRevId + 1;
        ar << nNewRevId;
        ar << m_obj;
      
        m_bNeedUpdateStream = false;
    }
    
public:
    /// Constructor
    ObjectSerializer()
    : m_nRevId( -1 ),
      m_stream( std::ios_base::out | std::ios_base::in | std::ios_base::binary ),
      m_bNeedUpdateStream( true )
    {
    }
    
    /// This method serializes current object to given data block
    /// @param lpData   Pointer to data block
    /// @param nSize    Point to integer which retrieves stored data size
    void Serialize( void* lpData, int& nSize )
    {
        UpdateStream();
       
        memcpy( lpData, m_stream.str().c_str(), m_stream.str().size() );
        nSize = m_stream.str().size();
    }
    
    /// This method deserializes object from given data block
    /// @param lpData   Pointer to data block
    /// @param nSize    Size of data block
    void DeSerialize( const void* lpData, int nSize )
    {
        m_stream.str( std::string( ( const char* )lpData, nSize ) );
      
        try
        {
            boost::archive::binary_iarchive ar( m_stream );
            int nRevId;
            ar >> nRevId;

            // If shared object revision is greater than current object revision - load it
            if( nRevId > m_nRevId )
            {
                ar >> m_obj;
                m_nRevId = nRevId;
                m_bNeedUpdateStream = false;
            }
        }
        catch( boost::archive::archive_exception& e )
        {
            m_obj = T();
            m_bNeedUpdateStream = true;
        }
    }

    /// This methos updates last revision id from shared memory
    /// @param lpData   Pointer to shared data block
    /// @param nSize    Size of shared data block
    void UpdateRevisionId( const void* lpData, int nSize )
    {
        m_stream.str( std::string( ( const char* )lpData, nSize ) );
       
        try
        {
            boost::archive::binary_iarchive ar( m_stream );
            ar >> m_nRevId;
        }
        catch( boost::archive::archive_exception& e )
        {
        }
        m_bNeedUpdateStream = true;
    }
    
    /// This methos returns size that is required to store current object
    int GetObjectSize()
    {
        UpdateStream();
        return m_stream.str().size();
    }
    
    T* GetObject()
    {
        m_bNeedUpdateStream = true;
        return &m_obj;   
    }
};

/// Singleton class which can store data in shared memory segment
template < class T, class SerializeStrategy = ObjectSerializer< T > > 
class InterprocessSingleton
{
    // Shared memory object
    SharedMemorySegment m_shms;
    // Object serialization strategy
    SerializeStrategy m_serializer;
    // Name of interprocess mutex
    std::string m_strMutexName;
    // Used muted type
    typedef boost::interprocess::named_mutex MutexType;
    // Pointer to mutex which synchronize access to shared memory
    std::auto_ptr< MutexType > m_ptrMtx;

    typedef InterprocessSingleton< T, SerializeStrategy > Self; 
    // Singleton class is a friend to construct InterprocessSingleton object which constructor is private
    friend class Singleton< Self >;
    
    /// Class constructor
    InterprocessSingleton()
    : m_shms( std::string( "InterprocessSingletonMemory" ) + typeid( T ).name() ),
      m_strMutexName( std::string( "InterprocessSingletonMemory" ) + typeid( T ).name() + "_mtx" )
    {
    }

    /// Hide copy constructor and assignment operator to prevent copying
    InterprocessSingleton( const InterprocessSingleton& );
    InterprocessSingleton& operator=( const InterprocessSingleton& );
public:
    
    /// This method returns reference to instance of interprocess object
    static T& Instance()
    {
        Self& obj = Singleton< Self >::Instance();
        if( !obj.m_ptrMtx.get() )
            obj.m_ptrMtx = std::auto_ptr< MutexType >( new MutexType( boost::interprocess::open_or_create, obj.m_strMutexName.c_str() ) );
        
        boost::interprocess::scoped_lock< MutexType > lock( *obj.m_ptrMtx );
    
        int nSize = obj.m_serializer.GetObjectSize();
        obj.m_serializer.DeSerialize( obj.m_shms.GetMemory( nSize ), obj.m_shms.GetSize() );
        
        return *obj.m_serializer.GetObject();
    }
    
    /// Method saves current object in shared memory
    static void Commit()
    {
        Self& obj = Singleton< Self >::Instance();
        if( !obj.m_ptrMtx.get() )
            obj.m_ptrMtx = std::auto_ptr< MutexType >( new MutexType( boost::interprocess::open_or_create, obj.m_strMutexName.c_str() ) );

        boost::interprocess::scoped_lock< MutexType > lock( *obj.m_ptrMtx );
        
        int nSize = obj.m_serializer.GetObjectSize();
        obj.m_serializer.UpdateRevisionId( obj.m_shms.GetMemory( nSize ), obj.m_shms.GetSize() );
        obj.m_serializer.Serialize( obj.m_shms.GetMemory( nSize, true ), nSize );
    }
    
    /// Method clears all data: deletes shared memory segment, deletes interprocess mutex
    static void Clear()
    {
        Self& obj = Singleton< Self >::Instance();

        obj.m_shms.Remove();
        MutexType::remove( obj.m_strMutexName.c_str() );
    }
};

#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Russian Federation Russian Federation
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions