Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

InterprocessSingleton - Convenient Template to Use Shared Memory Objects

4.33/5 (2 votes)
2 Feb 2009CPOL2 min read 33.3K   529  
This article describes basic concepts and code of C++ template which provides simple access to shared memory objects

Introduction

Interprocess communication is an important part of many distributed systems. Many libraries, such as Boost, give mechanisms for interprocess communication. Such a concept, as shared memory is one of the fastest mechanisms of data transmission between processes by one computer. This article represents a convenient template to work with objects in shared memory.

Required Technical Background

The basic concepts that were laid in the basis of the project are Boost.Interprocess and Boost.Serialization.
The library encapsulates interaction with Boost.Interprocess, thus the user does not need to know features of the library.

Boost.Serialization

Let's take a closer look at the Boost.Serialization library. The given generalized library provides convenient mechanisms for serialization/deserialization of objects, for example, their transfer on a network. In the given project, it is used for an object premise in a shared memory. To make object serializable, it is necessary to implement a method serialize or to steam methods load/save.

Example - method serialize is implemented, it both loads and stores data in archive:

C++
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>

class SeriazableClass
{
    // That line is added because serialize is private in this class
    friend class boost::serialization::access;

    std::string m_strValue;
    std::vector< std::string > m_vData;

    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & m_strValue;
        ar & m_vData;
    }

public:
    SerializableClass()
    {
        m_strValue = "test";
        m_vData.push_back( "vector_test1" );
        m_vData.push_back( "vector_test2" );
    }
};

Basic Ideas

The idea of Singleton design pattern is put in a template basis:

  • Unique instance of class creation
  • Maintenance of a uniform point of access to an interprocess shared object
  • Simple use of global objects

InterprocessSingleton is used to store serializable objects in shared memory.

The basic methods of InterprocessSingleton template are:

  • T& Instance() - Returns the reference to the global object
  • void Commit() - Keeps the current object in shared memory
  • void Clear() - Completely clears an object in shared memory

Also, a revision of the last stored object remains in the shared memory. Thus, the object is loaded from shared memory only when revision of the kept object is greater than revision of the current object in process. Access on read/write in shared memory is synchronised by interprocess mutex.

Method Instance() does the job, described above.

Important note: In shared segment, that object remains for which Commit() has been called last. That is, if instances of one shared object are simultaneously edited in different processes, and then Commit is called, in memory will remain the object for which Commit() was called last.

The basic scheme of work is as follows:

  1. Retrieving of the reference to the global object by call to Instance()
  2. Work and change an object
  3. Save changes in the shared memory by call to Commit()

It is also recommended to call Clear() method at the start of the main process to clear the previous shared instance of the object.

Using the Code

The basic principle of the following examples is that when you start several copies of the application, the data will remain in the shared area.

Example 1 - Use with POD (Plain Old Data)

C++
#include <interprocess.hpp>
#include <iostream>

int main()
{
    // Retrieve the reference to the global object
    int& nValue = InterprocessSingleton< int >::Instance();
    // Print a value of the object
    std::cout << "Value: " << nValue << std::endl;
    // Change the object
    nValue += 10;
    // Save changes
    InterprocessSingleton< int >::Commit();

    return 0;
}

Example 2 - Non-POD Data

C++
#include <interprocess.hpp>
#include <vector>
#include <boost/serialization/vector.hpp>
#include <iostream>

int main()
{
    // Retrieve the reference to the global object
    std::vector< int >& vData = InterprocessSingleton< std::vector< int > >::Instance();
    std::cout << "Container size: " << vData.size() << std::endl;
    // Change the object
    vData.push_back( 10 );
    // Save changes
    InterprocessSingleton< std::vector< int > >::Commit();

    return 0;
}

Example 3 - Own Class

C++
#include <interprocess.hpp>
#include <vector>
#include <iostream>

class MyClass
{
    friend class boost::serialization::access;

    std::string m_strValue1;
    int m_nValue2;

    // Implement that method to make object serializable
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & m_strValue1;
        ar & m_nValue2;
    }

    public:
        void PrintStatus()
        {
            std::cout << "String: " << m_strValue1 << std::endl;
            std::cout << "Integer: " << m_nValue2 << std::endl;
        }

        void ChangeClass()
        {
            m_strValue1 += "1";
            m_nValue2++;
        }
};

int main()
{
    MyClass& obj = InterprocessSingleton< MyClass >::Instance();
    obj.PrintStatus();
    obj.ChangeClass();
    InterprocessSingleton< MyClass >::Commit();

    return 0;
}

Points of Interest

I look forward to getting your comments and suggestions.

History

  • 1st February, 2009: First version

License

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