-6

I have a HANDLE to the waitable timer that can be shared among many running threads in my Windows service for the APIs such as CreateWaitableTimer, WaitForSingleObject, SetWaitableTimer and CancelWaitableTimer.

My question is, do I need to synchronize access (read/write) to this HANDLE itself via a mutex or a critical section?

EDIT: OK, I guess I need to clarify (with code):

//Say, I have a global handle
HANDLE ghWaitableTimer = NULL;      //Originally set to NULL

...

//Thread one
if(!ghWaitableTimer)
{
    ghWaitableTimer = ::CreateWaitableTimer(NULL, FALSE, NULL);
}

...

//Thread two
if(ghWaitableTimer)
{
    ::SetWaitableTimer(ghWaitableTimer, &liWhen, 0, NULL, NULL, FALSE);
}

...

//Thread three
if(ghWaitableTimer)
{
    ::WaitForSingleObject(ghWaitableTimer, INFINITE);
}

Or do I need to do this:

//Say, I have a global handle
HANDLE ghWaitableTimer = NULL;      //Originally set to NULL
CRITICAL_SECTION CriticalSection;   //Must be initialized before it's used

...

//Thread one
::EnterCriticalSection(&CriticalSection);

if(!ghWaitableTimer)
{
    ghWaitableTimer = ::CreateWaitableTimer(NULL, FALSE, NULL);
}

::LeaveCriticalSection(&CriticalSection);

...

//Thread two
::EnterCriticalSection(&CriticalSection);
HANDLE hTimer = ghWaitableTimer;
::LeaveCriticalSection(&CriticalSection);

if(hTimer)
{
    ::SetWaitableTimer(hTimer, &liWhen, 0, NULL, NULL, FALSE);
}

...

//Thread three
::EnterCriticalSection(&CriticalSection);
HANDLE hTimer = ghWaitableTimer;
::LeaveCriticalSection(&CriticalSection);

if(hTimer)
{
    ::WaitForSingleObject(hTimer, INFINITE);
}
c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • 1
    Whatever you are doing, it sounds 'off' :( – Martin James Nov 14 '15 at 22:52
  • @MartinJames Why? I don't see a problem here (except unnecessary worrying about synchronizing the synchronization, which is the question) – deviantfan Nov 14 '15 at 22:56
  • @deviantfan well, I'm not sure. I was struggling to think of some requirement that would need multiple threads to wait on the same timer. I could not think of one. Maybe it's just me :) – Martin James Nov 14 '15 at 23:21
  • I mean, there's enough problems with Events; if multiple threads wait on a MRE, it gets set and then immediately reset by one of the threads that was waiting, any other threads that were waiting, but did not get from ready to running, get set back to waiting again without actually running at all. So, I'm kinda worried that the same may apply to timers - perhaps if one thread is set running and restarts the timer wait, some of the other threads that were waiting may not run.... ? – Martin James Nov 14 '15 at 23:26
  • If I had such a requirement, I would have ONE thread wait on the timer and signal a separate synchro, (Event or Semaphore). for each of the other waiting threads. – Martin James Nov 14 '15 at 23:28
  • Oh blimey. The question has nothing at all to do with any of these synchronisation objects. I suggest you think about it again and perhaps re-think the question in terms of simple assignment and reading of a global variable. – David Heffernan Nov 14 '15 at 23:30
  • @DavidHeffernan: Oh blimey didn't I say `"Do I need to synchronize access to a HANDLE"` and not `"Do I need to synchronize access to a shared kernel object"`? – c00000fd Nov 15 '15 at 00:42
  • @MartinJames Your intuition was right :) – deviantfan Nov 15 '15 at 00:44
  • @c00000fd While you did ask that, nobody understood it before the clarification. Maybe because you're talking about sync mechanisms and their access functions here...? Your problem is not specific to timers, in some sense it's valid for any variable of any type. – deviantfan Nov 15 '15 at 00:45

2 Answers2

1

Using waitable timers, ie. starting the timer given a timer handle and waiting for triggering with that handle, does not need to be supported by mutexes etc.

It's pretty clear from the description that it is specifically made for inter-thread operations, that eg. another thread can use WaitForSingleObject to wait for triggering the timer event, etc.etc.
If it was necessary to protect it with a mutex, the mentioned use-case
wouldn't even be possible at all, instead polling would be necessary.

If this is not enough reasoning, here the first sentence of the page Synchronization Objects:

A synchronization object is an object whose handle can be specified in one of the wait functions to coordinate the execution of multiple threads.

...and sub-categories of mentioned page are eg. Mutex, Semaphore,
and Waitable Timer; everything on the same level.

However, what I just said is only valid for using it, as described in the first sentence. These actions have in common that you have a timer handle already (which isn't changed by said actions). Creating a new timer handle and assigning it to a variable does change the variable value ...

So, you essentially have two problems:
1) What do you think happens if thread 2 starts the timer before thread 1 created it? What happens if you type some text in Visual Studio before you start Visual Studio? Right.
2) A handle is just an numeric ID, which can be copied and moved around as much as you want. Even after being created, a timer can't protect it's own handle. Making any change on the handle while in use is bad.

For this purpose, a mutex isn't the solution either. It could protect the timer handle, but who protects the mutex handle...? Right. Any synchronization mechanism relies on being prepared before used to synchronize anything.

The real solution for your problem is to create the timer and assign it to a handle before you start any thread; then provide the handle to the started threads (parameter or global variable etc.)

deviantfan
  • 11,268
  • 3
  • 32
  • 49
1

You only need synchronization to read a global variable from multiple threads if the value of that variable may change. If you are creating a single timer object and the value of the handle never changes after that, you don't need synchronization to read its value.

Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79