Click here to Skip to main content
Click here to Skip to main content
Go to top

Synchronization of multiple reader and writer processes using Win32 APIs

, 10 Feb 2006
Rate this:
Please Sign up or sign in to vote.
An article on the use of shared memory and events.

Introduction

This source code demonstrates how to use Win32 events to solve the multiple readers and writers problem. On compilation of this source code, you will get a console application that can act as both a reader and a writer of the shared memory. Multiple instances of the same executable should be executed for testing. Only one write operation is allowed at a time. If there is already a write operation going on, all other write and read operations will get blocked. However, multiple read operations are allowed. If read operations are underway, a write operation will wait for all read operations to get over before proceeding. This synchronization mechanism ensures proper synchronization of readers and writers, and helps in prevention of data corruption at all times.

Background

I was asked to design a solution for the multiple readers and writers problem, in a recent interview. I described at an abstract level what I had in mind. But the more that I thought on this, the more I was convinced that I should implement it to flesh out the ideas that I had in mind. Hence, this source code.

Using the code

This source code has a shared memory which the readers and writers are trying to use. The synchronization is done using events. I have tried to add comments at the right places to make understanding of this source code easier.

Although, this code is in C++, it can easily be converted to C. This program doesn't use any object oriented concept or design.

The code includes <tchar.h> and uses generic functions at many places. However, this code was compiled and tested with ASCII builds only. The code may not compile or work for Unicode builds.

Following is the initialization function which initializes the events and the shared memory:

bool Initialize()
{
     //Trying to be defensive
     for (int ii = 0; ii < MAX_READ_PROCESSES_ALLOWED; ii++)
     {
          g_hReadEvent[ii] = NULL;
     }
     
     TCHAR szBuffer[32];  //big enough to hold the name
     
     //Creating Read events
     for (ii = 0; ii < MAX_READ_PROCESSES_ALLOWED; ii++)
     {
          _stprintf(szBuffer, _T("%s %d"), 
                             g_szReadEventName, ii);
          
          //Creates or opens event depending, 
          //on whether already exists or not
          g_hReadEvent[ii] = CreateEvent( 
               NULL,        //default security
               false,       //auto reset
               true,        //default state signaled
               szBuffer);
          
          if (NULL == g_hReadEvent[ii])
          {
               return false;
          }
     }
     
     //Write Event
     //Creates or opens event, depending 
     //on whether already exists or not
     g_hWriteEvent = CreateEvent( 
          NULL,             //default security
          false,            //auto reset
          true,             //default state signaled
          g_szWriteEventName);
     
     if (NULL == g_hWriteEvent)
     {
          return false;
     }
     
     //Shared Memory Stuff
     //Creates or opens shared memory, 
     //depending on whether already exists or not
     g_hSharedMemory = CreateFileMapping(
          INVALID_HANDLE_VALUE,    // use paging file
          NULL,                    // default security 
          PAGE_READWRITE,          // read/write access
          0,                       // max. object size 
          MAX_SH_MEM_SIZE,         // buffer size  
          g_szShareMemoryName);    // name of mapping object
     
     if (NULL == g_hSharedMemory || 
         INVALID_HANDLE_VALUE == g_hSharedMemory) 
     { 
          cout << "Error occured while" 
                  " creating file mapping object :" 
               << GetLastError() << "\n";
          return false;
     }
     
     g_pBuffer = 
          (LPTSTR) MapViewOfFile(g_hSharedMemory, // handle to map object
          FILE_MAP_ALL_ACCESS, // read/write permission
          0,                   
          0,                   
          MAX_SH_MEM_SIZE);           
     
     if (NULL == g_pBuffer) 
     { 
          cout << "Error occured while mapping" 
                  " view of the file :" << GetLastError() 
               << "\n";
          return false;
     }
     
     return true;
}

Following is the read function which blocks itself on write and read events:

