blob: 5bae3d65ed065cd6717c5c59ba423266e3bab7ef [file] [log] [blame]
// Copyright 2007-2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
//
// The implementation is straightforward except the destruction of the
// QueueTimer which needs some clarification.
// If there is no callback running, then the destructor gets the critical
// section and then it blocks on the DeleteTimerQueueTimer call, waiting for
// the kernel to clean up the timer handle. The callback never fires in this
// case.
// If a callback is running, then there are two possibilities:
// 1. The callback gets the critical section. The callback runs as usual and
// then the destructor gets the critical section. This is also easy.
// 2. The destructor gets the critical section. In this case, the callback
// tries the critical section then it returns right away.
//
// Alarm timers are started and restarted every time they fire. The usage
// patterns for alarms is usually Start, Callback, Start, Callback, etc...
// The cleanup of an alarm timer handle usually happens in the callback, unless
// the destructor of the QueueTimer is called, in which case the logic
// above applies.
//
// Periodic timers are only started once: Start, Callback, Callback, etc...
// In this case, the destructor does all the necessary cleanup.
// Periodic timers must fire at intervals that are reasonable long so that
// the callbacks do not queue up.
#include "omaha/common/queue_timer.h"
#include "omaha/common/debug.h"
#include "omaha/common/error.h"
#include "omaha/common/logging.h"
namespace omaha {
QueueTimer::QueueTimer(HANDLE timer_queue, Callback callback, void* ctx)
: callback_tid_(0),
ctx_(ctx),
due_time_(0),
period_(0),
flags_(0),
timer_handle_(NULL),
timer_queue_(timer_queue),
callback_(callback) {
UTIL_LOG(L3, (_T("[QueueTimer::QueueTimer][0x%08x]"), this));
ASSERT1(timer_queue);
ASSERT1(callback);
::InitializeCriticalSection(&dtor_cs_);
::InitializeCriticalSection(&cs_);
}
// The destructor blocks on waiting for the timer kernel object to be deleted.
// We can't call the destructor of QueueTimer while we are handling a callback.
// This will result is a deadlock.
QueueTimer::~QueueTimer() {
UTIL_LOG(L3, (_T("[QueueTimer::~QueueTimer][0x%08x]"), this));
::EnterCriticalSection(&dtor_cs_);
if (timer_handle_) {
ASSERT1(callback_tid_ != ::GetCurrentThreadId());
// This is a blocking call waiting for all callbacks to clear up.
bool res = !!::DeleteTimerQueueTimer(timer_queue_,
timer_handle_,
INVALID_HANDLE_VALUE);
ASSERT1(res);
timer_handle_ = NULL;
}
callback_ = NULL;
timer_queue_ = NULL;
flags_ = 0;
period_ = 0;
due_time_ = 0;
ctx_ = 0;
callback_tid_ = 0;
::LeaveCriticalSection(&dtor_cs_);
::DeleteCriticalSection(&cs_);
::DeleteCriticalSection(&dtor_cs_);
}
// Thread safe.
HRESULT QueueTimer::Start(int due_time, int period, uint32 flags) {
// Since Start creates the timer there could be a race condition where
// the timer could fire while we are still executing Start. We protect
// the start with a critical section so the Start completes before the
// timer can be entered by the callback.
::EnterCriticalSection(&cs_);
HRESULT hr = DoStart(due_time, period, flags);
::LeaveCriticalSection(&cs_);
return hr;
}
// Thread-safe.
void QueueTimer::TimerCallback(void* param, BOOLEAN timer_or_wait) {
ASSERT1(param);
VERIFY1(timer_or_wait);
QueueTimer* timer = static_cast<QueueTimer*>(param);
if (!::TryEnterCriticalSection(&timer->dtor_cs_)) {
return;
}
::EnterCriticalSection(&timer->cs_);
timer->DoCallback();
::LeaveCriticalSection(&timer->cs_);
::LeaveCriticalSection(&timer->dtor_cs_);
}
HRESULT QueueTimer::DoStart(int due_time, int period, uint32 flags) {
due_time_ = due_time;
period_ = period;
flags_ = flags;
// Application Verifier says period must be 0 for WT_EXECUTEONLYONCE timers.
if ((flags & WT_EXECUTEONLYONCE) && period != 0) {
return E_INVALIDARG;
}
// Periodic timers can't be started more than one time.
if (timer_handle_) {
return E_UNEXPECTED;
}
bool res = !!::CreateTimerQueueTimer(&timer_handle_,
timer_queue_,
&QueueTimer::TimerCallback,
this,
due_time,
period,
flags_);
if (!res) {
HRESULT hr = HRESULTFromLastError();
UTIL_LOG(LE, (_T("[QueueTimer::Start failed][0x%08x][0x%08x]"), hr, this));
return hr;
}
ASSERT1(timer_handle_);
UTIL_LOG(L3, (_T("[QueueTimer::Start timer created][0x%08x]"), this));
return S_OK;
}
void QueueTimer::DoCallback() {
UTIL_LOG(L2, (_T("[QueueTimer::OnCallback][0x%08x]"), this));
ASSERT1(timer_queue_);
ASSERT1(timer_handle_);
ASSERT1(callback_);
if (!period_) {
// Non-periodic aka alarm timers fire only once. We delete the timer
// handle so that the timer object can be restarted later on.
// The call below is non-blocking. The deletion of the kernel object can
// succeed right away, for example if the timer runs in the timer thread
// itself. Otherwise, if the last error is ERROR_IO_PENDING the kernel
// cleans up the object once the callback returns.
bool res = !!::DeleteTimerQueueTimer(timer_queue_, timer_handle_, NULL);
ASSERT1(res || (!res && ::GetLastError() == ERROR_IO_PENDING));
timer_handle_ = NULL;
}
callback_tid_ = ::GetCurrentThreadId();
callback_(this);
callback_tid_ = 0;
}
} // namespace omaha