mirror of
https://github.com/schnaader/precomp-cpp
synced 2026-08-23 00:23:04 -04:00
Add header files for std::thread support in MinGW
- MinGW lacks C++11 threading features out of the box, e.g. see http://stackoverflow.com/questions/21211980/mingw-error-thread-is-not-a-member-of-std - Source: https://github.com/meganz/mingw-std-threads
This commit is contained in:
parent
2cadccc2ac
commit
89d6bf423f
4 changed files with 677 additions and 0 deletions
24
contrib/mingw_std_threads/LICENSE
Normal file
24
contrib/mingw_std_threads/LICENSE
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
Copyright (c) 2016, Mega Limited
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
211
contrib/mingw_std_threads/mingw.condition_variable.h
Normal file
211
contrib/mingw_std_threads/mingw.condition_variable.h
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
/**
|
||||
* @file condition_variable.h
|
||||
* @brief std::condition_variable implementation for MinGW
|
||||
*
|
||||
* (c) 2013-2016 by Mega Limited, Auckland, New Zealand
|
||||
* @author Alexander Vassilev
|
||||
*
|
||||
* @copyright Simplified (2-clause) BSD License.
|
||||
* You should have received a copy of the license along with this
|
||||
* program.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* @note
|
||||
* This file may become part of the mingw-w64 runtime package. If/when this happens,
|
||||
* the appropriate license will be added, i.e. this code will become dual-licensed,
|
||||
* and the current BSD 2-clause license will stay.
|
||||
*/
|
||||
|
||||
#ifndef MINGW_CONDITIONAL_VARIABLE_H
|
||||
#define MINGW_CONDITIONAL_VARIABLE_H
|
||||
#include <atomic>
|
||||
#include <assert.h>
|
||||
#include "mingw.mutex.h"
|
||||
#include <chrono>
|
||||
#include <system_error>
|
||||
#include <windows.h>
|
||||
#ifdef _GLIBCXX_HAS_GTHREADS
|
||||
#error This version of MinGW seems to include a win32 port of pthreads, and probably \
|
||||
already has C++11 std threading classes implemented, based on pthreads. \
|
||||
It is likely that you will get errors about redefined classes, and unfortunately \
|
||||
this implementation can not be used standalone and independent of the system <mutex>\
|
||||
header, since it relies on it for \
|
||||
std::unique_lock and other utility classes. If you would still like to use this \
|
||||
implementation (as it is more lightweight), you have to edit the \
|
||||
c++-config.h system header of your MinGW to not define _GLIBCXX_HAS_GTHREADS. \
|
||||
This will prevent system headers from defining actual threading classes while still \
|
||||
defining the necessary utility classes.
|
||||
#endif
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
enum class cv_status { no_timeout, timeout };
|
||||
class condition_variable_any
|
||||
{
|
||||
protected:
|
||||
recursive_mutex mMutex;
|
||||
atomic<int> mNumWaiters;
|
||||
HANDLE mSemaphore;
|
||||
HANDLE mWakeEvent;
|
||||
public:
|
||||
typedef HANDLE native_handle_type;
|
||||
native_handle_type native_handle() {return mSemaphore;}
|
||||
condition_variable_any(const condition_variable_any&) = delete;
|
||||
condition_variable_any& operator=(const condition_variable_any&) = delete;
|
||||
condition_variable_any()
|
||||
:mNumWaiters(0), mSemaphore(CreateSemaphore(NULL, 0, 0xFFFF, NULL)),
|
||||
mWakeEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
|
||||
{}
|
||||
~condition_variable_any() { CloseHandle(mWakeEvent); CloseHandle(mSemaphore); }
|
||||
protected:
|
||||
template <class M>
|
||||
bool wait_impl(M& lock, DWORD timeout)
|
||||
{
|
||||
{
|
||||
lock_guard<recursive_mutex> guard(mMutex);
|
||||
mNumWaiters++;
|
||||
}
|
||||
lock.unlock();
|
||||
DWORD ret = WaitForSingleObject(mSemaphore, timeout);
|
||||
|
||||
mNumWaiters--;
|
||||
SetEvent(mWakeEvent);
|
||||
lock.lock();
|
||||
if (ret == WAIT_OBJECT_0)
|
||||
return true;
|
||||
else if (ret == WAIT_TIMEOUT)
|
||||
return false;
|
||||
//2 possible cases:
|
||||
//1)The point in notify_all() where we determine the count to
|
||||
//increment the semaphore with has not been reached yet:
|
||||
//we just need to decrement mNumWaiters, but setting the event does not hurt
|
||||
//
|
||||
//2)Semaphore has just been released with mNumWaiters just before
|
||||
//we decremented it. This means that the semaphore count
|
||||
//after all waiters finish won't be 0 - because not all waiters
|
||||
//woke up by acquiring the semaphore - we woke up by a timeout.
|
||||
//The notify_all() must handle this grafecully
|
||||
//
|
||||
else
|
||||
throw system_error(EPROTO, generic_category());
|
||||
}
|
||||
public:
|
||||
template <class M>
|
||||
void wait(M& lock)
|
||||
{
|
||||
wait_impl(lock, INFINITE);
|
||||
}
|
||||
template <class M, class Predicate>
|
||||
void wait(M& lock, Predicate pred)
|
||||
{
|
||||
while(!pred())
|
||||
{
|
||||
wait(lock);
|
||||
};
|
||||
}
|
||||
|
||||
void notify_all() noexcept
|
||||
{
|
||||
lock_guard<recursive_mutex> lock(mMutex); //block any further wait requests until all current waiters are unblocked
|
||||
if (mNumWaiters.load() <= 0)
|
||||
return;
|
||||
|
||||
ReleaseSemaphore(mSemaphore, mNumWaiters, NULL);
|
||||
while(mNumWaiters > 0)
|
||||
{
|
||||
auto ret = WaitForSingleObject(mWakeEvent, 1000);
|
||||
if ((ret == WAIT_FAILED) || (ret == WAIT_ABANDONED))
|
||||
throw system_error(EPROTO, generic_category());
|
||||
}
|
||||
assert(mNumWaiters == 0);
|
||||
//in case some of the waiters timed out just after we released the
|
||||
//semaphore by mNumWaiters, it won't be zero now, because not all waiters
|
||||
//woke up by acquiring the semaphore. So we must zero the semaphore before
|
||||
//we accept waiters for the next event
|
||||
//See _wait_impl for details
|
||||
while(WaitForSingleObject(mSemaphore, 0) == WAIT_OBJECT_0);
|
||||
}
|
||||
void notify_one() noexcept
|
||||
{
|
||||
lock_guard<recursive_mutex> lock(mMutex);
|
||||
if (!mNumWaiters)
|
||||
return;
|
||||
int targetWaiters = mNumWaiters.load() - 1;
|
||||
ReleaseSemaphore(mSemaphore, 1, NULL);
|
||||
while(mNumWaiters > targetWaiters)
|
||||
{
|
||||
auto ret = WaitForSingleObject(mWakeEvent, 1000);
|
||||
if ((ret == WAIT_FAILED) || (ret == WAIT_ABANDONED))
|
||||
throw system_error(EPROTO, generic_category());
|
||||
}
|
||||
assert(mNumWaiters == targetWaiters);
|
||||
}
|
||||
template <class M, class Rep, class Period>
|
||||
std::cv_status wait_for(M& lock,
|
||||
const std::chrono::duration<Rep, Period>& rel_time)
|
||||
{
|
||||
long long timeout = chrono::duration_cast<chrono::milliseconds>(rel_time).count();
|
||||
if (timeout < 0)
|
||||
timeout = 0;
|
||||
bool ret = wait_impl(lock, (DWORD)timeout);
|
||||
return ret?cv_status::no_timeout:cv_status::timeout;
|
||||
}
|
||||
|
||||
template <class M, class Rep, class Period, class Predicate>
|
||||
bool wait_for(M& lock,
|
||||
const std::chrono::duration<Rep, Period>& rel_time, Predicate pred)
|
||||
{
|
||||
wait_for(lock, rel_time);
|
||||
return pred();
|
||||
}
|
||||
template <class M, class Clock, class Duration>
|
||||
cv_status wait_until (M& lock,
|
||||
const chrono::time_point<Clock,Duration>& abs_time)
|
||||
{
|
||||
return wait_for(lock, abs_time - Clock::now());
|
||||
}
|
||||
template <class M, class Clock, class Duration, class Predicate>
|
||||
bool wait_until (M& lock,
|
||||
const std::chrono::time_point<Clock, Duration>& abs_time,
|
||||
Predicate pred)
|
||||
{
|
||||
auto time = abs_time - Clock::now();
|
||||
if (time < 0)
|
||||
return pred();
|
||||
else
|
||||
return wait_for(lock, time, pred);
|
||||
}
|
||||
};
|
||||
class condition_variable: protected condition_variable_any
|
||||
{
|
||||
protected:
|
||||
typedef condition_variable_any base;
|
||||
public:
|
||||
using base::native_handle_type;
|
||||
using base::native_handle;
|
||||
using base::base;
|
||||
using base::notify_all;
|
||||
using base::notify_one;
|
||||
void wait(unique_lock<mutex> &lock)
|
||||
{ base::wait(lock); }
|
||||
template <class Predicate>
|
||||
void wait(unique_lock<mutex>& lock, Predicate pred)
|
||||
{ base::wait(lock, pred); }
|
||||
template <class Rep, class Period>
|
||||
std::cv_status wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time)
|
||||
{ return base::wait_for(lock, rel_time); }
|
||||
template <class Rep, class Period, class Predicate>
|
||||
bool wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time, Predicate pred)
|
||||
{ return base::wait_for(lock, rel_time, pred); }
|
||||
template <class Clock, class Duration>
|
||||
cv_status wait_until (unique_lock<mutex>& lock, const chrono::time_point<Clock,Duration>& abs_time)
|
||||
{ return base::wait_for(lock, abs_time); }
|
||||
template <class Clock, class Duration, class Predicate>
|
||||
bool wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock, Duration>& abs_time, Predicate pred)
|
||||
{ return base::wait_until(lock, abs_time, pred); }
|
||||
};
|
||||
}
|
||||
#endif // MINGW_CONDITIONAL_VARIABLE_H
|
||||
275
contrib/mingw_std_threads/mingw.mutex.h
Normal file
275
contrib/mingw_std_threads/mingw.mutex.h
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
/**
|
||||
* @file mingw.mutex.h
|
||||
* @brief std::mutex et al implementation for MinGW
|
||||
** (c) 2013-2016 by Mega Limited, Auckland, New Zealand
|
||||
* @author Alexander Vassilev
|
||||
*
|
||||
* @copyright Simplified (2-clause) BSD License.
|
||||
* You should have received a copy of the license along with this
|
||||
* program.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* @note
|
||||
* This file may become part of the mingw-w64 runtime package. If/when this happens,
|
||||
* the appropriate license will be added, i.e. this code will become dual-licensed,
|
||||
* and the current BSD 2-clause license will stay.
|
||||
*/
|
||||
|
||||
#ifndef WIN32STDMUTEX_H
|
||||
#define WIN32STDMUTEX_H
|
||||
#ifdef _GLIBCXX_HAS_GTHREADS
|
||||
#error This version of MinGW seems to include a win32 port of pthreads, and probably \
|
||||
already has C++11 std threading classes implemented, based on pthreads. \
|
||||
You are likely to have class redefinition errors below, and unfirtunately this \
|
||||
implementation can not be used standalone \
|
||||
and independent of the system <mutex> header, since it relies on it for \
|
||||
std::unique_lock and other utility classes. If you would still like to use this \
|
||||
implementation (as it is more lightweight), you have to edit the \
|
||||
c++-config.h system header of your MinGW to not define _GLIBCXX_HAS_GTHREADS. \
|
||||
This will prevent system headers from defining actual threading classes while still \
|
||||
defining the necessary utility classes.
|
||||
#endif
|
||||
// Recursion checks on non-recursive locks have some performance penalty, so the user
|
||||
// may want to disable the checks in release builds. In that case, make sure they
|
||||
// are always enabled in debug builds.
|
||||
|
||||
#if defined(STDMUTEX_NO_RECURSION_CHECKS) && !defined(NDEBUG)
|
||||
#undef STDMUTEX_NO_RECURSION_CHECKS
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <chrono>
|
||||
#include <system_error>
|
||||
|
||||
#ifndef EPROTO
|
||||
#define EPROTO 134
|
||||
#endif
|
||||
#ifndef EOWNERDEAD
|
||||
#define EOWNERDEAD 133
|
||||
#endif
|
||||
|
||||
namespace std
|
||||
{
|
||||
class recursive_mutex
|
||||
{
|
||||
protected:
|
||||
CRITICAL_SECTION mHandle;
|
||||
public:
|
||||
typedef LPCRITICAL_SECTION native_handle_type;
|
||||
native_handle_type native_handle() {return &mHandle;}
|
||||
recursive_mutex() noexcept
|
||||
{
|
||||
InitializeCriticalSection(&mHandle);
|
||||
}
|
||||
recursive_mutex (const recursive_mutex&) = delete;
|
||||
recursive_mutex& operator=(const recursive_mutex&) = delete;
|
||||
~recursive_mutex() noexcept
|
||||
{
|
||||
DeleteCriticalSection(&mHandle);
|
||||
}
|
||||
void lock()
|
||||
{
|
||||
EnterCriticalSection(&mHandle);
|
||||
}
|
||||
void unlock()
|
||||
{
|
||||
LeaveCriticalSection(&mHandle);
|
||||
}
|
||||
bool try_lock()
|
||||
{
|
||||
return (TryEnterCriticalSection(&mHandle)!=0);
|
||||
}
|
||||
};
|
||||
template <class B>
|
||||
class _NonRecursive: protected B
|
||||
{
|
||||
protected:
|
||||
typedef B base;
|
||||
DWORD mOwnerThread;
|
||||
public:
|
||||
using base::native_handle_type;
|
||||
using base::native_handle;
|
||||
_NonRecursive() noexcept :base(), mOwnerThread(0) {}
|
||||
_NonRecursive (const _NonRecursive<B>&) = delete;
|
||||
_NonRecursive& operator= (const _NonRecursive<B>&) = delete;
|
||||
void lock()
|
||||
{
|
||||
base::lock();
|
||||
checkSetOwnerAfterLock();
|
||||
}
|
||||
protected:
|
||||
void checkSetOwnerAfterLock()
|
||||
{
|
||||
DWORD self = GetCurrentThreadId();
|
||||
if (mOwnerThread == self)
|
||||
{
|
||||
fprintf(stderr, "FATAL: Recursive locking or non-recursive mutex detected. Throwing sysetm exception\n");
|
||||
fflush(stderr);
|
||||
throw system_error(EDEADLK, generic_category());
|
||||
}
|
||||
mOwnerThread = self;
|
||||
}
|
||||
void checkSetOwnerBeforeUnlock()
|
||||
{
|
||||
DWORD self = GetCurrentThreadId();
|
||||
if (mOwnerThread != self)
|
||||
{
|
||||
fprintf(stderr, "FATAL: Recursive unlocking of non-recursive mutex detected. Throwing system exception\n");
|
||||
fflush(stderr);
|
||||
throw system_error(EDEADLK, generic_category());
|
||||
}
|
||||
mOwnerThread = 0;
|
||||
}
|
||||
public:
|
||||
void unlock()
|
||||
{
|
||||
checkSetOwnerBeforeUnlock();
|
||||
base::unlock();
|
||||
}
|
||||
bool try_lock()
|
||||
{
|
||||
bool ret = base::try_lock();
|
||||
if (ret)
|
||||
checkSetOwnerAfterLock();
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef STDMUTEX_NO_RECURSION_CHECKS
|
||||
typedef _NonRecursive<recursive_mutex> mutex;
|
||||
#else
|
||||
typedef recursive_mutex mutex;
|
||||
#endif
|
||||
|
||||
class recursive_timed_mutex
|
||||
{
|
||||
protected:
|
||||
HANDLE mHandle;
|
||||
public:
|
||||
typedef HANDLE native_handle_type;
|
||||
native_handle_type native_handle() const {return mHandle;}
|
||||
recursive_timed_mutex(const recursive_timed_mutex&) = delete;
|
||||
recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
|
||||
recursive_timed_mutex(): mHandle(CreateMutex(NULL, FALSE, NULL)){}
|
||||
~recursive_timed_mutex()
|
||||
{
|
||||
CloseHandle(mHandle);
|
||||
}
|
||||
void lock()
|
||||
{
|
||||
DWORD ret = WaitForSingleObject(mHandle, INFINITE);
|
||||
if (ret != WAIT_OBJECT_0)
|
||||
{
|
||||
if (ret == WAIT_ABANDONED)
|
||||
throw system_error(EOWNERDEAD, generic_category());
|
||||
else
|
||||
throw system_error(EPROTO, generic_category());
|
||||
}
|
||||
}
|
||||
void unlock()
|
||||
{
|
||||
if (!ReleaseMutex(mHandle))
|
||||
throw system_error(EDEADLK, generic_category());
|
||||
}
|
||||
bool try_lock()
|
||||
{
|
||||
DWORD ret = WaitForSingleObject(mHandle, 0);
|
||||
if (ret == WAIT_TIMEOUT)
|
||||
return false;
|
||||
else if (ret == WAIT_OBJECT_0)
|
||||
return true;
|
||||
else if (ret == WAIT_ABANDONED)
|
||||
throw system_error(EOWNERDEAD, generic_category());
|
||||
else
|
||||
throw system_error(EPROTO, generic_category());
|
||||
}
|
||||
template <class Rep, class Period>
|
||||
bool try_lock_for(const std::chrono::duration<Rep,Period>& dur)
|
||||
{
|
||||
DWORD timeout = (DWORD)chrono::duration_cast<chrono::milliseconds>(dur).count();
|
||||
|
||||
DWORD ret = WaitForSingleObject(mHandle, timeout);
|
||||
if (ret == WAIT_TIMEOUT)
|
||||
return false;
|
||||
else if (ret == WAIT_OBJECT_0)
|
||||
return true;
|
||||
else if (ret == WAIT_ABANDONED)
|
||||
throw system_error(EOWNERDEAD, generic_category());
|
||||
else
|
||||
throw system_error(EPROTO, generic_category());
|
||||
}
|
||||
template <class Clock, class Duration>
|
||||
bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)
|
||||
{
|
||||
return try_lock_for(timeout_time - Clock::now());
|
||||
}
|
||||
};
|
||||
|
||||
class timed_mutex: public _NonRecursive<recursive_timed_mutex>
|
||||
{
|
||||
protected:
|
||||
typedef _NonRecursive<recursive_timed_mutex> base;
|
||||
public:
|
||||
using base::base;
|
||||
timed_mutex(const timed_mutex&) = delete;
|
||||
timed_mutex& operator=(const timed_mutex&) = delete;
|
||||
template <class Rep, class Period>
|
||||
bool try_lock_for(const std::chrono::duration<Rep,Period>& dur)
|
||||
{
|
||||
bool ret = base::try_lock_for(dur);
|
||||
#ifndef STDMUTEX_NO_RECURSION_CHECKS
|
||||
if (ret)
|
||||
checkSetOwnerAfterLock();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
public:
|
||||
template <class Clock, class Duration>
|
||||
bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)
|
||||
{
|
||||
bool ret = base::try_lock_until(timeout_time);
|
||||
#ifndef STDMUTEX_NO_RECURSION_CHECKS
|
||||
if (ret)
|
||||
checkSetOwnerAfterLock();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
// You can use the scoped locks and other helpers that are still provided by <mutex>
|
||||
// In that case, you must include <mutex> before including this file, so that this
|
||||
// file will not try to redefine them
|
||||
#ifndef _GLIBCXX_MUTEX
|
||||
|
||||
/// Do not acquire ownership of the mutex.
|
||||
struct defer_lock_t { };
|
||||
|
||||
/// Try to acquire ownership of the mutex without blocking.
|
||||
struct try_to_lock_t { };
|
||||
|
||||
/// Assume the calling thread has already obtained mutex ownership
|
||||
/// and manage it.
|
||||
struct adopt_lock_t { };
|
||||
|
||||
constexpr defer_lock_t defer_lock { };
|
||||
constexpr try_to_lock_t try_to_lock { };
|
||||
constexpr adopt_lock_t adopt_lock { };
|
||||
|
||||
template <class M>
|
||||
class lock_guard
|
||||
{
|
||||
protected:
|
||||
M& mMutex;
|
||||
public:
|
||||
typedef M mutex_type;
|
||||
lock_guard(const lock_guard&) = delete;
|
||||
lock_guard& operator=(const lock_guard&) = delete;
|
||||
explicit lock_guard(mutex_type& m): mMutex(m) { mMutex.lock(); }
|
||||
lock_guard(mutex_type& m, std::adopt_lock_t):mMutex(m){}
|
||||
~lock_guard() { mMutex.unlock(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
}
|
||||
#endif // WIN32STDMUTEX_H
|
||||
167
contrib/mingw_std_threads/mingw.thread.h
Normal file
167
contrib/mingw_std_threads/mingw.thread.h
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/**
|
||||
* @file mingw.thread.h
|
||||
* @brief std::thread implementation for MinGW
|
||||
* (c) 2013-2016 by Mega Limited, Auckland, New Zealand
|
||||
* @author Alexander Vassilev
|
||||
*
|
||||
* @copyright Simplified (2-clause) BSD License.
|
||||
* You should have received a copy of the license along with this
|
||||
* program.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* @note
|
||||
* This file may become part of the mingw-w64 runtime package. If/when this happens,
|
||||
* the appropriate license will be added, i.e. this code will become dual-licensed,
|
||||
* and the current BSD 2-clause license will stay.
|
||||
*/
|
||||
|
||||
#ifndef WIN32STDTHREAD_H
|
||||
#define WIN32STDTHREAD_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <system_error>
|
||||
#include <process.h>
|
||||
|
||||
#ifdef _GLIBCXX_HAS_GTHREADS
|
||||
#error This version of MinGW seems to include a win32 port of pthreads, and probably \
|
||||
already has C++11 std threading classes implemented, based on pthreads. \
|
||||
It is likely that you will get class redefinition errors below, and unfortunately \
|
||||
this implementation can not be used standalone \
|
||||
and independent of the system <mutex> header, since it relies on it for \
|
||||
std::unique_lock and other utility classes. If you would still like to use this \
|
||||
implementation (as it is more lightweight), you have to edit the \
|
||||
c++-config.h system header of your MinGW to not define _GLIBCXX_HAS_GTHREADS. \
|
||||
This will prevent system headers from defining actual threading classes while still \
|
||||
defining the necessary utility classes.
|
||||
#endif
|
||||
|
||||
//instead of INVALID_HANDLE_VALUE _beginthreadex returns 0
|
||||
#define _STD_THREAD_INVALID_HANDLE 0
|
||||
namespace std
|
||||
{
|
||||
|
||||
class thread
|
||||
{
|
||||
public:
|
||||
class id
|
||||
{
|
||||
DWORD mId;
|
||||
void clear() {mId = 0;}
|
||||
friend class thread;
|
||||
public:
|
||||
id(DWORD aId=0):mId(aId){}
|
||||
bool operator==(const id& other) const {return mId == other.mId;}
|
||||
};
|
||||
protected:
|
||||
HANDLE mHandle;
|
||||
id mThreadId;
|
||||
public:
|
||||
typedef HANDLE native_handle_type;
|
||||
id get_id() const noexcept {return mThreadId;}
|
||||
native_handle_type native_handle() const {return mHandle;}
|
||||
thread(): mHandle(_STD_THREAD_INVALID_HANDLE){}
|
||||
|
||||
thread(thread&& other)
|
||||
:mHandle(other.mHandle), mThreadId(other.mThreadId)
|
||||
{
|
||||
other.mHandle = _STD_THREAD_INVALID_HANDLE;
|
||||
other.mThreadId.clear();
|
||||
}
|
||||
|
||||
thread(const thread &other)=delete;
|
||||
|
||||
template<class Function, class... Args>
|
||||
explicit thread(Function&& f, Args&&... args)
|
||||
{
|
||||
typedef decltype(std::bind(f, args...)) Call;
|
||||
Call* call = new Call(std::bind(f, args...));
|
||||
mHandle = (HANDLE)_beginthreadex(NULL, 0, threadfunc<Call>,
|
||||
(LPVOID)call, 0, (unsigned*)&(mThreadId.mId));
|
||||
}
|
||||
template <class Call>
|
||||
static unsigned int __stdcall threadfunc(void* arg)
|
||||
{
|
||||
std::unique_ptr<Call> upCall(static_cast<Call*>(arg));
|
||||
(*upCall)();
|
||||
return (unsigned long)0;
|
||||
}
|
||||
bool joinable() const {return mHandle != _STD_THREAD_INVALID_HANDLE;}
|
||||
void join()
|
||||
{
|
||||
if (get_id() == GetCurrentThreadId())
|
||||
throw system_error(EDEADLK, generic_category());
|
||||
if (mHandle == _STD_THREAD_INVALID_HANDLE)
|
||||
throw system_error(ESRCH, generic_category());
|
||||
if (!joinable())
|
||||
throw system_error(EINVAL, generic_category());
|
||||
WaitForSingleObject(mHandle, INFINITE);
|
||||
CloseHandle(mHandle);
|
||||
mHandle = _STD_THREAD_INVALID_HANDLE;
|
||||
mThreadId.clear();
|
||||
}
|
||||
|
||||
~thread()
|
||||
{
|
||||
if (joinable())
|
||||
std::terminate();
|
||||
}
|
||||
thread& operator=(const thread&) = delete;
|
||||
thread& operator=(thread&& other) noexcept
|
||||
{
|
||||
if (joinable())
|
||||
std::terminate();
|
||||
swap(std::forward<thread>(other));
|
||||
return *this;
|
||||
}
|
||||
void swap(thread&& other) noexcept
|
||||
{
|
||||
std::swap(mHandle, other.mHandle);
|
||||
std::swap(mThreadId.mId, other.mThreadId.mId);
|
||||
}
|
||||
static unsigned int hardware_concurrency() noexcept
|
||||
{
|
||||
static int ncpus = -1;
|
||||
if (ncpus == -1)
|
||||
{
|
||||
SYSTEM_INFO sysinfo;
|
||||
GetSystemInfo(&sysinfo);
|
||||
ncpus = sysinfo.dwNumberOfProcessors;
|
||||
}
|
||||
return ncpus;
|
||||
}
|
||||
void detach()
|
||||
{
|
||||
if (!joinable())
|
||||
throw system_error();
|
||||
if (mHandle != _STD_THREAD_INVALID_HANDLE)
|
||||
{
|
||||
CloseHandle(mHandle);
|
||||
mHandle = _STD_THREAD_INVALID_HANDLE;
|
||||
}
|
||||
mThreadId.clear();
|
||||
}
|
||||
};
|
||||
|
||||
namespace this_thread
|
||||
{
|
||||
inline thread::id get_id() {return thread::id(GetCurrentThreadId());}
|
||||
inline void yield() {Sleep(0);}
|
||||
template< class Rep, class Period >
|
||||
void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration)
|
||||
{
|
||||
Sleep(chrono::duration_cast<chrono::milliseconds>(sleep_duration).count());
|
||||
}
|
||||
template <class Clock, class Duration>
|
||||
void sleep_until(const std::chrono::time_point<Clock,Duration>& sleep_time)
|
||||
{
|
||||
sleep_for(sleep_time-Clock::now());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif // WIN32STDTHREAD_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue