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
{
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:
- 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()
{
int& nValue = InterprocessSingleton< int >::Instance();
std::cout << "Value: " << nValue << std::endl;
nValue += 10;
InterprocessSingleton< int >::Commit();
return 0;
}
Example 2 - Non-POD Data
#include <interprocess.hpp>
#include <vector>
#include <boost/serialization/vector.hpp>
#include <iostream>
int main()
{
std::vector< int >& vData = InterprocessSingleton< std::vector< int > >::Instance();
std::cout << "Container size: " << vData.size() << std::endl;
vData.push_back( 10 );
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;
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