314 lines
8.5 KiB
C++
314 lines
8.5 KiB
C++
/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*-
|
|
Copyright (c) 2010-2012 Marcus Geelnard
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
warranty. In no event will the authors be held liable for any damages
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source
|
|
distribution.
|
|
*/
|
|
|
|
#include <exception>
|
|
#include "tinythread.h"
|
|
|
|
#if defined(_TTHREAD_POSIX_)
|
|
#include <unistd.h>
|
|
#include <map>
|
|
#elif defined(_TTHREAD_WIN32_)
|
|
#include <process.h>
|
|
#endif
|
|
|
|
|
|
namespace tthread {
|
|
|
|
//------------------------------------------------------------------------------
|
|
// condition_variable
|
|
//------------------------------------------------------------------------------
|
|
// NOTE 1: The Win32 implementation of the condition_variable class is based on
|
|
// the corresponding implementation in GLFW, which in turn is based on a
|
|
// description by Douglas C. Schmidt and Irfan Pyarali:
|
|
// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
|
|
//
|
|
// NOTE 2: Windows Vista actually has native support for condition variables
|
|
// (InitializeConditionVariable, WakeConditionVariable, etc), but we want to
|
|
// be portable with pre-Vista Windows versions, so TinyThread++ does not use
|
|
// Vista condition variables.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#if defined(_TTHREAD_WIN32_)
|
|
#define _CONDITION_EVENT_ONE 0
|
|
#define _CONDITION_EVENT_ALL 1
|
|
#endif
|
|
|
|
#if defined(_TTHREAD_WIN32_)
|
|
condition_variable::condition_variable() : mWaitersCount(0)
|
|
{
|
|
mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
InitializeCriticalSection(&mWaitersCountLock);
|
|
}
|
|
#endif
|
|
|
|
#if defined(_TTHREAD_WIN32_)
|
|
condition_variable::~condition_variable()
|
|
{
|
|
CloseHandle(mEvents[_CONDITION_EVENT_ONE]);
|
|
CloseHandle(mEvents[_CONDITION_EVENT_ALL]);
|
|
DeleteCriticalSection(&mWaitersCountLock);
|
|
}
|
|
#endif
|
|
|
|
#if defined(_TTHREAD_WIN32_)
|
|
void condition_variable::_wait()
|
|
{
|
|
// Wait for either event to become signaled due to notify_one() or
|
|
// notify_all() being called
|
|
int result = WaitForMultipleObjects(2, mEvents, FALSE, INFINITE);
|
|
|
|
// Check if we are the last waiter
|
|
EnterCriticalSection(&mWaitersCountLock);
|
|
-- mWaitersCount;
|
|
bool lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
|
|
(mWaitersCount == 0);
|
|
LeaveCriticalSection(&mWaitersCountLock);
|
|
|
|
// If we are the last waiter to be notified to stop waiting, reset the event
|
|
if(lastWaiter)
|
|
ResetEvent(mEvents[_CONDITION_EVENT_ALL]);
|
|
}
|
|
#endif
|
|
|
|
#if defined(_TTHREAD_WIN32_)
|
|
void condition_variable::notify_one()
|
|
{
|
|
// Are there any waiters?
|
|
EnterCriticalSection(&mWaitersCountLock);
|
|
bool haveWaiters = (mWaitersCount > 0);
|
|
LeaveCriticalSection(&mWaitersCountLock);
|
|
|
|
// If we have any waiting threads, send them a signal
|
|
if(haveWaiters)
|
|
SetEvent(mEvents[_CONDITION_EVENT_ONE]);
|
|
}
|
|
#endif
|
|
|
|
#if defined(_TTHREAD_WIN32_)
|
|
void condition_variable::notify_all()
|
|
{
|
|
// Are there any waiters?
|
|
EnterCriticalSection(&mWaitersCountLock);
|
|
bool haveWaiters = (mWaitersCount > 0);
|
|
LeaveCriticalSection(&mWaitersCountLock);
|
|
|
|
// If we have any waiting threads, send them a signal
|
|
if(haveWaiters)
|
|
SetEvent(mEvents[_CONDITION_EVENT_ALL]);
|
|
}
|
|
#endif
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// POSIX pthread_t to unique thread::id mapping logic.
|
|
// Note: Here we use a global thread safe std::map to convert instances of
|
|
// pthread_t to small thread identifier numbers (unique within one process).
|
|
// This method should be portable across different POSIX implementations.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#if defined(_TTHREAD_POSIX_)
|
|
static thread::id _pthread_t_to_ID(const pthread_t &aHandle)
|
|
{
|
|
static mutex idMapLock;
|
|
static std::map<pthread_t, unsigned long int> idMap;
|
|
static unsigned long int idCount(1);
|
|
|
|
lock_guard<mutex> guard(idMapLock);
|
|
if(idMap.find(aHandle) == idMap.end())
|
|
idMap[aHandle] = idCount ++;
|
|
return thread::id(idMap[aHandle]);
|
|
}
|
|
#endif // _TTHREAD_POSIX_
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// thread
|
|
//------------------------------------------------------------------------------
|
|
|
|
/// Information to pass to the new thread (what to run).
|
|
struct _thread_start_info {
|
|
void (*mFunction)(void *); ///< Pointer to the function to be executed.
|
|
void * mArg; ///< Function argument for the thread function.
|
|
thread * mThread; ///< Pointer to the thread object.
|
|
};
|
|
|
|
// Thread wrapper function.
|
|
#if defined(_TTHREAD_WIN32_)
|
|
unsigned WINAPI thread::wrapper_function(void * aArg)
|
|
#elif defined(_TTHREAD_POSIX_)
|
|
void * thread::wrapper_function(void * aArg)
|
|
#endif
|
|
{
|
|
// Get thread startup information
|
|
_thread_start_info * ti = (_thread_start_info *) aArg;
|
|
|
|
try
|
|
{
|
|
// Call the actual client thread function
|
|
ti->mFunction(ti->mArg);
|
|
}
|
|
catch(...)
|
|
{
|
|
// Uncaught exceptions will terminate the application (default behavior
|
|
// according to C++11)
|
|
std::terminate();
|
|
}
|
|
|
|
#if 0
|
|
// DFHack fix: this code prevents join from freeing thread resources.
|
|
|
|
// The thread is no longer executing
|
|
lock_guard<mutex> guard(ti->mThread->mDataMutex);
|
|
ti->mThread->mNotAThread = true;
|
|
#endif
|
|
|
|
// The thread is responsible for freeing the startup information
|
|
delete ti;
|
|
|
|
return 0;
|
|
}
|
|
|
|
thread::thread(void (*aFunction)(void *), void * aArg)
|
|
{
|
|
// Serialize access to this thread structure
|
|
lock_guard<mutex> guard(mDataMutex);
|
|
|
|
// Fill out the thread startup information (passed to the thread wrapper,
|
|
// which will eventually free it)
|
|
_thread_start_info * ti = new _thread_start_info;
|
|
ti->mFunction = aFunction;
|
|
ti->mArg = aArg;
|
|
ti->mThread = this;
|
|
|
|
// The thread is now alive
|
|
mNotAThread = false;
|
|
|
|
// Create the thread
|
|
#if defined(_TTHREAD_WIN32_)
|
|
mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &mWin32ThreadID);
|
|
#elif defined(_TTHREAD_POSIX_)
|
|
if(pthread_create(&mHandle, NULL, wrapper_function, (void *) ti) != 0)
|
|
mHandle = 0;
|
|
#endif
|
|
|
|
// Did we fail to create the thread?
|
|
if(!mHandle)
|
|
{
|
|
mNotAThread = true;
|
|
delete ti;
|
|
}
|
|
}
|
|
|
|
thread::~thread()
|
|
{
|
|
if(joinable())
|
|
std::terminate();
|
|
}
|
|
|
|
void thread::join()
|
|
{
|
|
if(joinable())
|
|
{
|
|
#if defined(_TTHREAD_WIN32_)
|
|
WaitForSingleObject(mHandle, INFINITE);
|
|
CloseHandle(mHandle);
|
|
#elif defined(_TTHREAD_POSIX_)
|
|
pthread_join(mHandle, NULL);
|
|
#endif
|
|
|
|
#if 1
|
|
// DFHack patch: moved here from the wrapper function
|
|
lock_guard<mutex> guard(mDataMutex);
|
|
mNotAThread = true;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool thread::joinable() const
|
|
{
|
|
mDataMutex.lock();
|
|
bool result = !mNotAThread;
|
|
mDataMutex.unlock();
|
|
return result;
|
|
}
|
|
|
|
void thread::detach()
|
|
{
|
|
mDataMutex.lock();
|
|
if(!mNotAThread)
|
|
{
|
|
#if defined(_TTHREAD_WIN32_)
|
|
CloseHandle(mHandle);
|
|
#elif defined(_TTHREAD_POSIX_)
|
|
pthread_detach(mHandle);
|
|
#endif
|
|
mNotAThread = true;
|
|
}
|
|
mDataMutex.unlock();
|
|
}
|
|
|
|
thread::id thread::get_id() const
|
|
{
|
|
if(!joinable())
|
|
return id();
|
|
#if defined(_TTHREAD_WIN32_)
|
|
return id((unsigned long int) mWin32ThreadID);
|
|
#elif defined(_TTHREAD_POSIX_)
|
|
return _pthread_t_to_ID(mHandle);
|
|
#endif
|
|
}
|
|
|
|
unsigned thread::hardware_concurrency()
|
|
{
|
|
#if defined(_TTHREAD_WIN32_)
|
|
SYSTEM_INFO si;
|
|
GetSystemInfo(&si);
|
|
return (int) si.dwNumberOfProcessors;
|
|
#elif defined(_SC_NPROCESSORS_ONLN)
|
|
return (int) sysconf(_SC_NPROCESSORS_ONLN);
|
|
#elif defined(_SC_NPROC_ONLN)
|
|
return (int) sysconf(_SC_NPROC_ONLN);
|
|
#else
|
|
// The standard requires this function to return zero if the number of
|
|
// hardware cores could not be determined.
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// this_thread
|
|
//------------------------------------------------------------------------------
|
|
|
|
thread::id this_thread::get_id()
|
|
{
|
|
#if defined(_TTHREAD_WIN32_)
|
|
return thread::id((unsigned long int) GetCurrentThreadId());
|
|
#elif defined(_TTHREAD_POSIX_)
|
|
return _pthread_t_to_ID(pthread_self());
|
|
#endif
|
|
}
|
|
|
|
}
|