void ReadAndPrint()
{
     cout << "Trying to read and print the shared memory...\n";
     
     //Wait to make sure all writing 
     //operations are done and data is in sync
     bool bContinue = true;
     
     while(bContinue)  //We want to block until we get to read
     {
          cout << "Waiting for write operation to complete...\n";
          
          DWORD dwWaitResult = WaitForSingleObject(g_hWriteEvent, INFINITE );
          
          if (WAIT_OBJECT_0 == dwWaitResult)
          {
               bool bEventFound = false;
               
               for (int ii = 0; ii < MAX_READ_PROCESSES_ALLOWED; ii++)
               {
                    DWORD dwWaitResult = 
                      WaitForSingleObject(g_hReadEvent[ii], WAIT_TIME_OUT);
                    
                    if (WAIT_OBJECT_0 == dwWaitResult)
                    //The state of the specified object is signaled
                    {
                         bEventFound = true;
                         
                         cout << "Setting the Write Event...\n";
                         SetEvent(g_hWriteEvent);
                         
                         //Reading the shared memory
                         cout << "Shared Memory: " 
                              << g_pBuffer << "\n";
                         
                         cout << "Setting the Read Event...\n";
                         SetEvent(g_hReadEvent[ii]);
                         
                         bContinue = false;
                         break; //get out of the for loop
                    }
                    else
                    {
                         continue;
                         //The time-out interval elapsed, 
                         //and the object's state is nonsignaled.
                    }
               } // for
               
               if (false == bEventFound)
               {
                    cout << "Setting the Write Event...\n";
                    SetEvent(g_hWriteEvent);
                    
                    //SwitchToThread();
                    Sleep(WAIT_TIME_OUT);
               }
          }
          else
          {
               cout << "Error occured while waiting :" 
                    << GetLastError() << "\n";
          }
          
     } //while
}

Similarly, the write function uses events to synchronize:

void WriteAndPrint()
{
     cout << "Trying to write and print the shared memory...\n";
     
     cout << "Waiting for write operation to complete...\n";
     
     //Wait to make sure all writing operations are done and data is in sync
     if (WAIT_OBJECT_0 == WaitForSingleObject(g_hWriteEvent, INFINITE ))
     {
          cout << "Waiting for all read operations to complete...\n";
          
          DWORD dwWaitResult = WaitForMultipleObjects( 
               MAX_READ_PROCESSES_ALLOWED,   // number of handles in array
               g_hReadEvent,  // array of read-event handles
               TRUE,         // wait until all are signaled
               INFINITE);    // indefinite wait
          
          if (WAIT_OBJECT_0 == dwWaitResult)
          {
               cout << "Enter a string (without spaces): ";
               
               cin >> g_pBuffer;
               
               //Reading the shared memory
               cout << "Shared Memory: " << g_pBuffer << "\n";
          }
          else
          {
               cout << "Error occured while waiting :" 
                    << GetLastError() << "\n";
          }
          
          cout << "Setting the Write Event...\n";
          SetEvent(g_hWriteEvent);
          
          cout << "Setting the Read Events...\n";
          for (int ii = 0; ii < MAX_READ_PROCESSES_ALLOWED; ii++)
          {
               SetEvent(g_hReadEvent[ii]);
          }
     }
     else
     {
          cout << "Error occured while waiting :" 
               << GetLastError() << "\n";
     }
}

The de-initialization function is shown next. This function takes care of the de-initialization of the shared memory and events.

void DeInitialize()
{
     for (int ii = 0; ii < MAX_READ_PROCESSES_ALLOWED; ii++)
     {
          CloseHandle(g_hReadEvent[ii]);
     }
     
     CloseHandle(g_hWriteEvent);
     
     UnmapViewOfFile(g_pBuffer);
     
     CloseHandle(g_hSharedMemory);
}

Points of Interest

If you are a newbie to events, make sure you understand the difference between manual and auto-reset events. I have used auto-reset events, they are very handy to use.

History

  • Feb 2nd 2006 - Removed a wrong statement - CloseHandle(g_hReadEvent); from Initialize().

License

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

Share

About the Author

Swarajya Pendharkar
Software Developer (Senior)
India India
No Biography provided

