matrixelk
matrixelk

Reputation: 11

Unpredictable behavior with SetEvent and WaitForMultipleObjects

I am attempting to use SetEvent and WaitForMultipleObjects so I wrote a couple of functions that I wanted to synchronize using Events. I want the first function to execute, and then signal the second function to execute, which in turn signals the first function to execute again etc.

Two events were created, one initialized to a signaled state, the other unsignaled. The only way I can get the execution to flow as expected is when putting the each thread to sleep for 10 ms. I read about some caveats about using SetEvent: http://cboard.cprogramming.com/windows-programming/100818-setevent-resetevent-3.html

My question is must I put my threads to sleep because these function calls require extra time to actually signal the events? When debugging sometimes an event that is set still remains unsignalled. I verify this using process explorer. My code basically looks like this:

in the main:

//1. FinCalcpidUpdatestruct <<  initialize to unsignalled
//2. FinUpdatestructCalcpid << initialize to signalled
FinCalcpidUpdatestruct = CreateEvent (NULL, FALSE, FALSE, TEXT("FinCalcpidUpdatestruct"));
FinUpdatestructCalcpid = CreateEvent (NULL, FALSE, TRUE, TEXT ("FinUpdatestructCalcpid"));


void function1()
{
    int n;
    int noGoCode;
    n=0;

    noGoCode = 0;
    while(1)
    {
    //Sleep(10);
    noGoCode = WaitForSingleObject (FinCalcpidUpdatestruct, INFINITE);
    if (noGoCode == WAIT_OBJECT_0) {

    //wait for FinCalcpidUpdatestruct to be signalled
    BufferOut[n] = pid_data_array -> output;

    //signal FinUpdatestructCalcpid 
    if(!SetEvent (FinUpdatestructCalcpid))
        printf("Couldn't set the event FinUpdatestructCalcpid\n");
    else{
        printf("FinUpdatestructCalcpid event set\n");
        Sleep(10);
        }
    }
    else
        printf("error\n");
    }
}

void function2()
{
    int nGoCode = 0;
    while(1)
    {
    //wait for FinUpdatestructCalcpid to be signalled

    // Sleep(10);
    nGoCode = WaitForSingleObject (FinUpdatestructCalcpid, INFINITE);
    if (nGoCode == WAIT_OBJECT_0) {
    if(!SetEvent (FinCalcpidUpdatestruct))
        printf("Couldn't set the event FinCalcpidUpdatestruct\n");
    else{
        printf("FinCalcpidUpdatestruct event set\n");
        Sleep(10);
        }
    }
    else
        printf("error\n");
    }//end while(1)

If the sleep is not used then sometimes the same function will run through the while loop a couple of times instead of ping ponging back and forth between the two functions. Any ideas?

Upvotes: 0

Views: 2142

Answers (3)

Roman Ryltsov
Roman Ryltsov

Reputation: 69632

Let me cut your code short for brevity first:

FinCalcpidUpdatestruct = CreateEvent (NULL, FALSE, FALSE, TEXT("FinCalcpidUpdatestruct"));
FinUpdatestructCalcpid = CreateEvent (NULL, FALSE, TRUE, TEXT ("FinUpdatestructCalcpid"));

void function1()
{
    while(1)
    {
        WaitForSingleObject (FinCalcpidUpdatestruct, INFINITE);
        // Do Something
        SetEvent (FinUpdatestructCalcpid);
        printf(...);
        Sleep(10);
    }
}

void function2()
{
    while(1)
    {
        nGoCode = WaitForSingleObject (FinUpdatestructCalcpid, INFINITE);
        SetEvent (FinCalcpidUpdatestruct);
        printf(...); // **A**
        Sleep(10);
    }
}

At basically any point of execution the control might be taken away from thread and given to another. Now suppose that function2 already set the event and is about to print output around **A** in code. Before the output is printed, the control is taken away and given to function1. The last printed output is already from function1 and its wait event is set, so it falls through and prints its stuff again.

When this happens, your output is once in a while:

function1
function2
function1
function2
function1
// When the situation **A** above happens:
function1
function2
function2
function1
// And we move on as usual further
function2
function1
function2

Set your events when you are done, that is after printf, and you will be fine.

Upvotes: 1

Steve
Steve

Reputation: 7271

You can fix the problem by adding a printf above the SetEvent call on each function.

The problem is that you are setting the event and then performing some output.

In function2 the printf occurs after the SetEvent:

// Add a printf call here to see sensible output.
if(!SetEvent (FinUpdatestructCalcpid))
    printf("Couldn't set the event FinUpdatestructCalcpid\n");
else{
    // Thread is pre-empted by kernel here.  This is not executed immediately
    printf("FinUpdatestructCalcpid event set\n");
}

The kernel preempts the thread running function2 so the FinUpdatestructCalcpid event has now been set, without the corresponding printf that you're expecting.

The thread running function1 is then executed and sets the FinUpdatestructCalcpid event. The thread running function2 is now allowed to execute and continues from where it left off. It runs the printf and because the FinUpdatestructCalcpid event has been set it immediately runs again.

The Sleep() calls you're using help to make this race condition unlikely, but do not eliminate it.

Upvotes: 3

paddy
paddy

Reputation: 63461

Here's what I think...

You don't output any status until you have already set the event. At that point, execution could switch to the other thread before you output anything. If that thread runs first, then it might look as if one thread's loop has run through a second time -- a ping with out a pong...

But if you were to increment a global counter in between receiving a signal and setting an event, then save that counter's value to a local variable, you would probably find that one thread's count is always odd and the other's is always even (which is the behaviour you want), even if the output is not quite in order.

Upvotes: 0

Related Questions