Click here to Skip to main content
15,860,943 members
Articles / Desktop Programming / MFC
Article

Memory Map Class : Policy Based Design

Rate me:
Please Sign up or sign in to vote.
4.94/5 (16 votes)
26 Feb 2005CPOL6 min read 97.6K   1.1K   53   23
Memory Map class provides IPC using shared memory. The Policy based design provides flexibility, extensibility and easy to use class.

Introduction

Policy-based class design is a technique of simplifying a complex behavior by breaking it up into multiple abstractions called policies. Each policy abstracts only one behavioral or structural aspect of that class and lets the user control that aspect independent of others. Users can also override the default implementation of the policies to fit their requirements as long as they respect the policy interface.

Memory mapped files offer a unique memory management feature that allows applications to access files on the disk in the same way they access dynamic memory through pointer. More over the mapped memory can be accessed within the process or among multiple processes which can be used for inter-process communication (IPC).

There are various aspects of implementing a memory map class like creation of the memory map file (create new file, open only if the file exists, open in read only mode, open in r/w mode et al.), deletion of the memory map file (should the memory map file be deleted if the object goes out of scope or not), file truncation, file access mode (private to the process or shared with other processes) and so on.

This article explains how each of these aspects can be abstracted as policies and the benefits of this approach.

I will not go too deep in to the theoretical aspect of policy based design concept. There are plenty of resources available on the Internet. You can also refer to one of the best C++ books, Modern C++ Design by Andrei Alexandrescu, or his article "Policy-Based Class Design in C++". Readers are assumed to have basic understanding of C++, templates and memory map file. For more information about policy based design or memory map file, please see the resources listed in the References section.

Requirements

Let us take a real life scenario to use policy based design concept to implement memory mapped file. In a telephony voice mail system, the telephone call statistics like number of calls, number of Voice/Video calls, number of email/voice/fax messages sent etc.... needs to be monitored for reporting purpose. Each of these parameters could come from different components in different processes. Memory mapped file is the ideal solution as it allows mapped memory to be shared among multiple processes and allows for faster update. Even if the system crashes, you still have data available in file.

We want to design a generic memory mapped class which can be used for the above purpose. Let us collect the requirements.

  1. We want to create a new memory mapped file or open an existing one. If the file exists, we might want to truncate it.
  2. We want to open the file either in read-write mode or read-only mode (for monitoring process).
  3. Memory mapped file can be shared among multiple processes or it could be private to the process. (E.g. Multiple processes should be able to update the same file.)
  4. When the process destroys the memory map object, it should either preserve the underlying file or delete it.
  5. We might want to write data to the file synchronously or asynchronously. We might want to invalidate the section if data in memory is not consistent with that in the file.
  6. User of this class should not have to deal with creating/opening file and mapping to his user defined class. We should be able to just pass the file name to create memory map and access the memory map data as we are accessing class members.

Design

Let us look at each requirement and see how we can fulfill.

For the 1st and 2nd requirements, we want to create and open the file in different modes. These can be implemented using "Creation Policy" which will provide the different aspects of file creation. The interfaces for creation policy have two methods as given below.

class CreationPolicy
{
  public:
  // Open/Create a file
  static int OpenFile(const std::string &sFileName)
  { ..... }

    // Truncate the file
   static bool TruncateFile(int nFd, unsigned nFileSize, 
                            void **pMemFile)
   { ..... }
};

Below classes are created based on different requirements of the Creation Policy.

  • class CreateReadWrite
  • class CreateReadWriteTruncate
  • class OpenReadWrite
  • class OpenReadOnly

You can create your custom implementation of Creation Policy by overriding one of the default classes. See the source code for more details.

For the 3rd requirement, we want to share the mapped file either among the processes or keep it private to the process. Moreover we want memory protection in either READ-WRITE mode or READ-ONLY mode. These can be implemented using "Mapping Policy" as below.

class MappingPolicy
{
  public:
  // map the file
  static void* Map(unsigned nFileSize, int nMapFd)
  {
   ............
  }
};

I have provided the following two classes for "Mapping Policy". You can create your custom implementation and override these.

  • class SharedMapReadWrite
  • class PrivateMapReadWrite

4th requirement can be fulfilled by implementing the "Deletion Policy" as below.

class DeletePolicy
{
  public:
  // map the file
  static void DeleteFile(const std::string &sFileName)
  {
     ............
  }
};

I have provided the following two classes for Deletion Policy.

  • class Delete
  • class DoNotDelete

5th requirement can be implemented using "MemorySyncPolicy" which fulfills the different synchronization requirements.

class  MemorySyncPolicy
    {
    public:
        // syncronize data
        static bool Sync(void **pMemFile, unsigned nFileSize) 
        {
           .....
        }
    };

I have provided the following three classes as implementations for this policy. You can override these by providing your custom one.

  • class MemASync
  • class MemSync
  • class MemInvalidate

Before we look at the 6th requirement, let's put all the five policies together. I have provided pseudo code here. For detailed implementation, please see the source code.

In this class, by passing different policies, you can change the behavior of the class which will provide the flexibility and extensibility and allow you to reuse code. This class is defined in "Joshi" namespace.

