/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "thread_win.h"
#include <assert.h>
#include <process.h>
#include <stdio.h>
#include <windows.h>
#include "set_thread_name_win.h"
#include "trace.h"
namespace webrtc {
ThreadWindows::ThreadWindows(ThreadRunFunction func, ThreadObj obj,
ThreadPriority prio, const char* threadName)
: ThreadWrapper(),
_runFunction(func),
_obj(obj),
_alive(false),
_dead(true),
_doNotCloseHandle(false),
_prio(prio),
_event(NULL),
_thread(NULL),
_id(0),
_name(),
_setThreadName(false)
{
_event = EventWrapper::Create();
_critsectStop = CriticalSectionWrapper::CreateCriticalSection();
if (threadName != NULL)
{
// Set the thread name to appear in the VS debugger.
_setThreadName = true;
strncpy(_name, threadName, kThreadMaxNameLength);
}
}
ThreadWindows::~ThreadWindows()
{
#ifdef _DEBUG
assert(!_alive);
#endif
if (_thread)
{
CloseHandle(_thread);
}
if(_event)
{
delete _event;
}
if(_critsectStop)
{
delete _critsectStop;
}
}
uint32_t ThreadWrapper::GetThreadId() {
return GetCurrentThreadId();
}
unsigned int WINAPI ThreadWindows::StartThread(LPVOID lpParameter)
{
static_cast<ThreadWindows*>(lpParameter)->Run();
return 0;
}
bool ThreadWindows::Start(unsigned int& threadID)
{
_doNotCloseHandle = false;
// Set stack size to 1M
_thread=(HANDLE)_beginthreadex(NULL, 1024*1024, StartThread, (void*)this, 0,
&threadID);
if(_thread == NULL)
{
return false;
}
_id = threadID;
_event->Wait(INFINITE);
switch(_prio)
{
case kLowPriority:
SetThreadPriority(_thread, THREAD_PRIORITY_BELOW_NORMAL);
break;
case kNormalPriority:
SetThreadPriority(_thread, THREAD_PRIORITY_NORMAL);
break;
case kHighPriority:
SetThreadPriority(_thread, THREAD_PRIORITY_ABOVE_NORMAL);
break;
case kHighestPriority:
SetThreadPriority(_thread, THREAD_PRIORITY_HIGHEST);
break;
case kRealtimePriority:
SetThreadPriority(_thread, THREAD_PRIORITY_TIME_CRITICAL);
break;
};
return true;
}
bool ThreadWindows::SetAffinity(const int* processorNumbers,
const unsigned int amountOfProcessors)
{
DWORD_PTR processorBitMask = 0;
for(unsigned int processorIndex = 0;
processorIndex < amountOfProcessors;
processorIndex++)
{
// Convert from an array with processor numbers to a bitmask
// Processor numbers start at zero.
// TODO (hellner): this looks like a bug. Shouldn't the '=' be a '+='?
// Or even better |=
processorBitMask = 1 << processorNumbers[processorIndex];
}
return SetThreadAffinityMask(_thread,processorBitMask) != 0;
}
void ThreadWindows::SetNotAlive()
{
_alive = false;
}
bool ThreadWindows::Shutdown()
{
DWORD exitCode = 0;
BOOL ret = TRUE;
if (_thread)
{
ret = TerminateThread(_thread, exitCode);
_alive = false;
_dead = true;
_thread = NULL;
}
return ret == TRUE;
}
bool ThreadWindows::Stop()
{
_critsectStop->Enter();
// Prevents the handle from being closed in ThreadWindows::Run()
_doNotCloseHandle = true;
_alive = false;
bool signaled = false;
if (_thread && !_dead)
{
_critsectStop->Leave();
// Wait up to 2 seconds for the thread to complete.
if( WAIT_OBJECT_0 == WaitForSingleObject(_thread, 2000))
{
signaled = true;
}
_critsectStop->Enter();
}
if (_thread)
{
CloseHandle(_thread);
_thread = NULL;
}
_critsectStop->Leave();
if (_dead || signaled)
{
return true;
}
else
{
return false;
}
}
void ThreadWindows::Run()
{
_alive = true;
_dead = false;
_event->Set();
// All tracing must be after _event->Set to avoid deadlock in Trace.
if (_setThreadName)
{
WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, _id,
"Thread with name:%s started ", _name);
SetThreadName(-1, _name); // -1, set thread name for the calling thread.
}else
{
WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, _id,
"Thread without name started");
}
do
{
if (_runFunction)
{
if (!_runFunction(_obj))
{
_alive = false;
}
} else {
_alive = false;
}
} while(_alive);
if (_setThreadName)
{
WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, _id,
"Thread with name:%s stopped", _name);
} else {
WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,_id,
"Thread without name stopped");
}
_critsectStop->Enter();
if (_thread && !_doNotCloseHandle)
{
HANDLE thread = _thread;
_thread = NULL;
CloseHandle(thread);
}
_dead = true;
_critsectStop->Leave();
};
} // namespace webrtc