views:

436

answers:

2

Hello all!

I have an application consisting of two windows, one communicates to the other and sends it a struct constaining two integers (In this case two rolls of a dice).

I will be using events for the following circumstances:

  • Process a sends data to process b, process b displays data
  • Process a closes, in turn closing process b
  • Process b closes a, in turn closing process a

I have noticed that if the second process is constantly waiting for the first process to send data then the program will be just sat waiting, which is where the idea of implementing threads on each process occurred and I have started to implement this already.

The problem i'm having is that I don't exactly have a lot of experience with threads and events so I'm not sure of the best way to actually implement what I want to do.

I'm trying to work out how the other process will know of the event being fired so it can do the tasks it needs to do, I don't understand how one process that is separate from another can tell what the states the events are in especially as it needs to act as soon as the event has changed state.

Thanks for any help

Edit:

I can only use the Create/Set/Open methods for events, sorry for not mentioning it earlier.

Furthermore, I create a new thread in process A which lets the user interact with the application whilst listening for the close event.

Create thread:

hCreateEventThread = CreateThread(
                NULL,       // lpThreadAttributes (default)
                0,          // dwStackSize (default)
                ThreadFunc, // lpStartAddress
                NULL,       // lpParameter
                0,          // dwCreationFlags
                &hCreateEventThreadID   // lpThreadId (returned by function)
                );

            if(hCreateEventThread != NULL)
            {
                MessageBox(hMainWindow,L"Thread created!",L"Success!",MB_OK);
            }

Opening event on A when B closes:

    DWORD WINAPI ThreadFunc(LPVOID passedHandle)
    {
        hConsumerCloseEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("Global\\ConsumerCloseEvent"));

        while(TRUE)
        {
            dwCloseResult = WaitForSingleObject(hConsumerCloseEvent,INFINITE);

            switch (dwCloseResult) 
            {
                // State of object is signalled
            case WAIT_OBJECT_0: 
                //Consumer has closed, exit program.
                //CloseHandle(hDiceRoll);
                //CloseHandle(hCloseEvent);
                //CloseHandle(hCreateEventThread);
                ExitProcess(1);
                break;
            default: 
                return;
            }
        }
    }

Creating event in b (In WM_CREATE):

hConsumerCloseEvent = CreateEvent( 
                NULL,               // default security attributes
                TRUE,               // manual-reset event
                TRUE,              // initial state is nonsignaled
                TEXT("Global\\ConsumerCloseEvent")  // object name
                );

            if(hConsumerCloseEvent == NULL)
            {
                MessageBox(hMainWindow,L"CreateEvent failed",L"Error",MB_OK);
            }

Setting the event to signalled when B closes:

case WM_DESTROY:
        {
            SetEvent(hConsumerCloseEvent);
            PostQuitMessage(0);
            break;
        }

As you can see when the Event is signalled, application A is set to close. When I run both application and close process B, process A does not notice the changed signal and does not close.

Edit 2:

After using the GetLastError(); I was able to identify that the handle to the OpenEvent was NULL, the error given is

ERROR_FILE_NOT_FOUND - 2 :The system cannot find the file specified

Is my method of creating the event and reading it incorrect, I've made sure to include the Global\ prefix.

+1  A: 

Sounds like you might want to use CreateSemaphore. You can use a semaphore to represent the number of data items available for process b to handle. Initialise it to a count of 0, when a data item is made available increment it with ReleaseSemaphore. Then in your process b call WaitForSingleObject, or one of its bretheren, which will only return when the semaphore count is above 0, at which point the count is decremented.

This isn't complete solution because you still need to handle things like what to do when your shared buffer is full, but is should get you well and truly started.

torak
Unfortunately I'm restricted to using only the CreateEvent/SetEvent etc.. set of methods, sorry for not mentioning it in the question.
Jamie Keeling
+1  A: 

The nice thing about semaphores is that they single handedly take care of synchronizing the queue depth between the two processes. Since you're limited to using Event objects instead I'd suggest having the inter-process messages instead of a queue, or a queue of one if you will.

Process A

// In the initialization code
...
hMessageEmptiedEvent = CreateEvent(NULL, FALSE, TRUE, _T("MessageEmptied"));
hMessageSentEvent = CreateEvent(NULL, FALSE, FALSE, _T("MessageSent"));

