mirror of
https://github.com/schnaader/precomp-cpp
synced 2026-08-23 00:23:04 -04:00
- 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
211 lines
7.8 KiB
C++
211 lines
7.8 KiB
C++
/**
|
|
* @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
|