template<typename CreationPolicy,
             typename DeletionPolicy, 
             typename MappingPolicy,
             typename MemSyncPolicy
             >
    class MemMapFile
    {
        int m_nMapFd;  // file descriptor
        unsigned m_nFileSize;
        std::string m_sFileName;
        void *m_pMemFile;
     public:
        MemMapFile(const unsigned nSize, const std::string &sFileName);
        virtual ~MemMapFile();
        void OpenFile() throw (MemMapFileException);
        void* MapFile() throw (MemMapFileException);
        void Sync() throw (MemMapFileException);
        void TruncateFile(const unsigned nSize) throw (MemMapFileException);
        void DeleteFile() throw();
        void Unmap() throw();
     private:
        // private functions not implemented
        MemMapFile();
        MemMapFile(const MemMapFile &);
        MemMapFile& operator=(const MemMapFile&);
};

Now, to fulfill the 6th requirement, I have designed a wrapper class which takes care of creating, deleting, truncating and mapping the file. It has default policies which can be overridden.

template<typename T, typename CreationPolicy=CreateReadWrite,
             typename DeletionPolicy=DoNotDelete, 
             typename MappingPolicy=SharedMapReadWrite,
             typename MemSyncPolicy=MemSync
             >
    class MemMapFilePtr
    {
        typedef MemMapFile<CreationPolicy,DeletionPolicy,MappingPolicy,MemSync> 
                MemMapFileDef;
        MemMapFileDef * m_pFile;
        T* m_pObj;

        // private functions not implemented
        MemMapFilePtr();
        MemMapFilePtr(const MemMapFilePtr &);
        MemMapFilePtr& operator=(const MemMapFilePtr&);

    public:

        // constructor
        explicit MemMapFilePtr(const std::string &sFileName, 
        unsigned nSize=sizeof(T)):m_pFile(0)
        {
            ................
        }
        // syncronize the data
        void Sync()
        {
           ..................
        }
        // destructor
        virtual ~MemMapFilePtr()
        {
           .................
        }
        // operator*
        T& operator*()
        {
            ...........
        }
       
        // operator->
        T* operator->()
        {
            ...............
        }
        
    };

Finally, below is the code snippet to test memory mapped file class designed using policy based concept.

Now let us go back and revisit the requirements and see how our implementation simplifies the use of the memory map class. Suppose we have class "MyClass" as defined below which needs to be mapped with a memory mapped file. For simplicity, I have made all member variables public.

class MyClass
{
  public:
  int  m_nInt;
  char m_cChar;
  char m_sBuff[10];
};
  • To implement the 1st requirement ( Creating/Opening/Truncating File):
    • Without truncating file:
      typedef Joshi::MemMapFilePtr<MyClass> MemMappedClass;
    • With truncating file:
      typedef Joshi::MemMapFilePtr<MyClass, CreateReadWriteTruncate> 
                                                       MemMappedClass;
  • To implement the 2nd requirement (Opening File):
    • For ReadOnly mode:
      typedef Joshi::MemMapFilePtr<MyClass, OpenReadOnly> MemMappedClass;
    • For Read-Write mode:
      typedef Joshi::MemMapFilePtr<MyClass, OpenReadWrite> MemMappedClass;
  • For the 3rd requirement (Private/Shared mapping):
    • Shared among Processes:
      typedef Joshi::MemMapFilePtr
          <MyClass, CreateReadWrite, DoNotDelete, SharedMapReadWrite >
          MemMappedClass;
    • Private to Process:
      typedef Joshi::MemMapFilePtr
          <MyClass, CreateReadWrite, DoNotDelete, privateMapReadWrite> 
          MemMappedClass;
  • For the 4th Requirement (Deletion of File):
    • Delete file:
      typedef Joshi::MemMapFilePtr<MyClass, CreateReadWrite, Delete > 
          MemMappedClass;
    • Do not delete file:
      typedef Joshi::MemMapFilePtr<MyClass, CreateReadWrite, DoNotDelete > 
          MemMappedClass;
  • For the 5th requirement(Syncronization in Memory and File):
    • Synchronize data synchronously:
      typedef Joshi::MemMapFilePtr
          <MyClass, CreateReadWrite, DoNotDelete, SharedMapReadWrite,MemSync >
          MemMappedClass;
    • Synchronize data asynchronously:
      typedef Joshi::MemMapFilePtr
          <MyClass, CreateReadWrite, DoNotDelete, SharedMapReadWrite,MemASync > 
          MemMappedClass;
    • Invalidate data if not consistent:
      typedef Joshi::MemMapFilePtr
          <MyClass, CreateReadWrite, DoNotDelete, 
          SharedMapReadWrite, MemInvalidate > 
          MemMappedClass;
  • For the 6th Requirement (Easy to Use), the code given below shows the creation and usage of the memory mapped file using the simplified way.
    int main()
    {
    
      // based on your requirements, replace below line with line defined in above 
      //requirement section.      
      try {
      typedef Joshi::MemMapFilePtr<MyClass> MemMappedClass;
          
      // create a object of wrapper class
      MemMappedClass MemClass("/tmp/mymemmap");
      // Write  in     
      // now use MemClass as pointer of MyClass object.
      // write data to memory map file
    t as pointer
      MemClass->m_nInt = 5;
      // dereference it and write char
      (*MemClass).m_cChar = 'a';
      // write as buff
      strcpy(MemClass->m_sBuff, "12345678");
       // read data from memory map file   
      std::cout << "m_nInt:"  << MemClass->m_nInt  << std::endl;
      std::cout << "m_cChar:" << MemClass->m_cChar << std::endl;
      std::cout << "m_sBuff:" << MemClass->m_sBuff << std::endl;
      }catch (Joshi::MemMapFileException &e) {
        std::cout << e.what() << std::endl;
      }
      return 0;
    }

