InterprocessSingleton - Convenient Template to Use Shared Memory Objects






4.33/5 (2 votes)
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:
#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 objectvoid Commit()
- Keeps the current object in shared memoryvoid 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:
- Retrieving of the reference to the global object by call to
Instance()
- Work and change an object
- 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)
#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
#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
#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