blob: f7e743b0a8177ffe208e717ebdac6b4cc6da4de6 [file] [log] [blame]
// Copyright 2004-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.
// ========================================================================
#include "omaha/common/thread.h"
#include "omaha/common/debug.h"
#include "omaha/common/exception_barrier.h"
#include "omaha/common/logging.h"
#include "omaha/common/time.h"
namespace omaha {
// The system keeps an event associated with the thread message queue for a
// while even after the thread is dead. It can appear as a handle leak in the
// unit test but in fact it is not.
Thread::Thread() : thread_id_(0), thread_(NULL) {
}
Thread::~Thread() {
if (thread_) {
VERIFY1(CloseHandle(thread_));
}
thread_ = NULL;
}
// This is the thread proc function as required by win32.
DWORD __stdcall Thread::Prepare(void* this_pointer) {
ExceptionBarrier eb;
ASSERT1(this_pointer);
Thread * this_thread = reinterpret_cast<Thread*>(this_pointer);
Runnable * this_runner = this_thread->runner_;
// Create a message queue. Our thread should have one.
MSG message = {0};
PeekMessage(&message, NULL, WM_USER, WM_USER, PM_NOREMOVE);
// Start method is waiting on this gate to be open in order
// to proceed. By opening gate we say: OK thread is running.
this_thread->start_gate_.Open();
// Now call the interface method. We are done.
UTIL_LOG(L4, (L"Thread::Prepare calling thread's Run()"));
this_runner->Run();
return 0;
}
// Starts the thread. It does not return until the thread is started.
bool Thread::Start(Runnable* runner) {
ASSERT1(runner);
// Allow the thread object to be reused by cleaning its state up.
if (thread_) {
VERIFY1(CloseHandle(thread_));
}
start_gate_.Close();
runner_ = runner;
thread_ = CreateThread(NULL, // default security attributes
0, // use default stack size
&Thread::Prepare, // thread function
this, // argument to thread function
0, // use default creation flags
&thread_id_); // returns the thread identifier
if (!thread_) {
return false;
}
// Wait until the newly created thread opens the gate for us.
return start_gate_.Wait(INFINITE);
}
DWORD Thread::GetThreadId() const {
return thread_id_;
}
HANDLE Thread::GetThreadHandle() const {
return thread_;
}
bool Thread::Suspend() {
return (static_cast<DWORD>(-1) != SuspendThread(thread_));
}
bool Thread::Resume() {
return (static_cast<DWORD>(-1) != ResumeThread(thread_));
}
bool Thread::Terminate(int exit_code) {
return TRUE == TerminateThread(thread_, exit_code);
}
bool Thread::SetPriority(int priority) {
return TRUE == SetThreadPriority(thread_, priority);
}
bool Thread::GetPriority(int* priority) const {
if (!priority) {
return false;
}
*priority = GetThreadPriority(thread_);
return THREAD_PRIORITY_ERROR_RETURN != *priority;
}
// Waits for handle to become signaled.
bool Thread::WaitTillExit(DWORD msec) const {
if (!Running()) {
return true;
}
return WAIT_OBJECT_0 == WaitForSingleObject(thread_, msec);
}
// Checks if the thread is running.
bool Thread::Running() const {
if (NULL == thread_) {
return false;
}
return WAIT_TIMEOUT == WaitForSingleObject(thread_, 0);
}
// Executes an APC request.
void __stdcall Thread::APCProc(ULONG_PTR param) {
ApcInfo* pInfo = reinterpret_cast<ApcInfo*>(param);
if (pInfo) {
if (pInfo->receiver_) {
pInfo->receiver_->OnApc(pInfo->param_);
}
// Deallocates what was allocated in QueueApc.
delete pInfo;
}
}
// ApcReceiver wants to execute its OnApc function in the
// context of this thread.
bool Thread::QueueApc(ApcReceiver* receiver, ULONG_PTR param) {
ASSERT1(receiver);
if (!Running()) {
// No reason to queue anything to not running thread.
return true;
}
// This allocation will be freed in Thread::APCProc
ApcInfo* pInfo = new ApcInfo();
pInfo->receiver_ = receiver;
pInfo->param_ = param;
return 0 != QueueUserAPC(&Thread::APCProc,
thread_,
reinterpret_cast<ULONG_PTR>(pInfo));
}
bool Thread::PostMessage(UINT msg, WPARAM wparam, LPARAM lparam) {
return TRUE == PostThreadMessage(thread_id_, msg, wparam, lparam);
}
} // namespace omaha