4
votes

I've seen a project where communication between processes was made using shared memory (e.g. using ::CreateFileMapping under Windows) and every time one of the processes wanted to notify that some data is available in shared memory, a synchronization mechanism using named events notified the interested party that the content of the shared memory changed.

I am concerned on the fact that the appropriate memory fences are not present for the process that reads the new information to know that it has to invalidate it's copy of the data and read it from main memory once it is "published" by the producer process.

Do you know how can this be accomplished on Windows using shared memory?

EDIT Just wanted to add that after creating the file mapping the processes uses MapViewOfFile() API only once and every new modification to the shared data uses the pointer obtained by the initial call to MapViewOfFile() to read the new data sent over the shared memory. Does correct synchronization require that every time data changes in shared memory the process that reads data must create MapViewOfFile() every time ?

3
To be a valid synchronization mechanism, named events should have proper memory fences in place. Do you have evidence that this might not happen?Alexey Kukanov
@Alexey: Indeed, it's usually the case, but is it guaranted by standard/specs/etc.?user396672
On windows one can use CreateEvent api for synchronization. I don't know if setting and waiting for that event produces the aquire/release necessary mem fences thoughGhita

3 Answers

4
votes

What you're looking for for shared memory on windows is the InterlockedExchange function. See the msdn article here. The REALLY important part is quoted:

This function generates a full memory barrier (or fence) to ensure that memory operations are completed in order.

This will function cross-process. I've worked with it before, and found it 100% reliable for implementing a mutex-like construct on top of shared memory.

How you do that is that you exchange it with the "set" value. If you get "clear" back, you have it (it was clear), but if you get "set" back, then somebody else had it. You loop, sleep between looping, etc, until you "get" it. Basically this:

#define LOCK_SET 1
#define LOCK_CLEAR 0

int* lock_location = LOCK_LOCATION; // ensure this is in shared memory
if (InterlockedExchange(lock_location, LOCK_SET) == LOCK_CLEAR)
{
    return true; // got the lock
}
else
{
    return false; // didn't get the lock
}

As above, and loop until you "get" it.

4
votes

If you use a Windows Named Event for signaling changes, then everything should be OK.

Process A changes the data and calls SetEvent.

Process B waits for the event using WaitForSingleObject or similar, and sees that it is set.

Process B then reads the data. WaitForSingleObject contains all the necessary synchronization to ensure that the changes made by process A before the call to SetEvent are read by process B.

Of course, if you make any changes to the data after calling SetEvent, then these may or may not show up when process B reads the data.

If you don't want to use Events, you could use a Mutex created with CreateMutex, or you could write lock-free code using the Interlocked... functions such as InterlockedExchange and InterlockedIncrement.

However you do the synchronization, you do not need to call MapViewOfFile more than once.

0
votes

Let's call process A the data producer and process B the data consumer. Until now, you have a mechanism for process A to notify process B that new data has been produced. I suggest you created a reverse notification (from B to A) which tells process A that the data has been consumed. If, for performance reason, you don't want process A to wait for the data to be consumed, you could set up a ring-buffer in the shared memory.