0

I am in the midst of developing two applications that communicate using a file. These applications are running on the same machine. To be clear, I have a writer.exe and a reader.exe executables.

The writer.exe constantly writes a random integer to a common file "some_file.bin" and closes the file. It repeats the same process again: opens the file, writes random int and closes it. This is done in a while loop.

The reader.exe is constantly reading the file "some_file.bin", printing the read integer to its console.

Both of these applications are written in C++ using the std::fstream class for file I/O.

So far, this is not working properly, because we have a race condition happening here.

I want some way to properly communicate between these two processes, using Win32 Events, so that the writer.exe process knows to wait until the reader.exe process has finished reading, and the reader.exe process knows to pause until the writer.exe process has finished writing.

I'm hoping to do this using the CreateEvent, WaitForSingleObject family of API calls present natively on the Windows platform.

Please feel free to tell me if this is possible between two processes?

So far, I have only found examples using the above APIs to signal threads of one main process... ?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Vivekanand V
  • 340
  • 2
  • 12
  • 2
    windows events are global and can be used between processes – Gal Nov 18 '21 at 08:41
  • 7
    Sounds like a [pipe](https://learn.microsoft.com/en-us/windows/win32/ipc/pipes) is what you need. One process writes to it, the other reads from it, nothing gets lost, nothing gets read more than once. – Thomas Nov 18 '21 at 08:42
  • 3
    [Interprocess Communications](https://learn.microsoft.com/en-us/windows/win32/ipc/interprocess-communications). – IInspectable Nov 18 '21 at 08:49
  • IIRC Visual Studio's `std::ifstream` will happily _read_ from a pipe, but creation requires non-portable functions. You can then still write to that pipe using [`ofstream`](https://stackoverflow.com/questions/475853/can-i-use-createfile-but-force-the-handle-into-a-stdofstream) – MSalters Nov 18 '21 at 09:05
  • 1
    @Gal "*windows events are global and can be used between processes*" - more accurately, an Event *can* be used globally between processes, but *is not* global unless you *explicitly* ask for that behavior, by assigning a sharable name to it which both processes can then open a handle to. – Remy Lebeau Nov 18 '21 at 09:56
  • @RemyLebeau Yes I will give a common name as the last argument to ```CreateEventA()``` :) – Vivekanand V Nov 18 '21 at 10:10
  • @Thomas Yes, I loved the idea of a pipe. But the first process will indefinitely continue to edit the common file. The other process reads the file continuously. If I'm using a pipe, how can a synchronization happen? I have experience using pipes in Linux but I'm new to Windows pipes ... Can you please tell me, some sources where I can learn more about Win32 pipes... :) – Vivekanand V Nov 18 '21 at 10:13
  • https://learn.microsoft.com/en-us/windows/win32/ipc/synchronous-and-overlapped-input-and-output – Thomas Nov 18 '21 at 10:16
  • You also might consider https://www.boost.org/doc/libs/1_55_0/doc/html/interprocess/sharedmemorybetweenprocesses.html. NOTE: I have used this on windows before, but note, the behavior of the shared memory, which is not closed properly (like a crash would do) is differemt between platforms (or was when I tried) – Tiger4Hire Nov 18 '21 at 10:26
  • You can use Win32 private messages. The other method is with TCP/IP (especially if you need to transfer more data between apps). – i486 Nov 18 '21 at 10:44
  • @VivekanandV "*If I'm using a pipe, how can a synchronization happen?*" - you wouldn't need to sync them. The writer would create the pipe, wait for a reader to connect, and then continuously write ints to it. The reader would connect to the pipe and then continuously read ints from it." *please tell me, some source where I can learn more about Win32 pipes*" - the [documentation](https://learn.microsoft.com/en-us/windows/win32/ipc/pipes) is a good place to start. – Remy Lebeau Nov 18 '21 at 10:45
  • @RemyLebeau Events are always sharable. You can Duplicate/Inherit without ever using a name. – Anders Nov 18 '21 at 15:05
  • @Anders well, you *can*, but that also requires explicit action to setup. So, they are not inheritable/duplicated *by default* if you just create them as-is without asking for that extra setup. – Remy Lebeau Nov 18 '21 at 15:42

1 Answers1

2

Yes, it is possible. When one process creates a handle to an Event object, you have the option of assigning a name to that object in the kernel. The other process can then create/open its own handle to that Event object using the same name.

Note that to accomplish what you want, you actually would need 2 Events, eg:

writer.exe:

HANDLE hReadable = CreateEvent(NULL, FALSE, FALSE, TEXT("ReaderCanRead"));
if (!hReadable) ...

HANDLE hWritable = CreateEvent(NULL, FALSE, TRUE, TEXT("WriterCanWrite"));
if (!hWritable) ...

...

while (!quit)
{
    ...
    WaitForSingleObject(hWritable, INFINITE);
    if (quit) break;
    // write int...
    SetEvent(hReadable);
    ...
}

reader.exe:

HANDLE hReadable = CreateEvent(NULL, FALSE, FALSE, TEXT("ReaderCanRead"));
// or: HANDLE hReadable = OpenEvent(SYNCHRONIZE, FALSE, TEXT("ReaderCanRead"));
if (!hReadable) ...

HANDLE hWritable = CreateEvent(NULL, FALSE, TRUE, TEXT("WriterCanWrite"));
// or: HANDLE hWritable = OpenEvent(EVENT_MODIFY_STATE, FALSE, TEXT("WriterCanWrite"));
if (!hWritable) ...

...

while (!quit)
{
    ...
    WaitForSingleObject(hReadable, INFINITE);
    if (quit) break;
    // read int...
    SetEvent(hWritable);
    ...
}

That being said, you might consider using a named block of shared memory via CreateFileMapping()+MapViewOfFile(), rather than using a physical file.

However, there are many other Inter-Process Communication mechanisms that are way more suitable to your producer/consumer model than using a shared file/memory protected by Events. Pipes, sockets, mailslots, even window messages, would be a much better choice.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thankyou very much! :) Can the reader process use OpenEvent() because the writer process starts first, and the names might already be registered in the kernel? Is that possible? – Vivekanand V Nov 18 '21 at 10:50
  • 1
    @VivekanandV yes, of course. I've updated my example to show this. But using `CreateEvent()` in both processes would accomplish the same thing. `OpenEvent()` fails if the event doesn't exist yet. `CreateEvent()` creates the event if it doesn't exist yet. Nothing wrong with having the reader create the events if it starts first, it just won't have any data to read until the writer starts. But, use whichever method best suits your needs. – Remy Lebeau Nov 18 '21 at 11:18
  • Should I use ```ResetEvent(..) ``` ? – Vivekanand V Nov 18 '21 at 15:09
  • 1
    @VivekanandV my example is using auto-reset events, so no. If you use manual-reset events, then yes, call `ResetEvent(event)` after `WaitForSingleObject(event)` – Remy Lebeau Nov 18 '21 at 15:44
  • Thankyou very much! :) I highly appreciate your knowledge :) I love both Windows as well as Linux API. I'm trying to learn a lot of WINAPI myself, these days! :) – Vivekanand V Nov 20 '21 at 19:53