Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Ultimate Shared Memory: A Flexible Class for Interprocess Memory Sharing

4.81/5 (22 votes)
14 Feb 2019CPOL3 min read 58.9K   1.3K  
A flexible memory sharing class across processes and threads with locking support

Introduction

This is a template class for sharing memory across threads or processes in Windows. It provides locking mechanisms, notifications and more!

Using the Code

Instantiation of the Class

C++
// usm(const wchar_t* string_id = 0,bool Init = false,size_t csz = 1048576,DWORD MaxTh = 100)
usm<char> sm(L"{4BF41A4E-ACF9-4E1D-A479-14DE9FF83BC2}",false,1000,100);
  • string_id is a string that uniquely identifies kernel objects for this shared memory. All instances of usm that use the same string_id are accessing the same memory. It's recommended to use a CLSID value to avoid possible collisions with other applications using usm.
    If you pass 0, then the object is left uninitialized. To initialize the object, you must call CreateInit which takes the same arguments as the constructor.
  • Init specifies whether initialization of the object must be included in the constructor. If this is true and an error occurs, the constructor throws an exception.
  • csz specifies the number of items to allocate. Total memory allocation is csz*sizeof(char) in the above example.
  • MaxTh is the maximum number of threads that will be accessing the shared memory. This must be the same number in any instances of the class that share the same string_id, or memory corruption will occur.

The library allocates memory to store events for each thread, so MaxTh must be the same in all instances of the class that use the same memory (i.e., same string_id).

Initialization of the Class

C++
//  int Initialize();

This function creates the kernel object handles required for synchronization. Calling End() destroys these handles. Initialize() returns 1 on success, and -1 on error. If Initialize() fails, End() cleans up all remaining handles. The destructor of the class also calls End().

If another thread is writing to the shared memory, this function waits until the writing is over.

Reading Shared Memory

C++
// unsigned long long ReadData(T* b,size_t sz,size_t offset = 0,bool FailIfNotReady = false);

This function (which internally calls BeginRead() and EndRead()) does the following:

  • If FailfNotReady is true and any writing thread has not finished writing, the function immediately returns -1. If FailIfNotReady is false, the function waits until any writing thread has finished writing.
  • Marks the current state of the calling thread as reading. This forces any writing thread to wait until reading is finished.
  • Reads the data.
  • Marks the state of the calling thread as not reading. This allows any writing thread to resume (as long as there are no more reading threads).
  • Returns the same value as sz.

Writing Shared Memory

C++
// unsigned long long WriteData(const T* b,size_t sz,size_t offset = 0,bool FailIfNotReady = false);

This function (which internally calls BeginWrite() and EndWrite()) does the following:

  • If FailfNotReady is true and any other thread is writing or reading, the function immediately returns -1. If FailIfNotReady is false, the function waits until any other writing or reading thread has finished.
  • Marks the current state of the calling thread as writing. This forces any other thread (reading or writing) to wait until writing is finished. This also forces any thread that is initializing the object with Initialize() to wait.
  • Writes the data.
  • Marks the state of the calling thread as not writing. This allows any other writing or reading thread to resume. The function also sets an event that the memory has been written.
  • Returns the same value as sz.

If you need the pointer to the data, you can call BeginRead() (which must be followed by a call to EndRead()) and BeginWrite() (which must be followed by a call to EndWrite()).

C++
const T* BeginRead(bool FailOnNotReady = false);
void EndRead();
T* BeginWrite(bool FailOnNotReady = false);
void EndWrite();

Notifications

C++
// DWORD NotifyOnRead(bool Wait);
// DWORD NotifyWrite(bool Wait);
  • If you call NotifyOnRead, this function with Wait == false, then the function returns:
    • -1 if the function fails
    • -2 if there are no other registered threads
    • WAIT_TIMEOUT if any thread is still reading
    • WAIT_OBJECT_0 or a greater value if any of the reading threads has finished
  • If you call NotifyOnRead with Wait == true, the return values are the same, except that the function does not return if all threads are still reading.

NotifyOnWrite returns WAIT_OBJECT_0 if a thread has just finished writing (note that this event is auto-reset). If a thread is not writing or still writing, NotifyOnWrite either returns WAIT_TIMEOUT (if you call it with Wait == false) or it waits until a thread has completed writing.

Hopefully, this class will be helpful for you. It's already very helpful for me.
Good luck!

History

  • 2/11/2014 - Added the ability to initialize later, and added copy constructor and operator =, and added offset to read/write functions
  • 1/11/2014 - First release

License

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