Note: It supports only UNIX like operating systems. I have compiled and tested on Solaris and Linux OS.

Conclusion

Policy based design provides a lot of flexibility by allowing you to pick and choose the policies that make sense to your application needs. This also allows for reuse of existing code by either using the different policy implementations available in the class or providing custom behavior without modifying the existing class library.

References

  1. Queue Manager: Policy Based Design By Rohit Joshi
  2. Generic Pool: Policy Based Design By Rohit Joshi
  3. Policy-Based Class Design in C++ By Andrei Alexandresc
  4. IPC: Mapped Memory By Dave Marshall

Acknowldgement

Special thanks to Sathiya Thiruvengadathan for reviewing this article.

Please do vote and comment on this article !!.

License

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


Written By
Software Developer
United States United States
Rohit Joshi is a software engineer working for a telecom company in USA. He has development expirience using C, C++ ,C#, VoiceXML, ASR, IMAP, LDAP, HTTP, SIP, H323 on unix/linux and platforms.

Comments and Discussions

 
GeneralAbstract Pin
mbaocha4-May-09 16:23
mbaocha4-May-09 16:23 
GeneralRe: Abstract Pin
Rohit Joshi11-May-09 7:56
Rohit Joshi11-May-09 7:56 
Generalreally help me a lot to get a cross patform(win32 mac linux) solution for IPC. Pin
rotal25-Dec-06 4:12
rotal25-Dec-06 4:12 
GeneralRe: really help me a lot to get a cross patform(win32 mac linux) solution for IPC. Pin
Rohit Joshi26-Dec-06 5:23
Rohit Joshi26-Dec-06 5:23 
Generalterrible Pin
rm82212-Mar-05 23:22
rm82212-Mar-05 23:22 
GeneralRe: terrible Pin
Anonymous14-Mar-05 5:06
Anonymous14-Mar-05 5:06 
GeneralRe: terrible Pin
rm82215-Mar-05 1:20
rm82215-Mar-05 1:20 
GeneralRe: terrible Pin
Anonymous7-Apr-05 8:05
Anonymous7-Apr-05 8:05 
GeneralRe: terrible Pin
Rohit Joshi14-Apr-05 13:06
Rohit Joshi14-Apr-05 13:06 
QuestionWindows?? Pin
WREY24-Feb-05 3:14
WREY24-Feb-05 3:14 
AnswerRe: Windows?? Pin
Rohit Joshi24-Feb-05 14:51
Rohit Joshi24-Feb-05 14:51 
GeneralRe: Windows?? Pin
WREY26-Feb-05 3:02
WREY26-Feb-05 3:02 
Might you then replace those Unix files with the correct Windows equivalent in order for the sample to work?

Simply removing them only seems to make matters worst.

Thanks.

Smile | :)

William

Fortes in fide et opere!
GeneralRe: Windows?? Pin
Rohit Joshi26-Feb-05 5:43
Rohit Joshi26-Feb-05 5:43 
GeneralRe: Windows?? Pin
WREY15-Mar-05 1:17
WREY15-Mar-05 1:17 
GeneralRe: Windows?? Pin
zhuangkf28-Jun-06 17:24
zhuangkf28-Jun-06 17:24 
GeneralNice article!! Pin
Anonymous20-Feb-05 16:04
Anonymous20-Feb-05 16:04 
Generaljust because you can, doesn't mean you should! Pin
yafan20-Feb-05 11:19
yafan20-Feb-05 11:19 
GeneralRe: just because you can, doesn't mean you should! Pin
peakdetector20-Feb-05 13:29
peakdetector20-Feb-05 13:29 
GeneralRe: just because you can, doesn't mean you should! Pin
Rohit Joshi20-Feb-05 13:39
Rohit Joshi20-Feb-05 13:39 
GeneralRe: just because you can, doesn't mean you should! Pin
Tim Smith20-Feb-05 15:55
Tim Smith20-Feb-05 15:55 
GeneralRe: just because you can, doesn't mean you should! Pin
Rohit Joshi20-Feb-05 16:10
Rohit Joshi20-Feb-05 16:10 
GeneralRe: just because you can, doesn't mean you should! Pin
yafan22-Feb-05 4:50
yafan22-Feb-05 4:50 
GeneralRe: just because you can, doesn't mean you should! Pin
Rohit Joshi22-Feb-05 12:27
Rohit Joshi22-Feb-05 12:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.