CreateTimerQueueTimer Callback and Race Status

I use timer queues in my application and pass a pointer to one of my own C ++ Timer objects as a parameter for the callback (in CreateTimerQueueTimer). Then I call the virtual method on the object in the callback.

The Timer object's destructor will disable the timer using DeleteTimerQueueTimer ().

static void callback( PVOID param, BOOLEAN timerOrWaitFired )
{
    Timer* timer = reinterpret_cast< Timer* >( param );
    timer->TimedOut();
}

class Timer
{
public:
   Timer();

   virtual ~Timer()
   {
       ::DeleteTimerQueueTimer( handle );
   }

   void Start( double period )
   {
      ::CreateTimerQueueTimer( &handle, ..., &callback, this, ... );
   }

   virtual void TimedOut() = 0;

   ...
};

However, there is a subtle race condition that if a callback is already called, but the timer object is destroyed before calling TimedOut (), the application crashes because the callback calls the virtual method on a nonexistent object, or even worse, when it is deleted.

I have mutexes for managing multi-threaded calls, but I still have a problem.

? - , .

? ?

, , , Timer ( , ). , , , Timer , ; , .

.

+5
3

. , , .

, Timer . TimerImpl Timer TimerImpl:

class Timer
{
  TimerInstance* impl;
  void TimeOut() { impl->TimeOut(); }
public:
  ~Timer() {
    ... make sure the timer has ended and wont fire again after this line...
    delete impl;
  }
}

struct TimerImpl
{
  virtual void TimeOut()=0;
  virtual ~TimerImpl();
}

, , , .

-, , . MSDN, ,

DeleteTimerQueueTimer(TimerQueue, Timer, INVALID_HANDLE_VALUE)
+3

DeleteTimerQueueTimer, , INVALID_HANDLE_VALUE . .

.

virtual ~Timer()
   {
       ::DeleteTimerQueueTimer( timerQueue, handle, INVALID_HANDLE_VALUE );
   }

, , . , . - deleteetimerqueuetimer , .

, , , .

+2

. , , , , , , undefined.

, - . , , " ".

This implementation still has one drawback. There is a possibility that the timer event will wait for the timer object to start deleting. The destructor can release the mutex and then destroy the mutex while the timer thread is waiting for the mutex. We prevented the race when sending the "timed out" event, but the behavior of the thread waiting for the mutex to be destroyed depends on the implementation of the mutex.

static void callback( PVOID param, BOOLEAN timerOrWaitFired );

class TimerWrapper
{
    public:

        /* Take reference to std::auto_ptr to ensure ownership transfer is explicit */
        TimerWrapper( std::auto_ptr<Timer>& timer ) : timer_(timer)
        {
            ::CreateTimerQueueTimer( &htimer_, ..., callback, this, ... );
        }

        void TimedOut()
        {
            ScopedGuard guard( mutex_ );
            if( timer_.get() )
                timer_->TimedOut();
        }

        ~TimerWrapper()
        {
            ::DeleteTimerQueueTimer( htimer_, ... );
            ScopedGuard guard( mutex_ );
            timer_.reset();
        }

    private:

        Mutex mutex_;
        std::auto_ptr<Timer> timer_;
        HANDLE htimer_;
};

static void callback( PVOID param, BOOLEAN timerOrWaitFired )
{
    TimerWrapper* timer = reinterpret_cast< TimerWrapper* >( param );
    timer->TimedOut();
}
0
source

All Articles