// Call this function when you want to send the data to process b
void sendData(struct diceData data)
{
  // Make sure any pre-existing message has been processed
  WaitForSingleObject(hMessageEmptiedEvent, INFINITE);
  // Copy the data into the shared buffer
  *(struct diceData *) pBuf = data;
  // Signal the other process that data is ready
  SetEvent(hMessageSentEvnt);
}

Process B

// In the initialization code
...
hMessageEmptiedEvent = CreateEvent(NULL, FALSE, TRUE, _T("MessageEmptied"));
hMessageSentEvent = CreateEvent(NULL, FALSE, FALSE, _T("MessageSent"));

// Call this function when you want to recieve data from process a
struct diceData readData()
{
  struct diceData data;

  // Wait for a message to become available
  WaitForSingleObject(hMessageSentEvent, INFINITE);
  // Copy the data from the shared buffer
  data = * (struct diceData *)pBuf;
  // Signal the other process that message has been read.
  SetEvent(hMessageEmptiedEvnt);
}

If, as I'm guessing, you actually want to have a queue of length more than one you can now implement the queueing logic in process b. You'll want to do that in a separate thread for a couple of reasons. Implementation of the queue logic is up to you. Depending on your efficiency needs it could be a circular array or linked list or ???.

// In process b's initialzation add a thread to do the queueing
initializeEmptyQueue();
hQueueingThread = CreateThread(NULL, 0, enqueueLoop, NULL, 0, NULL);


DWORD enqueueLoop(LPVOID ignored)
{
  while (TRUE)
  {
    struct diceData data;
    data = getData();
    enqueueData(data);
  }
}
torak
I don't understand why you have duplicate sets of events for each sample you provided, also how can each process tell that the other has changed state?. For some reason even though I have set the event to false the program still see's it as signalled straight away, it makes no sense.
Jamie Keeling
Its actually one set of two events. Notice that the names are the same, the first process to run will create them, the second one will get a handle to the pre-existing events. The reason that I suggested the use of CreateEvent in both cases (as compared to CreateEvent in one process, and OpenEvent in the other) is that it should mean it doesn't matter which process you start first. The fact that the events are shared is how you share the state. I don't know why you'd be seeing the events as always signaled.
torak
Could you explain it simpler? The MSDN documentation only tends to show how to do it in one process, not between two so it's difficult to understand.
Jamie Keeling
What aspect is it that you don't understand?
torak
I've added some additional information to help explain where I'm going wrong.
Jamie Keeling
WaitForSingleObject blocks (doesn't return) until one of two conditions are satisfied. The first possible condition is that the Event is or becomes signalled. The second is the timeout value expires, if you are using INFINITE for this then timeout will never occur. All of this happens without "polling" the Event. Instead it is checked once when the function is called, if it isn't signalled then Windows puts the thread to sleep until the Event becomes signaled. What exactly do you mean when you say that you "have set the event to false it still shows as signalled"?
torak
If you look at the additional info I added when I create the event it's set to FALSE, yet the program still manages to access the ExitProcess(1); call which can only happen if it's signalled.
Jamie Keeling
According to the MSDN documentation for the third parameter to CreateEvent "bInitialState - If this parameter is TRUE, the initial state of the event object is signaled; otherwise, it is nonsignaled." In your code above you seem to have this the wrong way around.
torak
Hmm, I've changed the TRUE to FALSE and it's still closing as soon as it opens.
Jamie Keeling
That doesn't make any sense to me, I'll try to have a look into it later if I can find time.
torak
Thank you for your time, If necessary I can provide the application.
Jamie Keeling
I've managed to fix the problem where it closes straight away (Forgot to assign value to dwCloseResult) but now process B does not close process A.
Jamie Keeling
Which process are you launching first? If you launch process A first then I think that the call to OpenEvent will fail. That will mean that the call to WaitForSingleObject fails, probably with WAIT_FAILED, assuming that it tests for an event handle of NULL. That in turn means that you hit the default case of your switch, which returns, ending the thread. You really shouldn't need a while loop at all, since the WaitForSingleObject only returns when the event is signalled, which is when you want to exit.
torak
I was using visual studio to launch both applications at the same time, by setting Process B to launch before Process A the NULL event handle has stopped. Unfortunately process A still does not close when process B does.
Jamie Keeling
My mistake, I commented out a piece of code earlier and forgot to reimplement it. Everything works so far but I'll continue testing for the moment.
Jamie Keeling