реализация класса планировщика в Windows - PullRequest
0 голосов
/ 06 марта 2012

Я хочу реализовать класс планировщика, который любой объект может использовать для планирования тайм-аутов и отмены при необходимости.По истечении времени ожидания эта информация будет отправлена ​​установщику / владельцу времени ожидания в это время асинхронно.

Итак, для этой цели у меня есть 2 основных класса WindowsTimeout и WindowsScheduler.

class WindowsTimeout
{
  bool mCancelled;
  int mTimerID; // Windows handle to identify the actual timer set.
  ITimeoutReceiver* mSetter;

  int cancel()
  {
    mCancelled = true;

    if ( timeKillEvent(mTimerID) == SUCCESS) //  Line under question # 1
    {
        delete this; // Timeout instance is self-destroyed.
        return 0; // ok. OS Timer resource given back.
    }
    return 1; // fail. OS Timer resource not given back.
  }

  WindowsTimeout(ITimeoutReceiver* setter, int timerID)
  {
     mSetter = setter;
     mTimerID = timerID;
  }

};

class WindowsScheduler
{
    static void CALLBACK timerFunction(UINT uID,UINT uMsg,DWORD dwUser,DWORD dw1,DWORD dw2)    
{
     WindowsTimeout* timeout = (WindowsTimeout*) uMsg;
     if (timeout->mCancelled) 
          delete timeout;
     else
          timeout->mDestination->GEN(evTimeout(timeout));
}
WindowsTimeout* schedule(ITimeoutReceiver* setter, TimeUnit t)
    {
        int timerID = timeSetEvent(...);
        if (timerID == SUCCESS)
        {
             return WindowsTimeout(setter, timerID);
        }
        return 0;
    }
};

Мои вопросы:

Q.1.Когда выполняется вызов WindowsScheduler :: timerFunction (), в каком контексте этот вызов выполняется?Это просто функция обратного вызова, и я думаю, что она выполняется контекстом ОС, верно?Если это так, препятствует ли этот вызов другим задачам, которые уже выполняются?Я имею в виду, что обратные вызовы имеют более высокий приоритет, чем любая другая пользовательская задача?

Q.2.Когда установщик тайм-аута хочет отменить свой тайм-аут, он вызывает WindowsTimeout :: cancel ().Тем не менее, всегда существует вероятность того, что статический вызов timerFunction будет отозван операционной системой, упреждая операцию отмены, например, сразу после оператора mCancelled = true.В таком случае экземпляр тайм-аута будет удален функцией обратного вызова.Когда функция pre-empted cancel () приходит снова, после того, как функция обратного вызова завершает выполнение, она попытается получить доступ к атрибуту удаленного экземпляра (mTimerID), как вы можете видеть в строке: «Строка под вопросом № 1» вкод.

Как избежать такого случая?

Обратите внимание, что этот вопрос является улучшенной версией предыдущих версий, одной из моих собственных: Таймер мультимедиа Windows с аргументом обратного вызова

1 Ответ

1 голос
/ 08 марта 2012

Q1 - Я полагаю, что он вызывается в потоке, выделенном API таймера. Я не уверен, но я не удивлюсь, если поток будет работать с очень высоким приоритетом. (В Windows это не обязательно означает, что он полностью вытеснит другие потоки, это просто означает, что он получит больше циклов, чем другие потоки).

Q2 - Я начал набрасывать решение для этого, но потом понял, что это было немного сложнее, чем я думал. Лично я бы поддерживал хеш-таблицу, которая отображает идентификаторы timerID на экземпляры вашего объекта WindowsTimeout. Хеш-таблица может быть простым экземпляром std :: map, который защищен критическим разделом. Когда происходит обратный вызов таймера, он входит в критическую секцию и пытается получить указатель экземпляра WindowsTimer, а затем помечает экземпляр WindowsTimer как выполненный, выходит из критической секции и затем фактически выполняет обратный вызов. Если хеш-таблица не содержит экземпляр WindowsTimer, это означает, что вызывающая сторона уже удалила его. Будь здесь очень осторожен.

Одна тонкая ошибка в вашем собственном коде выше:

WindowsTimeout* schedule(ITimeoutReceiver* setter, TimeUnit t)
    {
        int timerID = timeSetEvent(...);
        if (timerID == SUCCESS)
        {
             return WindowsTimeout(setter, timerID);
        }
        return 0;
    }
};

В вашем методе расписания вполне возможно, что обратный вызов, запланированный timeSetEvent, вернется ДО того, как вы сможете создать экземпляр WindowsTimeout.

...