Comments and Discussions

 
GeneralMy vote of 4 PinmemberPranit Kothari27-Oct-12 8:20 
GeneralMicro-corrigendum !!! [modified] Pinmemberramayan10220-May-08 14:17 
GeneralRe: Micro-corrigendum !!! PinmemberSwarajya Pendharkar23-May-08 4:20 
QuestionWin32 Services Pinmemberscott.tiger16-Feb-07 4:31 
AnswerRe: Win32 Services PinmemberSwarajya Pendharkar17-Feb-07 2:34 
GeneralRe: Win32 Services Pinmemberscott.tiger17-Feb-07 12:22 
GeneralRe: Win32 Services PinmemberSwarajya Pendharkar17-Feb-07 19:45 
GeneralRe: Win32 Services Pinmemberscott.tiger20-Feb-07 22:26 
GeneralRe: Win32 Services PinmemberSwarajya Pendharkar20-Feb-07 22:50 
GeneralRe: Win32 Services Pinmemberscott.tiger20-Feb-07 22:58 
GeneralRe: Win32 Services PinmemberSwarajya Pendharkar20-Feb-07 23:32 
GeneralRe: Win32 Services Pinmemberscott.tiger21-Feb-07 11:27 
GeneralRe: Win32 Services PinmemberSwarajya Pendharkar21-Feb-07 19:00 
GeneralRe: Win32 Services Pinmemberscott.tiger22-Feb-07 5:36 
GeneralRe: Win32 Services Pinmembermabad6-Mar-07 0:01 
GeneralRe: Win32 Services Pinmemberscott.tiger6-Mar-07 0:26 
Generalmy way of doing Pinmemberf225-Jun-06 4:17 
GeneralRe: my way of doing PinmemberSwarajya Pendharkar25-Jun-06 18:50 
GeneralRe: my way of doing Pinmemberf226-Jun-06 1:17 
GeneralRe: my way of doing PinmemberSwarajya Pendharkar26-Jun-06 2:35 
GeneralRe: my way of doing Pinmemberf226-Jun-06 16:10 
GeneralRe: my way of doing PinmemberSwarajya Pendharkar26-Jun-06 22:40 
GeneralRe: my way of doing Pinmemberf227-Jun-06 3:51 
GeneralRe: my way of doing PinmemberSwarajya Pendharkar27-Jun-06 20:23 
GeneralRe: my way of doing Pinmemberf228-Jun-06 4:09 
Swarajya Pendharkar wrote:
Because writer can start writing after you are done and again you got reader and writer working on it at the same time. I would advice you to implement your concept, test it and then talk of it. And if it works create a new article and send me the link or send the code.

 
seems like you are the one who did not understand my code. the writer could write other page of memory while the reader 1st enter will be retrieving the previous data. did u notice the writer switch mem page everytime it write? the benefit of it will be a lot, for example, while the reader accessing mem, the writer dun waste time waiting the reader, it write new data into mem on other page. when writer release mutex, by the time 2nd reader will retrieve new data and subsequently all reader will get new data. i did not plan to explain to you with such a lengthy sentences, but seems that u did not understand it fully. initially i didn't plan to post these code segment in CP, so happen that i saw ur implementation was not clean enough, i am out of good will to have discussion with u but u did not welcome.
 

Swarajya Pendharkar wrote:
I think my implementation is clean and readable, just that you couldn't understand it.

 
you comment without think twice. such an implementation so call clean? basically u did not welcome any negative feedback on your article. you should take ppl's comment positively and learn instead of defending ur implementation. i wouldn't comment without understanding ur code. as i said, you used fixed(hard-coded) multiple event to control, i think that's bad idea dun you?
 

bad point number 1,
imagine that number of client exceed your hard-coded value... let's think abt it.
 
bad point number 2,
suffer from multiple event led to unpredictable overhead
 
bad point number 3,
the event code is scatherd everywhere. not that ppl or me not understand, anyone can write sophisticated logic inside but yet is not readable. and hence it is not a good/clean code anyway.
and it will not be passed on and on...
 
from,
-= aLbert =-

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140921.1 | Last Updated 10 Feb 2006
Article Copyright 2006 by Swarajya Pendharkar
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid