views:

147

answers:

4

I'm trying to write a unit test for my FileWatcher class.

FileWatcher derives from a Thread class and uses WaitForMultipleObjects to wait on two handles in its thread procedure:

  1. The handle returned from FindFirstChangeNotification
  2. A handle for an Event that lets me cancel the above wait.

So basically FileWatcher is waiting for whatever comes first: a file change or I tell it to stop watching.

Now, when trying to write code that tests this class I need to wait for it to start waiting.

Peusdo Code:

FileWatcher.Wait(INFINITE)
ChangeFile()
// Verify that FileWatcher works (with some other event - unimportant...)

Problem is that there's a race condition. I need to first make sure that FileWatcher has started waiting (i.e. that its thread is now blocked on WaitForMultipleObjects) before I can trigger the file change in line #2. I don't want to use Sleeps because, well, it seems hacky and is bound to give me problems when debugging.

I'm familiar with SignalObjectAndWait, but it doesn't really solve my problem, because I need it to "SignalObjectAndWaitOnMultipleObjects"...

Any ideas?


Edit

To clarify a bit, here's a simplified version of the FileWatcher class:

// Inherit from this class, override OnChange, and call Start() to turn on monitoring. 
class FileChangeWatcher : public Utils::Thread
{
public:
    // File must exist before constructing this instance
    FileChangeWatcher(const std::string& filename);
virtual int Run();
    virtual void OnChange() = 0;
};

It inherits from Thread and implements the thread function, which looks something like this (very simplified):

_changeEvent = ::FindFirstChangeNotificationW(wfn.c_str(), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);

HANDLE events[2] = { _changeEvent, m_hStopEvent };

DWORD hWaitDone = WAIT_OBJECT_0;
while (hWaitDone == WAIT_OBJECT_0)
{
    hWaitDone = ::WaitForMultipleObjects(2, events, FALSE, INFINITE);

    if (hWaitDone == WAIT_OBJECT_0)
        OnChange(); 
    else
        return Thread::THREAD_ABORTED;
} 
return THREAD_FINISHED;

Notice that the thread function waits on two handles, one - the change notification, and the other - the "stop thread" event (inherited from Thread).

Now the code that tests this class looks like this:

class TestFileWatcher : public FileChangeWatcher
{
public:
    bool Changed;
    Event evtDone;
    TestFileWatcher(const std::string& fname) : FileChangeWatcher(fname) { Changed = false; }
    virtual void OnChange()
    {
        Changed = true;
        evtDone.Set();
    }
};

And is invoked from a CPPUnit test:

std::string tempFile = TempFilePath();
StringToFile("Hello, file", tempFile);
TestFileWatcher tfw(tempFile);
tfw.Start();
::Sleep(100); // Ugly, but we have to wait for monitor to kick in in worker thread
StringToFile("Modify me", tempFile);
tfw.evtDone.Wait(INFINITE);
CPPUNIT_ASSERT(tfw.Changed);

The idea is to get rid of that Sleep in the middle.

+3  A: 

There's no race, you don't have to wait for the FileWatcher to enter WaitForMultipleObjects. If you perform the change before the function is called, it will simply return immediately.

Edit: I can see the race now. Why don't you move the following line

_changeEvent = ::FindFirstChangeNotificationW(/*...*/);

from the thread function to the constructor of FileChangeWatcher? That way, you can be certain that by the time the StringToFile function is called, the file is already being watched.

avakar
You mean I can change the file before calling Wait but _after_ creating the change notification handle? But that's not really the case with this class, which does not expose the creation of the change handle as an event. Or maybe I misunderstood you..
Assaf Lavie
But there's no other way to do it. The standard way to implement this is to open the object to wait on (in your case create the file watcher and start it), signal the other object, then wait on the first object. That's just how it's done, and you need to change your class design to suit this.
wj32
Aren't you calling FindFirstChangeNotification in `FileWatcher`'s constructor? The directory should be watched from that moment on. Perhaps you could post some more code so that we know what exactly is going on.
avakar
I wasn't calling FFCN from teh constructor. But now I am trying that and still I get a race condition. When I let it run without breakpoints the file change happens to quickly for the watcher and it remains waiting forever. When I put some breakpoint in the unit test code before changing the file and then let it continue after hitting it it all works fine (the delay is sufficient).
Assaf Lavie
Scratch that last comment. Some other bug (not visible in above code) was causing it to get stuck. Moving it to the constructor does solve the problem.
Assaf Lavie
A: 

Instead could you use a Mutex? Before a thread could access the resources it desire, it would have to lock the Mutex and unlock it for other threads that need the resource.

Partial
What resources do you mean? How do you suggest I use a Mutex in this case?
Assaf Lavie
A: 

Call CreateEvent() to create a non-signaled event. When the watcher thread enters its main loop (or whatever), SetEvent(). Meanwhile, in FileWatcher first WaitForSingleObject() on the event, then once that returns, WFMO as you were doing before.

John Dibling
But that creates an alternative race condition.
Assaf Lavie
+1  A: 

You should call FindFirstChangeNotification() in your watcher's constructor and store the handle that it returns for use in your thread function. This will mean that you will catch change events from the moment of construction onwards.

Once your thread has started it simply calls wait on the two handles. If a change occurred before the thread had started up then the handle that FindFirstChangeNotification() returned will be signalled already and the change will be processed. If you wish for the thread to monitor many changes then it should loop and call FindNextChangeNotification() after processing each notification.

Len Holgate