diff --git a/.gitignore b/.gitignore index 095c868..c27d282 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ recorded.ogg -compile_win.sh diff --git a/ptt_talk_src/compile_win.sh b/ptt_talk_src/compile_win.sh new file mode 100755 index 0000000..440c247 --- /dev/null +++ b/ptt_talk_src/compile_win.sh @@ -0,0 +1 @@ +x86_64-w64-mingw32-g++ ptt_talk.cpp audio_rec_play.cpp sine.cpp -static-libgcc -static-libstdc++ -lportaudio -lws2_32 -lm -pthread -o win_version/ptttalk.exe diff --git a/ptt_talk_src/mingw.thread.h b/ptt_talk_src/mingw.thread.h new file mode 100644 index 0000000..a14084d --- /dev/null +++ b/ptt_talk_src/mingw.thread.h @@ -0,0 +1,360 @@ +/** +* @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 + +#if !defined(__cplusplus) || (__cplusplus < 201103L) +#error A C++11 compiler is required! +#endif + +// Use the standard classes for std::, if available. +#include + +#include // For std::size_t +#include // Detect error type. +#include // For std::terminate +#include // For std::system_error +#include // For std::hash +#include // For std::tuple +#include // For sleep timing. +#include // For std::unique_ptr +#include // Stream output for thread ids. +#include // For std::swap, std::forward + +#include "mingw_tread/mingw.invoke.h" + +#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) +#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\ + with Microsoft's API. We'll try to work around this, but we can make no\ + guarantees. This problem does not exist in MinGW-w64." +#include // No further granularity can be expected. +#else +#include // For WaitForSingleObject +#include // For CloseHandle, etc. +#include // For GetNativeSystemInfo +#include // For GetCurrentThreadId +#endif +#include // For _beginthreadex + +#ifndef NDEBUG +#include +#endif + +#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. +#endif + +// Instead of INVALID_HANDLE_VALUE, _beginthreadex returns 0. +namespace mingw_stdthread +{ +namespace detail +{ + template + struct IntSeq {}; + + template + struct GenIntSeq : GenIntSeq { }; + + template + struct GenIntSeq<0, S...> { typedef IntSeq type; }; + +// Use a template specialization to avoid relying on compiler optimization +// when determining the parameter integer sequence. + template + class ThreadFuncCall; +// We can't define the Call struct in the function - the standard forbids template methods in that case + template + class ThreadFuncCall, Args...> + { + static_assert(sizeof...(S) == sizeof...(Args), "Args must match."); + using Tuple = std::tuple::type...>; + typename std::decay::type mFunc; + Tuple mArgs; + + public: + ThreadFuncCall(Func&& aFunc, Args&&... aArgs) + : mFunc(std::forward(aFunc)), + mArgs(std::forward(aArgs)...) + { + } + + void callFunc() + { + detail::invoke(std::move(mFunc), std::move(std::get(mArgs)) ...); + } + }; + +// Allow construction of threads without exposing implementation. + class ThreadIdTool; +} // Namespace "detail" + +class thread +{ +public: + class id + { + DWORD mId = 0; + friend class thread; + friend class std::hash; + friend class detail::ThreadIdTool; + explicit id(DWORD aId) noexcept : mId(aId){} + public: + id (void) noexcept = default; + friend bool operator==(id x, id y) noexcept {return x.mId == y.mId; } + friend bool operator!=(id x, id y) noexcept {return x.mId != y.mId; } + friend bool operator< (id x, id y) noexcept {return x.mId < y.mId; } + friend bool operator<=(id x, id y) noexcept {return x.mId <= y.mId; } + friend bool operator> (id x, id y) noexcept {return x.mId > y.mId; } + friend bool operator>=(id x, id y) noexcept {return x.mId >= y.mId; } + + template + friend std::basic_ostream<_CharT, _Traits>& + operator<<(std::basic_ostream<_CharT, _Traits>& __out, id __id) + { + if (__id.mId == 0) + { + return __out << "(invalid std::thread::id)"; + } + else + { + return __out << __id.mId; + } + } + }; +private: + static constexpr HANDLE kInvalidHandle = nullptr; + static constexpr DWORD kInfinite = 0xffffffffl; + HANDLE mHandle; + id mThreadId; + + template + static unsigned __stdcall threadfunc(void* arg) + { + std::unique_ptr call(static_cast(arg)); + call->callFunc(); + return 0; + } + + static unsigned int _hardware_concurrency_helper() noexcept + { + SYSTEM_INFO sysinfo; +// This is one of the few functions used by the library which has a nearly- +// equivalent function defined in earlier versions of Windows. Include the +// workaround, just as a reminder that it does exist. +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) + ::GetNativeSystemInfo(&sysinfo); +#else + ::GetSystemInfo(&sysinfo); +#endif + return sysinfo.dwNumberOfProcessors; + } +public: + typedef HANDLE native_handle_type; + id get_id() const noexcept {return mThreadId;} + native_handle_type native_handle() const {return mHandle;} + thread(): mHandle(kInvalidHandle), mThreadId(){} + + thread(thread&& other) + :mHandle(other.mHandle), mThreadId(other.mThreadId) + { + other.mHandle = kInvalidHandle; + other.mThreadId = id{}; + } + + thread(const thread &other)=delete; + + template + explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId() + { + using ArgSequence = typename detail::GenIntSeq::type; + using Call = detail::ThreadFuncCall; + auto call = new Call( + std::forward(func), std::forward(args)...); + unsigned id_receiver; + auto int_handle = _beginthreadex(NULL, 0, threadfunc, + static_cast(call), 0, &id_receiver); + if (int_handle == 0) + { + mHandle = kInvalidHandle; + int errnum = errno; + delete call; +// Note: Should only throw EINVAL, EAGAIN, EACCES + throw std::system_error(errnum, std::generic_category()); + } else { + mThreadId.mId = id_receiver; + mHandle = reinterpret_cast(int_handle); + } + } + + bool joinable() const {return mHandle != kInvalidHandle;} + +// Note: Due to lack of synchronization, this function has a race condition +// if called concurrently, which leads to undefined behavior. The same applies +// to all other member functions of this class, but this one is mentioned +// explicitly. + void join() + { + using namespace std; + if (get_id() == id(GetCurrentThreadId())) + throw system_error(make_error_code(errc::resource_deadlock_would_occur)); + if (mHandle == kInvalidHandle) + throw system_error(make_error_code(errc::no_such_process)); + if (!joinable()) + throw system_error(make_error_code(errc::invalid_argument)); + WaitForSingleObject(mHandle, kInfinite); + CloseHandle(mHandle); + mHandle = kInvalidHandle; + mThreadId = id{}; + } + + ~thread() + { + if (joinable()) + { +#ifndef NDEBUG + std::printf("Error: Must join() or detach() a thread before \ +destroying it.\n"); +#endif + std::terminate(); + } + } + thread& operator=(const thread&) = delete; + thread& operator=(thread&& other) noexcept + { + if (joinable()) + { +#ifndef NDEBUG + std::printf("Error: Must join() or detach() a thread before \ +moving another thread to it.\n"); +#endif + std::terminate(); + } + swap(std::forward(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 unsigned int cached = _hardware_concurrency_helper(); + return cached; + } + + void detach() + { + if (!joinable()) + { + using namespace std; + throw system_error(make_error_code(errc::invalid_argument)); + } + if (mHandle != kInvalidHandle) + { + CloseHandle(mHandle); + mHandle = kInvalidHandle; + } + mThreadId = id{}; + } +}; + +namespace detail +{ + class ThreadIdTool + { + public: + static thread::id make_id (DWORD base_id) noexcept + { + return thread::id(base_id); + } + }; +} // Namespace "detail" + +namespace this_thread +{ + inline thread::id get_id() noexcept + { + return detail::ThreadIdTool::make_id(GetCurrentThreadId()); + } + inline void yield() noexcept {Sleep(0);} + template< class Rep, class Period > + void sleep_for( const std::chrono::duration& sleep_duration) + { + static constexpr DWORD kInfinite = 0xffffffffl; + using namespace std::chrono; + using rep = milliseconds::rep; + rep ms = duration_cast(sleep_duration).count(); + while (ms > 0) + { + constexpr rep kMaxRep = static_cast(kInfinite - 1); + auto sleepTime = (ms < kMaxRep) ? ms : kMaxRep; + Sleep(static_cast(sleepTime)); + ms -= sleepTime; + } + } + template + void sleep_until(const std::chrono::time_point& sleep_time) + { + sleep_for(sleep_time-Clock::now()); + } +} +} // Namespace mingw_stdthread + +namespace std +{ +// Because of quirks of the compiler, the common "using namespace std;" +// directive would flatten the namespaces and introduce ambiguity where there +// was none. Direct specification (std::), however, would be unaffected. +// Take the safe option, and include only in the presence of MinGW's win32 +// implementation. +#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) +using mingw_stdthread::thread; +// Remove ambiguity immediately, to avoid problems arising from the above. +//using std::thread; +namespace this_thread +{ +using namespace mingw_stdthread::this_thread; +} +#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#pragma message "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. These classes, found in namespace std, are not overridden\ + by the mingw-std-thread library. If you would still like to use this\ + implementation (as it is more lightweight), use the classes provided in\ + namespace mingw_stdthread." +#endif + +// Specialize hash for this implementation's thread::id, even if the +// std::thread::id already has a hash. +template<> +struct hash +{ + typedef mingw_stdthread::thread::id argument_type; + typedef size_t result_type; + size_t operator() (const argument_type & i) const noexcept + { + return i.mId; + } +}; +} +#endif // WIN32STDTHREAD_H diff --git a/ptt_talk_src/mingw_tread/mingw.condition_variable.h b/ptt_talk_src/mingw_tread/mingw.condition_variable.h new file mode 100644 index 0000000..493a5c4 --- /dev/null +++ b/ptt_talk_src/mingw_tread/mingw.condition_variable.h @@ -0,0 +1,564 @@ +/** +* @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 + +#if !defined(__cplusplus) || (__cplusplus < 201103L) +#error A C++11 compiler is required! +#endif +// Use the standard classes for std::, if available. +#include + +#include +#include +#include + +#include // Detect Windows version. +#if (WINVER < _WIN32_WINNT_VISTA) +#include +#endif +#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) +#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\ + with Microsoft's API. We'll try to work around this, but we can make no\ + guarantees. This problem does not exist in MinGW-w64." +#include // No further granularity can be expected. +#else +#if (WINVER < _WIN32_WINNT_VISTA) +#include +#include // For CreateSemaphore +#include +#endif +#include +#endif + +#include "mingw.mutex.h" +#include "mingw.shared_mutex.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. +#endif + +namespace mingw_stdthread +{ +#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) +enum class cv_status { no_timeout, timeout }; +#else +using std::cv_status; +#endif +namespace xp +{ +// Include the XP-compatible condition_variable classes only if actually +// compiling for XP. The XP-compatible classes are slower than the newer +// versions, and depend on features not compatible with Windows Phone 8. +#if (WINVER < _WIN32_WINNT_VISTA) +class condition_variable_any +{ + recursive_mutex mMutex {}; + std::atomic mNumWaiters {0}; + HANDLE mSemaphore; + HANDLE mWakeEvent {}; +public: + using native_handle_type = HANDLE; + 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() + : mSemaphore(CreateSemaphoreA(NULL, 0, 0xFFFF, NULL)) + { + if (mSemaphore == NULL) + throw std::system_error(GetLastError(), std::generic_category()); + mWakeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (mWakeEvent == NULL) + { + CloseHandle(mSemaphore); + throw std::system_error(GetLastError(), std::generic_category()); + } + } + ~condition_variable_any() + { + CloseHandle(mWakeEvent); + CloseHandle(mSemaphore); + } +private: + template + bool wait_impl(M& lock, DWORD timeout) + { + { + lock_guard 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 gracefully +// + else + { + using namespace std; + throw system_error(make_error_code(errc::protocol_error)); + } + } +public: + template + void wait(M& lock) + { + wait_impl(lock, INFINITE); + } + template + void wait(M& lock, Predicate pred) + { + while(!pred()) + { + wait(lock); + }; + } + + void notify_all() noexcept + { + lock_guard 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) + std::terminate(); + } + 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 lock(mMutex); + int targetWaiters = mNumWaiters.load() - 1; + if (targetWaiters <= -1) + return; + ReleaseSemaphore(mSemaphore, 1, NULL); + while(mNumWaiters > targetWaiters) + { + auto ret = WaitForSingleObject(mWakeEvent, 1000); + if (ret == WAIT_FAILED || ret == WAIT_ABANDONED) + std::terminate(); + } + assert(mNumWaiters == targetWaiters); + } + template + cv_status wait_for(M& lock, + const std::chrono::duration& rel_time) + { + using namespace std::chrono; + auto timeout = duration_cast(rel_time).count(); + DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast(timeout)) : (INFINITE - 1); + bool ret = wait_impl(lock, waittime) || (timeout >= INFINITE); + return ret?cv_status::no_timeout:cv_status::timeout; + } + + template + bool wait_for(M& lock, + const std::chrono::duration& rel_time, Predicate pred) + { + return wait_until(lock, std::chrono::steady_clock::now()+rel_time, pred); + } + template + cv_status wait_until (M& lock, + const std::chrono::time_point& abs_time) + { + return wait_for(lock, abs_time - Clock::now()); + } + template + bool wait_until (M& lock, + const std::chrono::time_point& abs_time, + Predicate pred) + { + while (!pred()) + { + if (wait_until(lock, abs_time) == cv_status::timeout) + { + return pred(); + } + } + return true; + } +}; +class condition_variable: condition_variable_any +{ + using base = condition_variable_any; +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 &lock) + { + base::wait(lock); + } + template + void wait(unique_lock& lock, Predicate pred) + { + base::wait(lock, pred); + } + template + cv_status wait_for(unique_lock& lock, const std::chrono::duration& rel_time) + { + return base::wait_for(lock, rel_time); + } + template + bool wait_for(unique_lock& lock, const std::chrono::duration& rel_time, Predicate pred) + { + return base::wait_for(lock, rel_time, pred); + } + template + cv_status wait_until (unique_lock& lock, const std::chrono::time_point& abs_time) + { + return base::wait_until(lock, abs_time); + } + template + bool wait_until (unique_lock& lock, const std::chrono::time_point& abs_time, Predicate pred) + { + return base::wait_until(lock, abs_time, pred); + } +}; +#endif // Compiling for XP +} // Namespace mingw_stdthread::xp + +#if (WINVER >= _WIN32_WINNT_VISTA) +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; +#pragma GCC diagnostic pop + + friend class condition_variable_any; + +#if STDMUTEX_RECURSION_CHECKS + template + inline static void before_wait (MTX * pmutex) + { + pmutex->mOwnerThread.checkSetOwnerBeforeUnlock(); + } + template + inline static void after_wait (MTX * pmutex) + { + pmutex->mOwnerThread.setOwnerAfterLock(GetCurrentThreadId()); + } +#else + inline static void before_wait (void *) { } + inline static void after_wait (void *) { } +#endif + + bool wait_impl (unique_lock & lock, DWORD time) + { + using mutex_handle_type = typename xp::mutex::native_handle_type; + static_assert(std::is_same::value, + "Native Win32 condition variable requires std::mutex to \ +use native Win32 critical section objects."); + xp::mutex * pmutex = lock.release(); + before_wait(pmutex); + BOOL success = SleepConditionVariableCS(&cvariable_, + pmutex->native_handle(), + time); + after_wait(pmutex); + lock = unique_lock(*pmutex, adopt_lock); + return success; + } + + bool wait_unique (windows7::mutex * pmutex, DWORD time) + { + before_wait(pmutex); + BOOL success = SleepConditionVariableSRW( native_handle(), + pmutex->native_handle(), + time, +// CONDITION_VARIABLE_LOCKMODE_SHARED has a value not specified by +// Microsoft's Dev Center, but is known to be (convertible to) a ULONG. To +// ensure that the value passed to this function is not equal to Microsoft's +// constant, we can either use a static_assert, or simply generate an +// appropriate value. + !CONDITION_VARIABLE_LOCKMODE_SHARED); + after_wait(pmutex); + return success; + } + bool wait_impl (unique_lock & lock, DWORD time) + { + windows7::mutex * pmutex = lock.release(); + bool success = wait_unique(pmutex, time); + lock = unique_lock(*pmutex, adopt_lock); + return success; + } +public: + using native_handle_type = PCONDITION_VARIABLE; + native_handle_type native_handle (void) + { + return &cvariable_; + } + + condition_variable (void) = default; + ~condition_variable (void) = default; + + condition_variable (const condition_variable &) = delete; + condition_variable & operator= (const condition_variable &) = delete; + + void notify_one (void) noexcept + { + WakeConditionVariable(&cvariable_); + } + + void notify_all (void) noexcept + { + WakeAllConditionVariable(&cvariable_); + } + + void wait (unique_lock & lock) + { + wait_impl(lock, kInfinite); + } + + template + void wait (unique_lock & lock, Predicate pred) + { + while (!pred()) + wait(lock); + } + + template + cv_status wait_for(unique_lock& lock, + const std::chrono::duration& rel_time) + { + using namespace std::chrono; + auto timeout = duration_cast(rel_time).count(); + DWORD waittime = (timeout < kInfinite) ? ((timeout < 0) ? 0 : static_cast(timeout)) : (kInfinite - 1); + bool result = wait_impl(lock, waittime) || (timeout >= kInfinite); + return result ? cv_status::no_timeout : cv_status::timeout; + } + + template + bool wait_for(unique_lock& lock, + const std::chrono::duration& rel_time, + Predicate pred) + { + return wait_until(lock, + std::chrono::steady_clock::now() + rel_time, + std::move(pred)); + } + template + cv_status wait_until (unique_lock& lock, + const std::chrono::time_point& abs_time) + { + return wait_for(lock, abs_time - Clock::now()); + } + template + bool wait_until (unique_lock& lock, + const std::chrono::time_point& abs_time, + Predicate pred) + { + while (!pred()) + { + if (wait_until(lock, abs_time) == cv_status::timeout) + { + return pred(); + } + } + return true; + } +}; + +class condition_variable_any +{ + static constexpr DWORD kInfinite = 0xffffffffl; + using native_shared_mutex = windows7::shared_mutex; + + condition_variable internal_cv_ {}; +// When available, the SRW-based mutexes should be faster than the +// CriticalSection-based mutexes. Only try_lock will be unavailable in Vista, +// and try_lock is not used by condition_variable_any. + windows7::mutex internal_mutex_ {}; + + template + bool wait_impl (L & lock, DWORD time) + { + unique_lock internal_lock(internal_mutex_); + lock.unlock(); + bool success = internal_cv_.wait_impl(internal_lock, time); + lock.lock(); + return success; + } +// If the lock happens to be called on a native Windows mutex, skip any extra +// contention. + inline bool wait_impl (unique_lock & lock, DWORD time) + { + return internal_cv_.wait_impl(lock, time); + } +// Some shared_mutex functionality is available even in Vista, but it's not +// until Windows 7 that a full implementation is natively possible. The class +// itself is defined, with missing features, at the Vista feature level. + bool wait_impl (unique_lock & lock, DWORD time) + { + native_shared_mutex * pmutex = lock.release(); + bool success = internal_cv_.wait_unique(pmutex, time); + lock = unique_lock(*pmutex, adopt_lock); + return success; + } + bool wait_impl (shared_lock & lock, DWORD time) + { + native_shared_mutex * pmutex = lock.release(); + BOOL success = SleepConditionVariableSRW(native_handle(), + pmutex->native_handle(), time, + CONDITION_VARIABLE_LOCKMODE_SHARED); + lock = shared_lock(*pmutex, adopt_lock); + return success; + } +public: + using native_handle_type = typename condition_variable::native_handle_type; + + native_handle_type native_handle (void) + { + return internal_cv_.native_handle(); + } + + void notify_one (void) noexcept + { + internal_cv_.notify_one(); + } + + void notify_all (void) noexcept + { + internal_cv_.notify_all(); + } + + condition_variable_any (void) = default; + ~condition_variable_any (void) = default; + + template + void wait (L & lock) + { + wait_impl(lock, kInfinite); + } + + template + void wait (L & lock, Predicate pred) + { + while (!pred()) + wait(lock); + } + + template + cv_status wait_for(L& lock, const std::chrono::duration& period) + { + using namespace std::chrono; + auto timeout = duration_cast(period).count(); + DWORD waittime = (timeout < kInfinite) ? ((timeout < 0) ? 0 : static_cast(timeout)) : (kInfinite - 1); + bool result = wait_impl(lock, waittime) || (timeout >= kInfinite); + return result ? cv_status::no_timeout : cv_status::timeout; + } + + template + bool wait_for(L& lock, const std::chrono::duration& period, + Predicate pred) + { + return wait_until(lock, std::chrono::steady_clock::now() + period, + std::move(pred)); + } + template + cv_status wait_until (L& lock, + const std::chrono::time_point& abs_time) + { + return wait_for(lock, abs_time - Clock::now()); + } + template + bool wait_until (L& lock, + const std::chrono::time_point& abs_time, + Predicate pred) + { + while (!pred()) + { + if (wait_until(lock, abs_time) == cv_status::timeout) + { + return pred(); + } + } + return true; + } +}; +} // Namespace vista +#endif +#if WINVER < 0x0600 +using xp::condition_variable; +using xp::condition_variable_any; +#else +using vista::condition_variable; +using vista::condition_variable_any; +#endif +} // Namespace mingw_stdthread + +// Push objects into std, but only if they are not already there. +namespace std +{ +// Because of quirks of the compiler, the common "using namespace std;" +// directive would flatten the namespaces and introduce ambiguity where there +// was none. Direct specification (std::), however, would be unaffected. +// Take the safe option, and include only in the presence of MinGW's win32 +// implementation. +#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) +using mingw_stdthread::cv_status; +using mingw_stdthread::condition_variable; +using mingw_stdthread::condition_variable_any; +#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#pragma message "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. These classes, found in namespace std, are not overridden\ + by the mingw-std-thread library. If you would still like to use this\ + implementation (as it is more lightweight), use the classes provided in\ + namespace mingw_stdthread." +#endif +} +#endif // MINGW_CONDITIONAL_VARIABLE_H diff --git a/ptt_talk_src/mingw_tread/mingw.future.h b/ptt_talk_src/mingw_tread/mingw.future.h new file mode 100644 index 0000000..9e0dd96 --- /dev/null +++ b/ptt_talk_src/mingw_tread/mingw.future.h @@ -0,0 +1,1131 @@ +/// \file mingw.future.h +/// \brief Standard-compliant C++ futures for MinGW +/// +/// (c) 2018 by Nathaniel J. McClatchey, San Jose, California +/// \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. +/// \note Target Windows version is determined by WINVER, which is determined in +/// from _WIN32_WINNT, which can itself be set by the user. + +#ifndef MINGW_FUTURE_H_ +#define MINGW_FUTURE_H_ + +#if !defined(__cplusplus) || (__cplusplus < 201103L) +#error The MinGW STD Threads library requires a compiler supporting C++11. +#endif + +#include + +#include +#include +#include // For std::pair +#include +#include +#include // For std::hash + +#include "mingw.thread.h" // Start new threads, and use invoke. + +// Mutexes and condition variables are used explicitly. +#include "mingw.mutex.h" +#include "mingw.condition_variable.h" + +#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) +#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\ + with Microsoft's API. We'll try to work around this, but we can make no\ + guarantees. This problem does not exist in MinGW-w64." +#include // No further granularity can be expected. +#else +#include +#include +#include +#endif + +// 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 +// class itself (no need to support weak pointers). + +namespace mingw_stdthread +{ +using std::future_errc; +using std::future_error; +using std::future_status; +using std::launch; +using std::future_category; + +namespace detail +{ +struct Empty { }; + +// Use a class template to allow instantiation of statics in a header-only +// library. Note: Template will only be instantiated once to avoid bloat. +template +struct FutureStatic +{ + enum Type : uint_fast8_t + { + kUndecided = 0x00, + kDeferred = 0x05, + kValue = 0x02, + kException = 0x03, + kSetFlag = 0x02, + kTypeMask = 0x03, + kReadyFlag = 0x04 + }; + + static std::vector > sync_pool; + + static mutex & get_mutex (void const * ptr) + { + std::hash hash_func; + return sync_pool[hash_func(ptr) % sync_pool.size()].first; + } + static condition_variable & get_condition_variable (void const * ptr) + { + std::hash hash_func; + return sync_pool[hash_func(ptr) % sync_pool.size()].second; + } +}; +template +std::vector > FutureStatic::sync_pool (thread::hardware_concurrency() * 2 + 1); + +struct FutureStateBase +{ + inline mutex & get_mutex (void) const + { + return FutureStatic::get_mutex(this); + } + inline condition_variable & get_condition_variable (void) const + { + return FutureStatic::get_condition_variable(this); + } + typedef typename FutureStatic::Type Type; +// Destroys this object. Used for allocator-awareness. + virtual void deallocate_this (void) noexcept = 0; + virtual ~FutureStateBase (void) = default; + + FutureStateBase (FutureStateBase const &) = delete; + FutureStateBase & operator= (FutureStateBase const &) = delete; + + FutureStateBase(Type t) noexcept + : mReferences(0), mType(t) + { + } + + void increment_references (void) noexcept + { + mReferences.fetch_add(1, std::memory_order_relaxed); + } + + void decrement_references (void) noexcept + { + if (mReferences.fetch_sub(1, std::memory_order_acquire) == 0) + deallocate_this(); + } + + std::atomic mReferences; + std::atomic mType; +}; + +// Reduce compilation time and improve code re-use. +struct FutureBase : public FutureStatic +{ + typedef FutureStatic Base; + FutureStateBase * mState; + + mutex & get_mutex (void) const + { + return FutureStatic::get_mutex(mState); + } + condition_variable & get_condition_variable (void) const + { + return FutureStatic::get_condition_variable(mState); + } + + FutureBase (FutureStateBase * ptr) noexcept + : mState(ptr) + { + } + + FutureBase (FutureBase && source) noexcept + : mState(source.mState) + { + source.mState = nullptr; + } + + ~FutureBase (void) + { + release(); + } + + FutureBase (FutureBase const &) = delete; + FutureBase & operator= (FutureBase const &) = delete; + + bool valid (void) const noexcept + { + return mState != nullptr; + } + +// Releases this object's hold on its state. Requires a specification of +// which state is being used. + inline void release (void) noexcept + { + if (valid()) + mState->decrement_references(); + mState = nullptr; + } + + void wait (std::unique_lock & lock) const + { +#if !defined(NDEBUG) + if (!valid()) + throw future_error(future_errc::no_state); +#endif +// If there's already a value or exception, don't do any extraneous +// synchronization. The `get()` method will do that for us. + if (mState->mType.load(std::memory_order_relaxed) & kReadyFlag) + return; + get_condition_variable().wait(lock, [this](void)->bool { + return mState->mType.load(std::memory_order_relaxed) & kReadyFlag; + }); + } + + template + future_status wait_for (std::chrono::duration const & dur) const + { +#if !defined(NDEBUG) + if (!valid()) + throw future_error(future_errc::no_state); +#endif + auto current_state = mState->mType.load(std::memory_order_relaxed); + if (current_state & kReadyFlag) + return (current_state == kDeferred) ? future_status::deferred : future_status::ready; + std::unique_lock lock { get_mutex() }; + if (get_condition_variable().wait_for(lock, dur, + [this](void)->bool { + return mState->mType.load(std::memory_order_relaxed) & kReadyFlag; + })) + return future_status::ready; + else + return future_status::timeout; + } + + template + future_status wait_until(const std::chrono::time_point& time) const + { + return wait_for(time - Clock::now()); + } +}; + +template +struct FutureState : public FutureStateBase +{ +// The state never needs more than one of these at any one time, so don't +// waste space or allocation time. + union { + struct {} mUndecided; // Included to make the active member unambiguous. + T mObject; + std::exception_ptr mException; + std::function mFunction; + }; + + FutureState (void) noexcept + : FutureStateBase(Type::kUndecided), mUndecided() + { + } + + FutureState (std::function && deferred_function) + : FutureStateBase(Type::kDeferred), mFunction(std::move(deferred_function)) + { + } + + void deallocate_this (void) noexcept override + { + delete this; + } + + template + void set_value (Arg && arg) + { + assert(!(mType.load(std::memory_order_relaxed) & Type::kSetFlag)); + new(&mObject) T (std::forward(arg)); + mType.store(Type::kValue | Type::kReadyFlag, std::memory_order_release); + } + template + void set_exception (Arg && arg) + { + assert(!(mType.load(std::memory_order_relaxed) & Type::kSetFlag)); + new(&mException) std::exception_ptr (std::forward(arg)); + mType.store(Type::kException | Type::kReadyFlag, std::memory_order_release); + } +// These overloads set value/exception, but don't make it ready. + template + void set_value (Arg && arg, bool) + { + assert(!(mType.load(std::memory_order_relaxed) & Type::kSetFlag)); + new(&mObject) T (std::forward(arg)); + mType.store(Type::kValue, std::memory_order_release); + } + template + void set_exception (Arg && arg, bool) + { + assert(!(mType.load(std::memory_order_relaxed) & Type::kSetFlag)); + new(&mException) std::exception_ptr (std::forward(arg)); + mType.store(Type::kException, std::memory_order_release); + } + //private: + ~FutureState (void) + { + switch (mType.load(std::memory_order_acquire) & Type::kTypeMask) + { + case Type::kDeferred & Type::kTypeMask: + mFunction.~function(); + break; + case Type::kValue: + mObject.~T(); + break; + case Type::kException: + mException.~exception_ptr(); + break; + default:; + } + } +}; + +template +struct FutureStateAllocated : public FutureState +{ + typedef typename std::allocator_traits::void_pointer void_pointer; + void_pointer mThis; + Alloc mAllocator; + + FutureStateAllocated (Alloc const & alloc, void_pointer const & vptr) noexcept + : FutureState(), mThis(vptr), mAllocator(alloc) + { + } + + FutureStateAllocated (FutureStateAllocated const &) = delete; + FutureStateAllocated & operator= (FutureStateAllocated const &) = delete; + + void deallocate_this (void) noexcept override + { + typedef typename std::allocator_traits::template rebind_traits > allocator_traits; + typename allocator_traits::allocator_type alloc(std::move(mAllocator)); + typedef typename allocator_traits::pointer pointer; + pointer ptr(static_cast(mThis)); + allocator_traits::destroy(alloc, this); + allocator_traits::deallocate(alloc, ptr, 1); + } +}; +} // Namespace "detail" + +#if (defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)) +} +namespace std { +#else +template +class future; +template +class shared_future; +template +class promise; +#endif + +template +class future : mingw_stdthread::detail::FutureBase +{ + typedef mingw_stdthread::detail::FutureState state_type; + future (state_type * ptr) noexcept + : FutureBase(ptr) + { + } + + friend class shared_future; + friend class promise; + + template + friend class future; + + template + friend future<__async_result_of<_Fn, _Args...>> async (std::launch, _Fn &&, _Args&&...); + public: + using FutureBase::valid; + using FutureBase::wait_for; + using FutureBase::wait_until; + + future (void) noexcept + : FutureBase(nullptr) + { + } + future & operator= (future && source) noexcept + { +// Check for this atypical behavior rather than creating a nonsensical state. + if (this != &source) + { + release(); + mState = source.mState; + source.mState = nullptr; + } + return *this; + } + future (future && source) noexcept + : FutureBase(std::move(source)) + { + } + + ~future (void) = default; + + future (future const &) = delete; + future & operator= (future const &) = delete; + + T const & get (void) const + { + wait(); + if (mState->mType.load(std::memory_order_acquire) == (kValue | kReadyFlag)) + return static_cast(mState)->mObject; + else + { + assert(mState->mType.load(std::memory_order_relaxed) == (kException | kReadyFlag)); + std::rethrow_exception(static_cast(mState)->mException); + } + } + + shared_future share (void) noexcept; + + void wait (void) const + { + std::unique_lock lock { get_mutex() }; + FutureBase::wait(lock); + if (mState->mType.load(std::memory_order_acquire) == kDeferred) + { + state_type * ptr = static_cast(mState); + decltype(ptr->mFunction) func = std::move(ptr->mFunction); + ptr->mFunction.~function(); + func(); + ptr->get_condition_variable().notify_all(); + } + } +}; + +template +class shared_future : future +{ + typedef typename future::state_type state_type; + public: + using future::get; + using future::wait; + using future::wait_for; + using future::wait_until; + using future::valid; + + shared_future (void) noexcept : future() + { + } + + shared_future (shared_future && source) noexcept + : future(std::move(source)) + { + } + + shared_future & operator= (shared_future && source) noexcept + { + return future::operator=(std::move(source)); + } + + shared_future (shared_future const & source) noexcept(__cplusplus >= 201703L) + : future(static_cast(source.mState)) + { + future::mState->increment_references(); + } + + shared_future & operator= (shared_future const & source) noexcept(__cplusplus >= 201703L) + { + if (future::mState == source.mState) + return *this; + future::release(); + future::mState = source.mState; + future::mState->increment_references(); + return *this; + } + + shared_future (future && source) noexcept + : future(std::move(source)) + { + } + + shared_future & operator= (future && source) noexcept + { + future::operator=(std::move(source)); + return *this; + } + + ~shared_future (void) = default; +}; + +template +class promise : mingw_stdthread::detail::FutureBase +{ + bool mRetrieved; + typedef mingw_stdthread::detail::FutureState state_type; + void check_before_set (void) const + { + if (!valid()) + throw future_error(future_errc::no_state); + if (mState->mType.load(std::memory_order_relaxed) & kSetFlag) + throw future_error(future_errc::promise_already_satisfied); + } + + void check_abandon (void) + { + if (valid() && !(mState->mType.load(std::memory_order_relaxed) & kSetFlag)) + { + 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(), + GetCurrentThread(), + GetCurrentProcess(), + &thread_handle, + 0, // Access doesn't matter. Will be duplicated. + FALSE, // No need for this to be inherited. + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + if (!success) + throw std::runtime_error("MinGW STD Threads library failed to make a promise ready after thread exit."); + + mState->increment_references(); + bool handle_handled = false; + try { + state_type * ptr = static_cast(mState); + mingw_stdthread::thread watcher_thread ([ptr, thread_handle, &handle_handled](void) + { + { + std::lock_guard guard (ptr->get_mutex()); + handle_handled = true; + } + ptr->get_condition_variable().notify_all(); +// Wait for the original thread to die. + WaitForSingleObject(thread_handle, kInfinite); + CloseHandle(thread_handle); + + { + std::lock_guard guard (ptr->get_mutex()); + ptr->mType.fetch_or(kReadyFlag, std::memory_order_relaxed); + } + ptr->get_condition_variable().notify_all(); + + ptr->decrement_references(); + }); + { + std::unique_lock guard (ptr->get_mutex()); + ptr->get_condition_variable().wait(guard, [&handle_handled](void)->bool + { + return handle_handled; + }); + } + watcher_thread.detach(); + } + catch (...) + { +// Because the original promise is still alive, this can't be the decrement +// destroys it. + mState->decrement_references(); + if (!handle_handled) + CloseHandle(thread_handle); + } + } + + template + future make_future (void) + { + if (!valid()) + throw future_error(future_errc::no_state); + if (mRetrieved) + throw future_error(future_errc::future_already_retrieved); + mState->increment_references(); + mRetrieved = true; + return future(static_cast(mState)); + } + + template + friend class promise; + public: +// Create a promise with an empty state, with the reference counter set to +// indicate that the state is only held by this promise (i.e. not by any +// futures). + promise (void) + : FutureBase(new state_type ()), mRetrieved(false) + { + } + + template + promise (std::allocator_arg_t, Alloc const & alloc) + : FutureBase(nullptr), mRetrieved(false) + { + typedef mingw_stdthread::detail::FutureStateAllocated State; + typedef typename std::allocator_traits::template rebind_traits Traits; + typename Traits::allocator_type rebound_alloc(alloc); + typename Traits::pointer ptr = Traits::allocate(rebound_alloc, 1); + typename Traits::void_pointer vptr = ptr; + State * sptr = std::addressof(*ptr); + Traits::construct(rebound_alloc, sptr, std::move(rebound_alloc), vptr); + mState = static_cast(sptr); + } + + promise (promise && source) noexcept + : FutureBase(std::move(source)), mRetrieved(source.mRetrieved) + { + } + + ~promise (void) + { + check_abandon(); + } + + promise & operator= (promise && source) noexcept + { + if (this == &source) + return *this; + check_abandon(); + release(); + mState = source.mState; + mRetrieved = source.mRetrieved; + source.mState = nullptr; + return *this; + } + + void swap (promise & other) noexcept + { + std::swap(mState, other.mState); + std::swap(mRetrieved, other.mRetrieved); + } + + promise (promise const &) = delete; + promise & operator= (promise const &) = delete; + + future get_future (void) + { + return make_future(); + } + + void set_value (T const & value) + { + { + std::lock_guard lock { get_mutex() }; + check_before_set(); + static_cast(mState)->set_value(value); + } + get_condition_variable().notify_all(); + } + + void set_value (T && value) + { + { + std::lock_guard lock { get_mutex() }; + check_before_set(); + static_cast(mState)->set_value(std::move(value)); + } + get_condition_variable().notify_all(); + } + + void set_value_at_thread_exit (T const & value) + { + { + std::lock_guard lock { get_mutex() }; + check_before_set(); + static_cast(mState)->set_value(value, false); + } + make_ready_at_thread_exit(); + } + + void set_value_at_thread_exit (T && value) + { + { + std::lock_guard lock { get_mutex() }; + check_before_set(); + static_cast(mState)->set_value(std::move(value), false); + } + make_ready_at_thread_exit(); + } + + void set_exception (std::exception_ptr eptr) + { + { + std::lock_guard lock { get_mutex() }; + check_before_set(); + static_cast(mState)->set_exception(eptr); + } + get_condition_variable().notify_all(); + } + + void set_exception_at_thread_exit (std::exception_ptr eptr) + { + { + std::lock_guard lock { get_mutex() }; + check_before_set(); + static_cast(mState)->set_exception(eptr, false); + } + make_ready_at_thread_exit(); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Reference Specialization // +//////////////////////////////////////////////////////////////////////////////// + +template +class future : future +{ + typedef future Base; + template + friend class shared_future; + template + friend class promise; + + future (typename Base::state_type * state) + : Base(state) + { + } + + template + friend future<__async_result_of<_Fn, _Args...>> async (std::launch, _Fn &&, _Args&&...); + public: + using Base::valid; + using Base::wait_for; + using Base::wait_until; + using Base::wait; + + future (void) noexcept = default; + + inline T& get (void) const + { + return *static_cast(Base::get()); + } + + shared_future share (void) noexcept; +}; + +template +class shared_future : shared_future +{ + typedef shared_future Base; + public: + using Base::wait; + using Base::wait_for; + using Base::wait_until; + using Base::valid; + + inline T& get (void) const + { + return *static_cast(Base::get()); + } + + shared_future (future && source) noexcept + : Base(std::move(source)) + { + } + + shared_future & operator= (future && source) noexcept + { + Base::operator=(std::move(source)); + return *this; + } + + ~shared_future (void) = default; +}; + +template +class promise : private promise +{ + typedef promise Base; + public: + using Base::set_exception; + using Base::set_exception_at_thread_exit; + + promise (void) = default; + template + promise (std::allocator_arg_t arg, Alloc const & alloc) + : Base(arg, alloc) + { + } + + inline void set_value (T & value) + { + typedef typename std::remove_cv::type T_non_cv; + Base::set_value(const_cast(std::addressof(value))); + } + + inline void set_value_at_thread_exit (T & value) + { + typedef typename std::remove_cv::type T_non_cv; + Base::set_value_at_thread_exit(const_cast(std::addressof(value))); + } + + inline future get_future (void) + { + return Base::template make_future(); + } + + void swap (promise & other) noexcept + { + Base::swap(other); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Void Specialization // +//////////////////////////////////////////////////////////////////////////////// + +template<> +class future : future +{ + typedef mingw_stdthread::detail::Empty Empty; + template + friend class shared_future; + template + friend class promise; + + future(future::state_type * state) + : future(state) + { + } + + template + friend future<__async_result_of<_Fn, _Args...>> async (std::launch, _Fn &&, _Args&&...); + + public: + using future::valid; + using future::wait_for; + using future::wait_until; + using future::wait; + + future (void) noexcept = default; + + void get (void) const + { + future::get(); + } + + shared_future share (void) noexcept; +}; + +template<> +class shared_future : shared_future +{ + typedef mingw_stdthread::detail::Empty Empty; + public: + using shared_future::wait; + using shared_future::wait_for; + using shared_future::wait_until; + using shared_future::valid; + + void get (void) const + { + shared_future::get(); + } + + shared_future (void) noexcept = default; + + shared_future (shared_future && source) noexcept = default; + + shared_future & operator= (shared_future && source) noexcept = default; + + shared_future (shared_future const & source) noexcept(__cplusplus >= 201703L) = default; + + shared_future & operator= (shared_future const & source) noexcept(__cplusplus >= 201703L) = default; + + shared_future (future && source) noexcept + : shared_future(std::move(source)) + { + } + + shared_future & operator= (future && source) noexcept + { + shared_future::operator=(std::move(source)); + return *this; + } + + ~shared_future (void) = default; +}; + +inline shared_future future::share (void) noexcept +{ + return shared_future(std::move(*this)); +} + +template +shared_future future::share (void) noexcept +{ + return shared_future(std::move(*this)); +} + +template +shared_future future::share (void) noexcept +{ + return shared_future(std::move(*this)); +} + +template<> +class promise : private promise +{ + typedef mingw_stdthread::detail::Empty Empty; + public: + using promise::set_exception; + using promise::set_exception_at_thread_exit; + + promise (void) = default; + template + promise (std::allocator_arg_t arg, Alloc const & alloc) + : promise(arg, alloc) + { + } + + inline void set_value (void) + { + promise::set_value(Empty()); + } + + inline void set_value_at_thread_exit (void) + { + promise::set_value_at_thread_exit(Empty()); + } + + inline future get_future (void) + { + return promise::template make_future(); + } + + void swap (promise & other) noexcept + { + promise::swap(other); + } +}; + + + +template +void swap(promise & lhs, promise & rhs) noexcept +{ + lhs.swap(rhs); +} + +template +struct uses_allocator, Alloc> : std::true_type +{ +}; + +} // Namespace "std" +namespace mingw_stdthread +{ +namespace detail +{ +template +struct StorageHelper +{ + template + static void store_deferred (FutureState * state_ptr, Func && func, Args&&... args) + { + try { + state_ptr->set_value(invoke(std::forward(func), std::forward(args)...)); + } catch (...) { + state_ptr->set_exception(std::current_exception()); + } + } + template + static void store (FutureState * state_ptr, Func && func, Args&&... args) + { + try { + auto result = invoke(std::forward(func), std::forward(args)...); + std::lock_guard lock { state_ptr->get_mutex() }; // Reveal the results of the above action to + state_ptr->set_value(std::move(result)); + } catch (...) { + std::lock_guard lock { state_ptr->get_mutex() }; // Reveal the results of the above action to + state_ptr->set_exception(std::current_exception()); + } + state_ptr->get_condition_variable().notify_all(); + } +}; + +template +struct StorageHelper +{ + template + static void store_deferred (FutureState * state_ptr, Func && func, Args&&... args) + { + try { + typedef typename std::remove_cv::type Ref_non_cv; + Ref & rf = invoke(std::forward(func), std::forward(args)...); + state_ptr->set_value(const_cast(std::addressof(rf))); + } catch (...) { + state_ptr->set_exception(std::current_exception()); + } + } + template + static void store (FutureState * state_ptr, Func && func, Args&&... args) + { + try { + typedef typename std::remove_cv::type Ref_non_cv; + Ref & rf = invoke(std::forward(func), std::forward(args)...); + std::lock_guard lock { state_ptr->get_mutex() }; + state_ptr->set_value(const_cast(std::addressof(rf))); + } catch (...) { + std::lock_guard lock { state_ptr->get_mutex() }; + state_ptr->set_exception(std::current_exception()); + } + state_ptr->get_condition_variable().notify_all(); + } +}; + +template<> +struct StorageHelper +{ + template + static void store_deferred (FutureState * state_ptr, Func && func, Args&&... args) + { + try { + invoke(std::forward(func), std::forward(args)...); + state_ptr->set_value(Empty{}); + } catch (...) { + state_ptr->set_exception(std::current_exception()); + } + } + template + static void store (FutureState * state_ptr, Func && func, Args&&... args) + { + try { + invoke(std::forward(func), std::forward(args)...); + std::lock_guard lock { state_ptr->get_mutex() }; + state_ptr->set_value(Empty{}); + } catch (...) { + std::lock_guard lock { state_ptr->get_mutex() }; + state_ptr->set_exception(std::current_exception()); + } + state_ptr->get_condition_variable().notify_all(); + } +}; +} // Namespace "detail" +} // Namespace "mingw_stdthread" +namespace std +{ + + +// Unfortunately, MinGW's locks us into a particular (non-standard) +// signature for async. +template< class Function, class... Args> +/*#if (__cplusplus < 201703L) +std::future::type(std::decay::type...)>::type> +#else +#if (__cplusplus > 201703L) +[[nodiscard]] +#endif +std::future, std::decay_t...>> +#endif*/ +#if (__cplusplus > 201703L) +[[nodiscard]] +#endif +std::future<__async_result_of > + async(Function&& f, Args&&... args) +{ + return async(launch::async | launch::deferred, std::forward(f), std::forward(args)...); +} +template< class Function, class... Args > +/*#if (__cplusplus < 201703L) +std::future::type(std::decay::type...)>::type> +#else +#if (__cplusplus > 201703L) +[[nodiscard]] +#endif +std::future, std::decay_t...> > +#endif*/ +#if (__cplusplus > 201703L) +[[nodiscard]] +#endif +std::future<__async_result_of > + async(std::launch policy, Function&& f, Args&&... args) +{ + typedef __async_result_of result_type; +/*#if (__cplusplus < 201703L) + typedef std::result_of::type(std::decay::type...)>::type result_type; +#else + typedef std::invoke_result_t, std::decay_t...> result_type; +#endif*/ + typedef future future_type; + typedef typename future_type::state_type state_type; + + //auto setter = [] + + state_type * state_ptr = nullptr; + /*if ((policy & std::launch::async) == std::launch::async) + state_ptr = new state_type (); + else + state_ptr = new state_type (std::function(std::bind(std::forward(f), std::forward(args)...)));*/ + + + if ((policy & std::launch::async) == std::launch::async) + { + auto deleter = [](state_type * ptr) { ptr->decrement_references(); }; + state_ptr = new state_type (); + state_ptr->increment_references(); + std::unique_ptr ooptr { state_ptr, deleter }; + mingw_stdthread::thread t ([](decltype(ooptr) ptr, typename std::decay::type f2, typename std::decay::type... args2) + { + typedef mingw_stdthread::detail::StorageHelper s_helper; + s_helper::store(ptr.get(), f2, args2...); + }, std::move(ooptr), std::forward(f), std::forward(args)...); + t.detach(); + } else { + typedef std::function func_type; + struct Packed + { + func_type func; + state_type * ptr; + }; + std::shared_ptr bound { new Packed { std::bind(std::forward(f), std::forward(args)...), nullptr } }; + state_ptr = new state_type (std::function([bound](void) + { + typedef mingw_stdthread::detail::StorageHelper s_helper; + s_helper::store_deferred(bound->ptr, std::move(bound->func)); + })); + bound->ptr = state_ptr; + } + assert(state_ptr != nullptr); + return future { state_ptr }; +} + +#if (defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)) +} // Namespace std +namespace mingw_stdthread +{ +using std::future; +using std::shared_future; +using std::promise; +using std::async; +#else +} // Namespace mingw_stdthread +namespace std +{ +template +void swap(mingw_stdthread::promise & lhs, mingw_stdthread::promise & rhs) noexcept +{ + lhs.swap(rhs); +} + +template +struct uses_allocator, Alloc> : std::true_type +{ +}; +#endif +} // Namespace + +#endif // MINGW_FUTURE_H_ diff --git a/ptt_talk_src/mingw_tread/mingw.invoke.h b/ptt_talk_src/mingw_tread/mingw.invoke.h new file mode 100644 index 0000000..d5c9dd3 --- /dev/null +++ b/ptt_talk_src/mingw_tread/mingw.invoke.h @@ -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 // For std::result_of, etc. +#include // For std::forward +#include // For std::reference_wrapper + +namespace mingw_stdthread +{ +namespace detail +{ +// For compatibility, implement std::invoke for C++11 and C++14 +#if __cplusplus < 201703L + template + struct Invoker + { + template + inline static typename std::result_of::type invoke (F&& f, Args&&... args) + { + return std::forward(f)(std::forward(args)...); + } + }; + template + struct InvokerHelper; + + template<> + struct InvokerHelper + { + template + inline static auto get (T1&& t1) -> decltype(*std::forward(t1)) + { + return *std::forward(t1); + } + + template + inline static auto get (const std::reference_wrapper& t1) -> decltype(t1.get()) + { + return t1.get(); + } + }; + + template<> + struct InvokerHelper + { + template + inline static auto get (T1&& t1) -> decltype(std::forward(t1)) + { + return std::forward(t1); + } + }; + + template<> + struct Invoker + { + template + inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\ + decltype((InvokerHelper::type>::value>::get(std::forward(t1)).*f)(std::forward(args)...)) + { + return (InvokerHelper::type>::value>::get(std::forward(t1)).*f)(std::forward(args)...); + } + }; + + template<> + struct Invoker + { + template + inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\ + decltype(InvokerHelper::type>::value>::get(t1).*f) + { + return InvokerHelper::type>::value>::get(t1).*f; + } + }; + + template + struct InvokeResult + { + typedef Invoker::type>::value, + std::is_member_object_pointer::type>::value && + (sizeof...(Args) == 1)> invoker; + inline static auto invoke (F&& f, Args&&... args) -> decltype(invoker::invoke(std::forward(f), std::forward(args)...)) + { + return invoker::invoke(std::forward(f), std::forward(args)...); + } + }; + + template + auto invoke (F&& f, Args&&... args) -> decltype(InvokeResult::invoke(std::forward(f), std::forward(args)...)) + { + return InvokeResult::invoke(std::forward(f), std::forward(args)...); + } +#else + using std::invoke; +#endif +} // Namespace "detail" +} // Namespace "mingw_stdthread" + +#endif diff --git a/ptt_talk_src/mingw_tread/mingw.mutex.h b/ptt_talk_src/mingw_tread/mingw.mutex.h new file mode 100644 index 0000000..1a0b257 --- /dev/null +++ b/ptt_talk_src/mingw_tread/mingw.mutex.h @@ -0,0 +1,500 @@ +/** +* @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 + +#if !defined(__cplusplus) || (__cplusplus < 201103L) +#error A C++11 compiler is required! +#endif +// Recursion checks on non-recursive locks have some performance penalty, and +// the C++ standard does not mandate them. The user might want to explicitly +// enable or disable such checks. If the user has no preference, enable such +// checks in debug builds, but not in release builds. +#ifdef STDMUTEX_RECURSION_CHECKS +#elif defined(NDEBUG) +#define STDMUTEX_RECURSION_CHECKS 0 +#else +#define STDMUTEX_RECURSION_CHECKS 1 +#endif + +#include +#include +#include +#include //need for call_once() + +#if STDMUTEX_RECURSION_CHECKS || !defined(NDEBUG) +#include +#endif + +#include // Detect Windows version. + +#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) +#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\ + with Microsoft's API. We'll try to work around this, but we can make no\ + guarantees. This problem does not exist in MinGW-w64." +#include // No further granularity can be expected. +#else +#if STDMUTEX_RECURSION_CHECKS +#include // For GetCurrentThreadId +#endif +#include // For InitializeCriticalSection, etc. +#include // For GetLastError +#include +#endif + +// Need for the implementation of invoke +#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. +#endif + +namespace mingw_stdthread +{ +// The _NonRecursive class has mechanisms that do not play nice with direct +// manipulation of the native handle. This forward declaration is part of +// a friend class declaration. +#if STDMUTEX_RECURSION_CHECKS +namespace vista +{ +class condition_variable; +} +#endif +// To make this namespace equivalent to the thread-related subset of std, +// pull in the classes and class templates supplied by std but not by this +// implementation. +using std::lock_guard; +using std::unique_lock; +using std::adopt_lock_t; +using std::defer_lock_t; +using std::try_to_lock_t; +using std::adopt_lock; +using std::defer_lock; +using std::try_to_lock; + +class recursive_mutex +{ + CRITICAL_SECTION mHandle; +public: + typedef LPCRITICAL_SECTION native_handle_type; + native_handle_type native_handle() {return &mHandle;} + recursive_mutex() noexcept : mHandle() + { + 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); + } +}; + +#if STDMUTEX_RECURSION_CHECKS +struct _OwnerThread +{ +// If this is to be read before locking, then the owner-thread variable must +// be atomic to prevent a torn read from spuriously causing errors. + std::atomic mOwnerThread; + constexpr _OwnerThread () noexcept : mOwnerThread(0) {} + static void on_deadlock (void) + { + using namespace std; + fprintf(stderr, "FATAL: Recursive locking of non-recursive mutex\ + detected. Throwing system exception\n"); + fflush(stderr); + throw system_error(make_error_code(errc::resource_deadlock_would_occur)); + } + DWORD checkOwnerBeforeLock() const + { + DWORD self = GetCurrentThreadId(); + if (mOwnerThread.load(std::memory_order_relaxed) == self) + on_deadlock(); + return self; + } + void setOwnerAfterLock(DWORD id) + { + mOwnerThread.store(id, std::memory_order_relaxed); + } + void checkSetOwnerBeforeUnlock() + { + DWORD self = GetCurrentThreadId(); + if (mOwnerThread.load(std::memory_order_relaxed) != self) + on_deadlock(); + mOwnerThread.store(0, std::memory_order_relaxed); + } +}; +#endif + +// Though the Slim Reader-Writer (SRW) locks used here are not complete until +// Windows 7, implementing partial functionality in Vista will simplify the +// interaction with condition variables. + +//Define SRWLOCK_INIT. + +#if !defined(SRWLOCK_INIT) +#pragma message "SRWLOCK_INIT macro is not defined. Defining automatically." +#define SRWLOCK_INIT {0} +#endif + +#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA) +namespace windows7 +{ +class mutex +{ + SRWLOCK mHandle; +// Track locking thread for error checking. +#if STDMUTEX_RECURSION_CHECKS + friend class vista::condition_variable; + _OwnerThread mOwnerThread {}; +#endif +public: + typedef PSRWLOCK native_handle_type; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" + constexpr mutex () noexcept : mHandle(SRWLOCK_INIT) { } +#pragma GCC diagnostic pop + mutex (const mutex&) = delete; + mutex & operator= (const mutex&) = delete; + void lock (void) + { +// Note: Undefined behavior if called recursively. +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + AcquireSRWLockExclusive(&mHandle); +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.setOwnerAfterLock(self); +#endif + } + void unlock (void) + { +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.checkSetOwnerBeforeUnlock(); +#endif + ReleaseSRWLockExclusive(&mHandle); + } +// TryAcquireSRW functions are a Windows 7 feature. +#if (WINVER >= _WIN32_WINNT_WIN7) + bool try_lock (void) + { +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + BOOL ret = TryAcquireSRWLockExclusive(&mHandle); +#if STDMUTEX_RECURSION_CHECKS + if (ret) + mOwnerThread.setOwnerAfterLock(self); +#endif + return ret; + } +#endif + native_handle_type native_handle (void) + { + return &mHandle; + } +}; +} // Namespace windows7 +#endif // Compiling for Vista +namespace xp +{ +class mutex +{ + CRITICAL_SECTION mHandle; + std::atomic_uchar mState; +// Track locking thread for error checking. +#if STDMUTEX_RECURSION_CHECKS + friend class vista::condition_variable; + _OwnerThread mOwnerThread {}; +#endif +public: + typedef PCRITICAL_SECTION native_handle_type; + constexpr mutex () noexcept : mHandle(), mState(2) { } + mutex (const mutex&) = delete; + mutex & operator= (const mutex&) = delete; + ~mutex() noexcept + { +// Undefined behavior if the mutex is held (locked) by any thread. +// Undefined behavior if a thread terminates while holding ownership of the +// mutex. + DeleteCriticalSection(&mHandle); + } + void lock (void) + { + unsigned char state = mState.load(std::memory_order_acquire); + while (state) { + if ((state == 2) && mState.compare_exchange_weak(state, 1, std::memory_order_acquire)) + { + InitializeCriticalSection(&mHandle); + mState.store(0, std::memory_order_release); + break; + } + if (state == 1) + { + Sleep(0); + state = mState.load(std::memory_order_acquire); + } + } +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + EnterCriticalSection(&mHandle); +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.setOwnerAfterLock(self); +#endif + } + void unlock (void) + { +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.checkSetOwnerBeforeUnlock(); +#endif + LeaveCriticalSection(&mHandle); + } + bool try_lock (void) + { + unsigned char state = mState.load(std::memory_order_acquire); + if ((state == 2) && mState.compare_exchange_strong(state, 1, std::memory_order_acquire)) + { + InitializeCriticalSection(&mHandle); + mState.store(0, std::memory_order_release); + } + if (state == 1) + return false; +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + BOOL ret = TryEnterCriticalSection(&mHandle); +#if STDMUTEX_RECURSION_CHECKS + if (ret) + mOwnerThread.setOwnerAfterLock(self); +#endif + return ret; + } + native_handle_type native_handle (void) + { + return &mHandle; + } +}; +} // Namespace "xp" +#if (WINVER >= _WIN32_WINNT_WIN7) +using windows7::mutex; +#else +using xp::mutex; +#endif + +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 == kWaitAbandoned) + { + using namespace std; + fprintf(stderr, "FATAL: Thread terminated while holding a mutex."); + terminate(); + } +#endif + return (ret == kWaitObject0) || (ret == kWaitAbandoned); + } +protected: + HANDLE mHandle; +// Track locking thread for error checking of non-recursive timed_mutex. For +// standard compliance, this must be defined in same class and at the same +// access-control level as every other variable in the timed_mutex. +#if STDMUTEX_RECURSION_CHECKS + friend class vista::condition_variable; + _OwnerThread mOwnerThread {}; +#endif +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, 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 == kWaitAbandoned) + { + using namespace std; + fprintf(stderr, "FATAL: Thread terminated while holding a mutex."); + terminate(); + } +#endif + if ((ret != kWaitObject0) && (ret != kWaitAbandoned)) + { + throw std::system_error(GetLastError(), std::system_category()); + } + } + void unlock() + { + if (!ReleaseMutex(mHandle)) + throw std::system_error(GetLastError(), std::system_category()); + } + bool try_lock() + { + return try_lock_internal(0); + } + template + bool try_lock_for(const std::chrono::duration& dur) + { + using namespace std::chrono; + auto timeout = duration_cast(dur).count(); + while (timeout > 0) + { + constexpr auto kMaxStep = static_cast(kInfinite-1); + auto step = (timeout < kMaxStep) ? timeout : kMaxStep; + if (try_lock_internal(static_cast(step))) + return true; + timeout -= step; + } + return false; + } + template + bool try_lock_until(const std::chrono::time_point& timeout_time) + { + return try_lock_for(timeout_time - Clock::now()); + } +}; + +// Override if, and only if, it is necessary for error-checking. +#if STDMUTEX_RECURSION_CHECKS +class timed_mutex: recursive_timed_mutex +{ +public: + timed_mutex() = default; + timed_mutex(const timed_mutex&) = delete; + timed_mutex& operator=(const timed_mutex&) = delete; + void lock() + { + DWORD self = mOwnerThread.checkOwnerBeforeLock(); + recursive_timed_mutex::lock(); + mOwnerThread.setOwnerAfterLock(self); + } + void unlock() + { + mOwnerThread.checkSetOwnerBeforeUnlock(); + recursive_timed_mutex::unlock(); + } + template + bool try_lock_for(const std::chrono::duration& dur) + { + DWORD self = mOwnerThread.checkOwnerBeforeLock(); + bool ret = recursive_timed_mutex::try_lock_for(dur); + if (ret) + mOwnerThread.setOwnerAfterLock(self); + return ret; + } + template + bool try_lock_until(const std::chrono::time_point& timeout_time) + { + return try_lock_for(timeout_time - Clock::now()); + } + bool try_lock () + { + return try_lock_for(std::chrono::milliseconds(0)); + } +}; +#else +typedef recursive_timed_mutex timed_mutex; +#endif + +class once_flag +{ +// When available, the SRW-based mutexes should be faster than the +// CriticalSection-based mutexes. Only try_lock will be unavailable in Vista, +// and try_lock is not used by once_flag. +#if (_WIN32_WINNT == _WIN32_WINNT_VISTA) + windows7::mutex mMutex; +#else + mutex mMutex; +#endif + std::atomic_bool mHasRun; + once_flag(const once_flag&) = delete; + once_flag& operator=(const once_flag&) = delete; + template + friend void call_once(once_flag& once, Callable&& f, Args&&... args); +public: + constexpr once_flag() noexcept: mMutex(), mHasRun(false) {} +}; + +template +void call_once(once_flag& flag, Callable&& func, Args&&... args) +{ + if (flag.mHasRun.load(std::memory_order_acquire)) + return; + lock_guard lock(flag.mMutex); + if (flag.mHasRun.load(std::memory_order_relaxed)) + return; + detail::invoke(std::forward(func),std::forward(args)...); + flag.mHasRun.store(true, std::memory_order_release); +} +} // Namespace mingw_stdthread + +// Push objects into std, but only if they are not already there. +namespace std +{ +// Because of quirks of the compiler, the common "using namespace std;" +// directive would flatten the namespaces and introduce ambiguity where there +// was none. Direct specification (std::), however, would be unaffected. +// Take the safe option, and include only in the presence of MinGW's win32 +// implementation. +#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) +using mingw_stdthread::recursive_mutex; +using mingw_stdthread::mutex; +using mingw_stdthread::recursive_timed_mutex; +using mingw_stdthread::timed_mutex; +using mingw_stdthread::once_flag; +using mingw_stdthread::call_once; +#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#pragma message "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. These classes, found in namespace std, are not overridden\ + by the mingw-std-thread library. If you would still like to use this\ + implementation (as it is more lightweight), use the classes provided in\ + namespace mingw_stdthread." +#endif +} +#endif // WIN32STDMUTEX_H diff --git a/ptt_talk_src/mingw_tread/mingw.shared_mutex.h b/ptt_talk_src/mingw_tread/mingw.shared_mutex.h new file mode 100644 index 0000000..ff1ac65 --- /dev/null +++ b/ptt_talk_src/mingw_tread/mingw.shared_mutex.h @@ -0,0 +1,503 @@ +/// \file mingw.shared_mutex.h +/// \brief Standard-compliant shared_mutex for MinGW +/// +/// (c) 2017 by Nathaniel J. McClatchey, Athens OH, United States +/// \author Nathaniel J. McClatchey +/// +/// \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. +/// \note Target Windows version is determined by WINVER, which is determined in +/// from _WIN32_WINNT, which can itself be set by the user. + +// Notes on the namespaces: +// - The implementation can be accessed directly in the namespace +// mingw_stdthread. +// - Objects will be brought into namespace std by a using directive. This +// will cause objects declared in std (such as MinGW's implementation) to +// hide this implementation's definitions. +// - To avoid poluting the namespace with implementation details, all objects +// to be pushed into std will be placed in mingw_stdthread::visible. +// The end result is that if MinGW supplies an object, it is automatically +// used. If MinGW does not supply an object, this implementation's version will +// instead be used. + +#ifndef MINGW_SHARED_MUTEX_H_ +#define MINGW_SHARED_MUTEX_H_ + +#if !defined(__cplusplus) || (__cplusplus < 201103L) +#error A C++11 compiler is required! +#endif + +#include +// For descriptive errors. +#include +// Implementing a shared_mutex without OS support will require atomic read- +// modify-write capacity. +#include +// For timing in shared_lock and shared_timed_mutex. +#include +#include + +// Use MinGW's shared_lock class template, if it's available. Requires C++14. +// If unavailable (eg. because this library is being used in C++11), then an +// implementation of shared_lock is provided by this header. +#if (__cplusplus >= 201402L) +#include +#endif + +// For defer_lock_t, adopt_lock_t, and try_to_lock_t +#include "mingw.mutex.h" +// For this_thread::yield. +//#include "mingw.thread.h" + +// Might be able to use native Slim Reader-Writer (SRW) locks. +#ifdef _WIN32 +#include // Detect Windows version. +#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) +#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\ + with Microsoft's API. We'll try to work around this, but we can make no\ + guarantees. This problem does not exist in MinGW-w64." +#include // No further granularity can be expected. +#else +#include +#endif +#endif + +namespace mingw_stdthread +{ +// Define a portable atomics-based shared_mutex +namespace portable +{ +class shared_mutex +{ + typedef uint_fast16_t counter_type; + std::atomic mCounter {0}; + static constexpr counter_type kWriteBit = 1 << (std::numeric_limits::digits - 1); + +#if STDMUTEX_RECURSION_CHECKS +// Runtime checker for verifying owner threads. Note: Exclusive mode only. + _OwnerThread mOwnerThread {}; +#endif +public: + typedef shared_mutex * native_handle_type; + + shared_mutex () = default; + +// No form of copying or moving should be allowed. + shared_mutex (const shared_mutex&) = delete; + shared_mutex & operator= (const shared_mutex&) = delete; + + ~shared_mutex () + { +// Terminate if someone tries to destroy an owned mutex. + assert(mCounter.load(std::memory_order_relaxed) == 0); + } + + void lock_shared (void) + { + counter_type expected = mCounter.load(std::memory_order_relaxed); + do + { +// Delay if writing or if too many readers are attempting to read. + if (expected >= kWriteBit - 1) + { + using namespace std; + expected = mCounter.load(std::memory_order_relaxed); + continue; + } + if (mCounter.compare_exchange_weak(expected, + static_cast(expected + 1), + std::memory_order_acquire, + std::memory_order_relaxed)) + break; + } + while (true); + } + + bool try_lock_shared (void) + { + counter_type expected = mCounter.load(std::memory_order_relaxed) & static_cast(~kWriteBit); + if (expected + 1 == kWriteBit) + return false; + else + return mCounter.compare_exchange_strong( expected, + static_cast(expected + 1), + std::memory_order_acquire, + std::memory_order_relaxed); + } + + void unlock_shared (void) + { + using namespace std; +#ifndef NDEBUG + if (!(mCounter.fetch_sub(1, memory_order_release) & static_cast(~kWriteBit))) + throw system_error(make_error_code(errc::operation_not_permitted)); +#else + mCounter.fetch_sub(1, memory_order_release); +#endif + } + +// Behavior is undefined if a lock was previously acquired. + void lock (void) + { +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + using namespace std; +// 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(); +// Wait for readers to finish up. + while (current != kWriteBit) + { + //this_thread::yield(); + current = mCounter.load(std::memory_order_acquire); + } +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.setOwnerAfterLock(self); +#endif + } + + bool try_lock (void) + { +#if STDMUTEX_RECURSION_CHECKS + DWORD self = mOwnerThread.checkOwnerBeforeLock(); +#endif + counter_type expected = 0; + bool ret = mCounter.compare_exchange_strong(expected, kWriteBit, + std::memory_order_acquire, + std::memory_order_relaxed); +#if STDMUTEX_RECURSION_CHECKS + if (ret) + mOwnerThread.setOwnerAfterLock(self); +#endif + return ret; + } + + void unlock (void) + { +#if STDMUTEX_RECURSION_CHECKS + mOwnerThread.checkSetOwnerBeforeUnlock(); +#endif + using namespace std; +#ifndef NDEBUG + if (mCounter.load(memory_order_relaxed) != kWriteBit) + throw system_error(make_error_code(errc::operation_not_permitted)); +#endif + mCounter.store(0, memory_order_release); + } + + native_handle_type native_handle (void) + { + return this; + } +}; + +} // Namespace portable + +// The native shared_mutex implementation primarily uses features of Windows +// Vista, but the features used for try_lock and try_lock_shared were not +// introduced until Windows 7. To allow limited use while compiling for Vista, +// I define the class without try_* functions in that case. +// Only fully-featured implementations will be placed into namespace std. +#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA) +namespace vista +{ +class condition_variable_any; +} + +namespace windows7 +{ +// We already #include "mingw.mutex.h". May as well reduce redundancy. +class shared_mutex : windows7::mutex +{ +// Allow condition_variable_any (and only condition_variable_any) to treat a +// shared_mutex as its base class. + friend class vista::condition_variable_any; +public: + using windows7::mutex::native_handle_type; + using windows7::mutex::lock; + using windows7::mutex::unlock; + using windows7::mutex::native_handle; + + void lock_shared (void) + { + AcquireSRWLockShared(native_handle()); + } + + void unlock_shared (void) + { + ReleaseSRWLockShared(native_handle()); + } + +// TryAcquireSRW functions are a Windows 7 feature. +#if (WINVER >= _WIN32_WINNT_WIN7) + bool try_lock_shared (void) + { + return TryAcquireSRWLockShared(native_handle()) != 0; + } + + using windows7::mutex::try_lock; +#endif +}; + +} // Namespace windows7 +#endif // Compiling for Vista +#if (defined(_WIN32) && (WINVER >= _WIN32_WINNT_WIN7)) +using windows7::shared_mutex; +#else +using portable::shared_mutex; +#endif + +class shared_timed_mutex : shared_mutex +{ + typedef shared_mutex Base; +public: + using Base::lock; + using Base::try_lock; + using Base::unlock; + using Base::lock_shared; + using Base::try_lock_shared; + using Base::unlock_shared; + + template< class Clock, class Duration > + bool try_lock_until ( const std::chrono::time_point& cutoff ) + { + do + { + if (try_lock()) + return true; + } + while (std::chrono::steady_clock::now() < cutoff); + return false; + } + + template< class Rep, class Period > + bool try_lock_for (const std::chrono::duration& rel_time) + { + return try_lock_until(std::chrono::steady_clock::now() + rel_time); + } + + template< class Clock, class Duration > + bool try_lock_shared_until ( const std::chrono::time_point& cutoff ) + { + do + { + if (try_lock_shared()) + return true; + } + while (std::chrono::steady_clock::now() < cutoff); + return false; + } + + template< class Rep, class Period > + bool try_lock_shared_for (const std::chrono::duration& rel_time) + { + return try_lock_shared_until(std::chrono::steady_clock::now() + rel_time); + } +}; + +#if __cplusplus >= 201402L +using std::shared_lock; +#else +// If not supplied by shared_mutex (eg. because C++14 is not supported), I +// supply the various helper classes that the header should have defined. +template +class shared_lock +{ + Mutex * mMutex; + bool mOwns; +// Reduce code redundancy + void verify_lockable (void) + { + using namespace std; + if (mMutex == nullptr) + throw system_error(make_error_code(errc::operation_not_permitted)); + if (mOwns) + throw system_error(make_error_code(errc::resource_deadlock_would_occur)); + } +public: + typedef Mutex mutex_type; + + shared_lock (void) noexcept + : mMutex(nullptr), mOwns(false) + { + } + + shared_lock (shared_lock && other) noexcept + : mMutex(other.mutex_), mOwns(other.owns_) + { + other.mMutex = nullptr; + other.mOwns = false; + } + + explicit shared_lock (mutex_type & m) + : mMutex(&m), mOwns(true) + { + mMutex->lock_shared(); + } + + shared_lock (mutex_type & m, defer_lock_t) noexcept + : mMutex(&m), mOwns(false) + { + } + + shared_lock (mutex_type & m, adopt_lock_t) + : mMutex(&m), mOwns(true) + { + } + + shared_lock (mutex_type & m, try_to_lock_t) + : mMutex(&m), mOwns(m.try_lock_shared()) + { + } + + template< class Rep, class Period > + shared_lock( mutex_type& m, const std::chrono::duration& timeout_duration ) + : mMutex(&m), mOwns(m.try_lock_shared_for(timeout_duration)) + { + } + + template< class Clock, class Duration > + shared_lock( mutex_type& m, const std::chrono::time_point& timeout_time ) + : mMutex(&m), mOwns(m.try_lock_shared_until(timeout_time)) + { + } + + shared_lock& operator= (shared_lock && other) noexcept + { + if (&other != this) + { + if (mOwns) + mMutex->unlock_shared(); + mMutex = other.mMutex; + mOwns = other.mOwns; + other.mMutex = nullptr; + other.mOwns = false; + } + return *this; + } + + + ~shared_lock (void) + { + if (mOwns) + mMutex->unlock_shared(); + } + + shared_lock (const shared_lock &) = delete; + shared_lock& operator= (const shared_lock &) = delete; + +// Shared locking + void lock (void) + { + verify_lockable(); + mMutex->lock_shared(); + mOwns = true; + } + + bool try_lock (void) + { + verify_lockable(); + mOwns = mMutex->try_lock_shared(); + return mOwns; + } + + template< class Clock, class Duration > + bool try_lock_until( const std::chrono::time_point& cutoff ) + { + verify_lockable(); + do + { + mOwns = mMutex->try_lock_shared(); + if (mOwns) + return mOwns; + } + while (std::chrono::steady_clock::now() < cutoff); + return false; + } + + template< class Rep, class Period > + bool try_lock_for (const std::chrono::duration& rel_time) + { + return try_lock_until(std::chrono::steady_clock::now() + rel_time); + } + + void unlock (void) + { + using namespace std; + if (!mOwns) + throw system_error(make_error_code(errc::operation_not_permitted)); + mMutex->unlock_shared(); + mOwns = false; + } + +// Modifiers + void swap (shared_lock & other) noexcept + { + using namespace std; + swap(mMutex, other.mMutex); + swap(mOwns, other.mOwns); + } + + mutex_type * release (void) noexcept + { + mutex_type * ptr = mMutex; + mMutex = nullptr; + mOwns = false; + return ptr; + } +// Observers + mutex_type * mutex (void) const noexcept + { + return mMutex; + } + + bool owns_lock (void) const noexcept + { + return mOwns; + } + + explicit operator bool () const noexcept + { + return owns_lock(); + } +}; + +template< class Mutex > +void swap( shared_lock& lhs, shared_lock& rhs ) noexcept +{ + lhs.swap(rhs); +} +#endif // C++11 +} // Namespace mingw_stdthread + +namespace std +{ +// Because of quirks of the compiler, the common "using namespace std;" +// directive would flatten the namespaces and introduce ambiguity where there +// was none. Direct specification (std::), however, would be unaffected. +// Take the safe option, and include only in the presence of MinGW's win32 +// implementation. +#if (__cplusplus < 201703L) || (defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)) +using mingw_stdthread::shared_mutex; +#endif +#if (__cplusplus < 201402L) || (defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS)) +using mingw_stdthread::shared_timed_mutex; +using mingw_stdthread::shared_lock; +#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#pragma message "This version of MinGW seems to include a win32 port of\ + pthreads, and probably already has C++ std threading classes implemented,\ + based on pthreads. These classes, found in namespace std, are not overridden\ + by the mingw-std-thread library. If you would still like to use this\ + implementation (as it is more lightweight), use the classes provided in\ + namespace mingw_stdthread." +#endif +} // Namespace std +#endif // MINGW_SHARED_MUTEX_H_ diff --git a/ptt_talk_src/mingw_tread/mingw.thread.h b/ptt_talk_src/mingw_tread/mingw.thread.h new file mode 100644 index 0000000..6d61a00 --- /dev/null +++ b/ptt_talk_src/mingw_tread/mingw.thread.h @@ -0,0 +1,360 @@ +/** +* @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 + +#if !defined(__cplusplus) || (__cplusplus < 201103L) +#error A C++11 compiler is required! +#endif + +// Use the standard classes for std::, if available. +#include + +#include // For std::size_t +#include // Detect error type. +#include // For std::terminate +#include // For std::system_error +#include // For std::hash +#include // For std::tuple +#include // For sleep timing. +#include // For std::unique_ptr +#include // Stream output for thread ids. +#include // For std::swap, std::forward + +#include "mingw.invoke.h" + +#if (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) +#pragma message "The Windows API that MinGW-w32 provides is not fully compatible\ + with Microsoft's API. We'll try to work around this, but we can make no\ + guarantees. This problem does not exist in MinGW-w64." +#include // No further granularity can be expected. +#else +#include // For WaitForSingleObject +#include // For CloseHandle, etc. +#include // For GetNativeSystemInfo +#include // For GetCurrentThreadId +#endif +#include // For _beginthreadex + +#ifndef NDEBUG +#include +#endif + +#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. +#endif + +// Instead of INVALID_HANDLE_VALUE, _beginthreadex returns 0. +namespace mingw_stdthread +{ +namespace detail +{ + template + struct IntSeq {}; + + template + struct GenIntSeq : GenIntSeq { }; + + template + struct GenIntSeq<0, S...> { typedef IntSeq type; }; + +// Use a template specialization to avoid relying on compiler optimization +// when determining the parameter integer sequence. + template + class ThreadFuncCall; +// We can't define the Call struct in the function - the standard forbids template methods in that case + template + class ThreadFuncCall, Args...> + { + static_assert(sizeof...(S) == sizeof...(Args), "Args must match."); + using Tuple = std::tuple::type...>; + typename std::decay::type mFunc; + Tuple mArgs; + + public: + ThreadFuncCall(Func&& aFunc, Args&&... aArgs) + : mFunc(std::forward(aFunc)), + mArgs(std::forward(aArgs)...) + { + } + + void callFunc() + { + detail::invoke(std::move(mFunc), std::move(std::get(mArgs)) ...); + } + }; + +// Allow construction of threads without exposing implementation. + class ThreadIdTool; +} // Namespace "detail" + +class thread +{ +public: + class id + { + DWORD mId = 0; + friend class thread; + friend class std::hash; + friend class detail::ThreadIdTool; + explicit id(DWORD aId) noexcept : mId(aId){} + public: + id (void) noexcept = default; + friend bool operator==(id x, id y) noexcept {return x.mId == y.mId; } + friend bool operator!=(id x, id y) noexcept {return x.mId != y.mId; } + friend bool operator< (id x, id y) noexcept {return x.mId < y.mId; } + friend bool operator<=(id x, id y) noexcept {return x.mId <= y.mId; } + friend bool operator> (id x, id y) noexcept {return x.mId > y.mId; } + friend bool operator>=(id x, id y) noexcept {return x.mId >= y.mId; } + + template + friend std::basic_ostream<_CharT, _Traits>& + operator<<(std::basic_ostream<_CharT, _Traits>& __out, id __id) + { + if (__id.mId == 0) + { + return __out << "(invalid std::thread::id)"; + } + else + { + return __out << __id.mId; + } + } + }; +private: + static constexpr HANDLE kInvalidHandle = nullptr; + static constexpr DWORD kInfinite = 0xffffffffl; + HANDLE mHandle; + id mThreadId; + + template + static unsigned __stdcall threadfunc(void* arg) + { + std::unique_ptr call(static_cast(arg)); + call->callFunc(); + return 0; + } + + static unsigned int _hardware_concurrency_helper() noexcept + { + SYSTEM_INFO sysinfo; +// This is one of the few functions used by the library which has a nearly- +// equivalent function defined in earlier versions of Windows. Include the +// workaround, just as a reminder that it does exist. +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) + ::GetNativeSystemInfo(&sysinfo); +#else + ::GetSystemInfo(&sysinfo); +#endif + return sysinfo.dwNumberOfProcessors; + } +public: + typedef HANDLE native_handle_type; + id get_id() const noexcept {return mThreadId;} + native_handle_type native_handle() const {return mHandle;} + thread(): mHandle(kInvalidHandle), mThreadId(){} + + thread(thread&& other) + :mHandle(other.mHandle), mThreadId(other.mThreadId) + { + other.mHandle = kInvalidHandle; + other.mThreadId = id{}; + } + + thread(const thread &other)=delete; + + template + explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId() + { + using ArgSequence = typename detail::GenIntSeq::type; + using Call = detail::ThreadFuncCall; + auto call = new Call( + std::forward(func), std::forward(args)...); + unsigned id_receiver; + auto int_handle = _beginthreadex(NULL, 0, threadfunc, + static_cast(call), 0, &id_receiver); + if (int_handle == 0) + { + mHandle = kInvalidHandle; + int errnum = errno; + delete call; +// Note: Should only throw EINVAL, EAGAIN, EACCES + throw std::system_error(errnum, std::generic_category()); + } else { + mThreadId.mId = id_receiver; + mHandle = reinterpret_cast(int_handle); + } + } + + bool joinable() const {return mHandle != kInvalidHandle;} + +// Note: Due to lack of synchronization, this function has a race condition +// if called concurrently, which leads to undefined behavior. The same applies +// to all other member functions of this class, but this one is mentioned +// explicitly. + void join() + { + using namespace std; + if (get_id() == id(GetCurrentThreadId())) + throw system_error(make_error_code(errc::resource_deadlock_would_occur)); + if (mHandle == kInvalidHandle) + throw system_error(make_error_code(errc::no_such_process)); + if (!joinable()) + throw system_error(make_error_code(errc::invalid_argument)); + WaitForSingleObject(mHandle, kInfinite); + CloseHandle(mHandle); + mHandle = kInvalidHandle; + mThreadId = id{}; + } + + ~thread() + { + if (joinable()) + { +#ifndef NDEBUG + std::printf("Error: Must join() or detach() a thread before \ +destroying it.\n"); +#endif + std::terminate(); + } + } + thread& operator=(const thread&) = delete; + thread& operator=(thread&& other) noexcept + { + if (joinable()) + { +#ifndef NDEBUG + std::printf("Error: Must join() or detach() a thread before \ +moving another thread to it.\n"); +#endif + std::terminate(); + } + swap(std::forward(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 unsigned int cached = _hardware_concurrency_helper(); + return cached; + } + + void detach() + { + if (!joinable()) + { + using namespace std; + throw system_error(make_error_code(errc::invalid_argument)); + } + if (mHandle != kInvalidHandle) + { + CloseHandle(mHandle); + mHandle = kInvalidHandle; + } + mThreadId = id{}; + } +}; + +namespace detail +{ + class ThreadIdTool + { + public: + static thread::id make_id (DWORD base_id) noexcept + { + return thread::id(base_id); + } + }; +} // Namespace "detail" + +namespace this_thread +{ + inline thread::id get_id() noexcept + { + return detail::ThreadIdTool::make_id(GetCurrentThreadId()); + } + inline void yield() noexcept {Sleep(0);} + template< class Rep, class Period > + void sleep_for( const std::chrono::duration& sleep_duration) + { + static constexpr DWORD kInfinite = 0xffffffffl; + using namespace std::chrono; + using rep = milliseconds::rep; + rep ms = duration_cast(sleep_duration).count(); + while (ms > 0) + { + constexpr rep kMaxRep = static_cast(kInfinite - 1); + auto sleepTime = (ms < kMaxRep) ? ms : kMaxRep; + Sleep(static_cast(sleepTime)); + ms -= sleepTime; + } + } + template + void sleep_until(const std::chrono::time_point& sleep_time) + { + sleep_for(sleep_time-Clock::now()); + } +} +} // Namespace mingw_stdthread + +namespace std +{ +// Because of quirks of the compiler, the common "using namespace std;" +// directive would flatten the namespaces and introduce ambiguity where there +// was none. Direct specification (std::), however, would be unaffected. +// Take the safe option, and include only in the presence of MinGW's win32 +// implementation. +#if defined(__MINGW32__ ) && !defined(_GLIBCXX_HAS_GTHREADS) +using mingw_stdthread::thread; +// Remove ambiguity immediately, to avoid problems arising from the above. +//using std::thread; +namespace this_thread +{ +using namespace mingw_stdthread::this_thread; +} +#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#pragma message "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. These classes, found in namespace std, are not overridden\ + by the mingw-std-thread library. If you would still like to use this\ + implementation (as it is more lightweight), use the classes provided in\ + namespace mingw_stdthread." +#endif + +// Specialize hash for this implementation's thread::id, even if the +// std::thread::id already has a hash. +template<> +struct hash +{ + typedef mingw_stdthread::thread::id argument_type; + typedef size_t result_type; + size_t operator() (const argument_type & i) const noexcept + { + return i.mId; + } +}; +} +#endif // WIN32STDTHREAD_H diff --git a/ptt_talk_src/ptt_talk.cpp b/ptt_talk_src/ptt_talk.cpp index 22dcd59..5fd96a2 100644 --- a/ptt_talk_src/ptt_talk.cpp +++ b/ptt_talk_src/ptt_talk.cpp @@ -42,8 +42,9 @@ #ifdef _WIN32 - #include #include + #include + #include "mingw.thread.h" #else #include #include @@ -104,6 +105,50 @@ int main(int argc, char **argv) { printf("ptt_talk.cpp\n"); fflush(stdout); + float last_time = 0.0; + float sleep_time = 0.3; + + #ifdef _WIN32 + SHORT keyState = GetKeyState(VK_CAPITAL/*(caps lock)*/); + bool isToggled = keyState & 1; + bool isDown = keyState & 0x8000; + initialize(&inputParameters, stream, err, &data, &numSamples, &numBytes, &totalFrames, &should_stop); + while (1) { + Sleep(sleep_time); + + keyState = GetAsyncKeyState(VK_MENU) & 0x8000; + + /* Record some audio. -------------------------------------------- */ + if( keyState && capture_key) { //--- tab key + printf ("Alt pressed!\n"); + if( is_recording == false ) { //isn't recording + if(last_time >= 0.5) { + last_time = 0.0; + play_sine(); + printf(">>> starting\n"); + is_recording = true; //so start the record + should_stop = false; + initialize(&inputParameters, stream, err, &data, &numSamples, &numBytes, &totalFrames, &should_stop); + //recordAudio(inputParameters, stream, err, data, numSamples, &should_stop, &is_recording); + std::thread thread(recordAudio, inputParameters, stream, err, data, numSamples, &should_stop, &is_recording); + thread.detach(); + } + } else { + if (should_stop == false && is_recording == true) { + if(last_time >= 0.5) { + last_time = 0.0; + printf(">>> stopping\n"); + should_stop = true; + } + } + } + } + + } + + #else + + //linux /* ------------------- set key event ------------------- */ Display* display = XOpenDisplay(NULL); if(display == NULL) { @@ -122,7 +167,7 @@ int main(int argc, char **argv) { XComposeStatus comp; int len; int revert; - + unsigned int keycode = XKeysymToKeycode(display, XK_Tab); XGetInputFocus (display, &curFocus, &revert); XSelectInput(display, window, KeyPressMask|KeyReleaseMask|FocusChangeMask); @@ -132,12 +177,11 @@ int main(int argc, char **argv) { XEvent ev; /* ------------------- end key event ------------------- */ - float last_time = 0.0; - float sleep_time = 0.3; initialize(&inputParameters, stream, err, &data, &numSamples, &numBytes, &totalFrames, &should_stop); while (1) { sleep(sleep_time); + last_time += sleep_time; if(last_time > 0.5) last_time = 0.5; XNextEvent(display, &ev); @@ -215,6 +259,7 @@ int main(int argc, char **argv) { } //end switch } //end while + #endif } int sendMessage(char* file_path) { @@ -238,12 +283,11 @@ int sendMessage(char* file_path) { } //printf("socket openned"); - bzero((char *) &serv_addr, sizeof(serv_addr)); + memset((char *) &serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; - bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); - serv_addr.sin_port = htons(portno); + memmove((char *)&serv_addr.sin_addr.s_addr, (char *)server->h_addr, server->h_length); //printf("ok here\n"); int con_result = connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)); @@ -254,8 +298,7 @@ int sendMessage(char* file_path) { //printf("ok here too\n"); - - bzero(buffer,256); + memset((char*) buffer, 0, sizeof(buffer)); strncpy(buffer, nick, sizeof(buffer)); strncat(buffer, (const char*)"\n", sizeof(buffer - 1)); //n = send(sockfd , buffer , strlen(nick) , 0 ); @@ -263,7 +306,7 @@ int sendMessage(char* file_path) { //printf ("return write %d\n", (int)n); if (n < 0) printf("ERROR writing to socket"); - bzero(buffer,256); + memset((char*) buffer, 0, sizeof(buffer)); n = read(sockfd,buffer,255); if (n < 0) printf("ERROR reading from socket"); @@ -271,7 +314,6 @@ int sendMessage(char* file_path) { std::string str_buffer = (std::string) buffer; if(str_buffer == (std::string) "file\n") { //printf("Lets's send file\n"); - //TODO mandar arquivo int64_t rc = SendFile(sockfd, "recorded.ogg"); if (rc < 0) { printf("Failed to send file: %ld\n", rc); diff --git a/ptt_talk_src/ptttalk b/ptt_talk_src/ptttalk index 160ca7b..fcd1098 100755 Binary files a/ptt_talk_src/ptttalk and b/ptt_talk_src/ptttalk differ diff --git a/ptt_talk_src/win_version/libportaudio.dll b/ptt_talk_src/win_version/libportaudio.dll new file mode 100644 index 0000000..c77cabb Binary files /dev/null and b/ptt_talk_src/win_version/libportaudio.dll differ diff --git a/ptt_talk_src/win_version/ptttalk.exe b/ptt_talk_src/win_version/ptttalk.exe new file mode 100755 index 0000000..cf54c85 Binary files /dev/null and b/ptt_talk_src/win_version/ptttalk.exe differ diff --git a/ptt_talk_src/win_version/sndfile.dll b/ptt_talk_src/win_version/sndfile.dll new file mode 100755 index 0000000..333468d Binary files /dev/null and b/ptt_talk_src/win_version/sndfile.dll differ