Skip to content

Commit

Permalink
Merge pull request #55 from nmcclatchey/minimal-headers
Browse files Browse the repository at this point in the history
Minimize Windows headers
  • Loading branch information
alxvasilev authored Aug 19, 2019
2 parents 4e22f33 + 2cc2291 commit 5217ba5
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 120 deletions.
22 changes: 14 additions & 8 deletions mingw.condition_variable.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@
#include <cassert>
#include <chrono>
#include <system_error>
#include <windows.h>

#include <sdkddkver.h> // Detect Windows version.
#if (WINVER < _WIN32_WINNT_VISTA)
#include <atomic>
#include <windef.h>
#include <winbase.h> // For CreateSemaphore
#include <handleapi.h>
#endif
#include <synchapi.h>

#include "mingw.mutex.h"
#include "mingw.shared_mutex.h"
Expand Down Expand Up @@ -71,7 +75,7 @@ class condition_variable_any
condition_variable_any(const condition_variable_any&) = delete;
condition_variable_any& operator=(const condition_variable_any&) = delete;
condition_variable_any()
: mSemaphore(CreateSemaphore(NULL, 0, 0xFFFF, NULL))
: mSemaphore(CreateSemaphoreA(NULL, 0, 0xFFFF, NULL))
{
if (mSemaphore == NULL)
throw std::system_error(GetLastError(), std::generic_category());
Expand Down Expand Up @@ -259,6 +263,7 @@ namespace vista
// If compiling for Vista or higher, use the native condition variable.
class condition_variable
{
static constexpr DWORD kInfinite = 0xffffffffl;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
CONDITION_VARIABLE cvariable_ = CONDITION_VARIABLE_INIT;
Expand Down Expand Up @@ -345,7 +350,7 @@ use native Win32 critical section objects.");

void wait (unique_lock<mutex> & lock)
{
wait_impl(lock, INFINITE);
wait_impl(lock, kInfinite);
}

template<class Predicate>
Expand All @@ -361,8 +366,8 @@ use native Win32 critical section objects.");
{
using namespace std::chrono;
auto timeout = duration_cast<milliseconds>(rel_time).count();
DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1);
bool result = wait_impl(lock, waittime) || (timeout >= INFINITE);
DWORD waittime = (timeout < kInfinite) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (kInfinite - 1);
bool result = wait_impl(lock, waittime) || (timeout >= kInfinite);
return result ? cv_status::no_timeout : cv_status::timeout;
}

Expand Down Expand Up @@ -399,6 +404,7 @@ use native Win32 critical section objects.");

class condition_variable_any
{
static constexpr DWORD kInfinite = 0xffffffffl;
using native_shared_mutex = windows7::shared_mutex;

condition_variable internal_cv_ {};
Expand Down Expand Up @@ -465,7 +471,7 @@ class condition_variable_any
template<class L>
void wait (L & lock)
{
wait_impl(lock, INFINITE);
wait_impl(lock, kInfinite);
}

template<class L, class Predicate>
Expand All @@ -480,8 +486,8 @@ class condition_variable_any
{
using namespace std::chrono;
auto timeout = duration_cast<milliseconds>(period).count();
DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1);
bool result = wait_impl(lock, waittime) || (timeout >= INFINITE);
DWORD waittime = (timeout < kInfinite) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (kInfinite - 1);
bool result = wait_impl(lock, waittime) || (timeout >= kInfinite);
return result ? cv_status::no_timeout : cv_status::timeout;
}

Expand Down
13 changes: 7 additions & 6 deletions mingw.future.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
#include "mingw.mutex.h"
#include "mingw.condition_variable.h"

#include <synchapi.h>
#include <handleapi.h>
#include <processthreadsapi.h>

// Note:
// std::shared_ptr is the natural choice for this. However, a custom
// implementation removes the need to keep a control block separate from the
Expand Down Expand Up @@ -473,17 +477,14 @@ class promise : mingw_stdthread::detail::FutureBase
{
if (valid() && !(mState->mType.load(std::memory_order_relaxed) & kSetFlag))
{
try {
throw future_error(future_errc::broken_promise);
} catch (...) {
set_exception(std::current_exception());
}
set_exception(std::make_exception_ptr(future_error(future_errc::broken_promise)));
}
}
/// \bug Might throw more exceptions than specified by the standard...
// Need OS support for this...
void make_ready_at_thread_exit (void)
{
static constexpr DWORD kInfinite = 0xffffffffl;
// Need to turn the pseudohandle from GetCurrentThread() into a true handle...
HANDLE thread_handle;
BOOL success = DuplicateHandle(GetCurrentProcess(),
Expand All @@ -508,7 +509,7 @@ class promise : mingw_stdthread::detail::FutureBase
}
ptr->get_condition_variable().notify_all();
// Wait for the original thread to die.
WaitForSingleObject(thread_handle, INFINITE);
WaitForSingleObject(thread_handle, kInfinite);
CloseHandle(thread_handle);

{
Expand Down
109 changes: 109 additions & 0 deletions mingw.invoke.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/// \file mingw.invoke.h
/// \brief Lightweight `invoke` implementation, for C++11 and C++14.
///
/// (c) 2018-2019 by Nathaniel J. McClatchey, San Jose, CA, United States
/// \author Nathaniel J. McClatchey, PhD
///
/// \copyright Simplified (2-clause) BSD License.
///
/// \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_INVOKE_H_
#define MINGW_INVOKE_H_

#include <type_traits> // For std::result_of, etc.
#include <utility> // For std::forward
#include <functional> // For std::reference_wrapper

namespace mingw_stdthread
{
namespace detail
{
// For compatibility, implement std::invoke for C++11 and C++14
#if __cplusplus < 201703L
template<bool PMemFunc, bool PMemData>
struct Invoker
{
template<class F, class... Args>
inline static typename std::result_of<F(Args...)>::type invoke (F&& f, Args&&... args)
{
return std::forward<F>(f)(std::forward<Args>(args)...);
}
};
template<bool>
struct InvokerHelper;

template<>
struct InvokerHelper<false>
{
template<class T1>
inline static auto get (T1&& t1) -> decltype(*std::forward<T1>(t1))
{
return *std::forward<T1>(t1);
}

template<class T1>
inline static auto get (const std::reference_wrapper<T1>& t1) -> decltype(t1.get())
{
return t1.get();
}
};

template<>
struct InvokerHelper<true>
{
template<class T1>
inline static auto get (T1&& t1) -> decltype(std::forward<T1>(t1))
{
return std::forward<T1>(t1);
}
};

template<>
struct Invoker<true, false>
{
template<class T, class F, class T1, class... Args>
inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\
decltype((InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f)(std::forward<Args>(args)...))
{
return (InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f)(std::forward<Args>(args)...);
}
};

template<>
struct Invoker<false, true>
{
template<class T, class F, class T1, class... Args>
inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\
decltype(InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f)
{
return InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f;
}
};

template<class F, class... Args>
struct InvokeResult
{
typedef Invoker<std::is_member_function_pointer<typename std::remove_reference<F>::type>::value,
std::is_member_object_pointer<typename std::remove_reference<F>::type>::value &&
(sizeof...(Args) == 1)> invoker;
inline static auto invoke (F&& f, Args&&... args) -> decltype(invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...))
{
return invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...);
};
};

template<class F, class...Args>
auto invoke (F&& f, Args&&... args) -> decltype(InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...))
{
return InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
#else
using std::invoke;
#endif
} // Namespace "detail"
} // Namespace "mingw_stdthread"

#endif
26 changes: 18 additions & 8 deletions mingw.mutex.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,17 @@
#include <cstdio>
#endif

#include <windows.h>

#include <sdkddkver.h> // Detect Windows version.
#if STDMUTEX_RECURSION_CHECKS
#include <processthreadsapi.h> // For GetCurrentThreadId
#endif
#include <synchapi.h> // For InitializeCriticalSection, etc.
#include <errhandlingapi.h> // For GetLastError
#include <handleapi.h>

// Need for the implementation of invoke
#include "mingw.thread.h"
#include "mingw.invoke.h"

#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)
#error To use the MinGW-std-threads library, you will need to define the macro _WIN32_WINNT to be 0x0501 (Windows XP) or higher.
Expand Down Expand Up @@ -291,18 +298,21 @@ using xp::mutex;

class recursive_timed_mutex
{
static constexpr DWORD kWaitAbandoned = 0x00000080l;
static constexpr DWORD kWaitObject0 = 0x00000000l;
static constexpr DWORD kInfinite = 0xffffffffl;
inline bool try_lock_internal (DWORD ms) noexcept
{
DWORD ret = WaitForSingleObject(mHandle, ms);
#ifndef NDEBUG
if (ret == WAIT_ABANDONED)
if (ret == kWaitAbandoned)
{
using namespace std;
fprintf(stderr, "FATAL: Thread terminated while holding a mutex.");
terminate();
}
#endif
return (ret == WAIT_OBJECT_0) || (ret == WAIT_ABANDONED);
return (ret == kWaitObject0) || (ret == kWaitAbandoned);
}
protected:
HANDLE mHandle;
Expand All @@ -325,19 +335,19 @@ class recursive_timed_mutex
}
void lock()
{
DWORD ret = WaitForSingleObject(mHandle, INFINITE);
DWORD ret = WaitForSingleObject(mHandle, kInfinite);
// If (ret == WAIT_ABANDONED), then the thread that held ownership was
// terminated. Behavior is undefined, but Windows will pass ownership to this
// thread.
#ifndef NDEBUG
if (ret == WAIT_ABANDONED)
if (ret == kWaitAbandoned)
{
using namespace std;
fprintf(stderr, "FATAL: Thread terminated while holding a mutex.");
terminate();
}
#endif
if ((ret != WAIT_OBJECT_0) && (ret != WAIT_ABANDONED))
if ((ret != kWaitObject0) && (ret != kWaitAbandoned))
{
throw std::system_error(GetLastError(), std::system_category());
}
Expand All @@ -358,7 +368,7 @@ class recursive_timed_mutex
auto timeout = duration_cast<milliseconds>(dur).count();
while (timeout > 0)
{
constexpr auto kMaxStep = static_cast<decltype(timeout)>(INFINITE-1);
constexpr auto kMaxStep = static_cast<decltype(timeout)>(kInfinite-1);
auto step = (timeout < kMaxStep) ? timeout : kMaxStep;
if (try_lock_internal(static_cast<DWORD>(step)))
return true;
Expand Down
13 changes: 6 additions & 7 deletions mingw.shared_mutex.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@
// For defer_lock_t, adopt_lock_t, and try_to_lock_t
#include "mingw.mutex.h"
// For this_thread::yield.
#include "mingw.thread.h"
//#include "mingw.thread.h"

// Might be able to use native Slim Reader-Writer (SRW) locks.
#ifdef _WIN32
#include <windows.h>
#include <sdkddkver.h> // Detect Windows version.
#include <synchapi.h>
#endif

namespace mingw_stdthread
Expand Down Expand Up @@ -97,8 +98,6 @@ class shared_mutex
if (expected >= kWriteBit - 1)
{
using namespace std;
using namespace this_thread;
yield();
expected = mCounter.load(std::memory_order_relaxed);
continue;
}
Expand Down Expand Up @@ -144,12 +143,12 @@ class shared_mutex
// Might be able to use relaxed memory order...
// Wait for the write-lock to be unlocked, then claim the write slot.
counter_type current;
while ((current = mCounter.fetch_or(kWriteBit, std::memory_order_acquire)) & kWriteBit)
this_thread::yield();
while ((current = mCounter.fetch_or(kWriteBit, std::memory_order_acquire)) & kWriteBit);
//this_thread::yield();
// Wait for readers to finish up.
while (current != kWriteBit)
{
this_thread::yield();
//this_thread::yield();
current = mCounter.load(std::memory_order_acquire);
}
#if STDMUTEX_RECURSION_CHECKS
Expand Down
Loading

0 comments on commit 5217ba5

Please sign in to comment.