Merge branch 'master' (early part) into medusa
jump to
@@ -42,6 +42,9 @@ - Qt: More app metadata fixes
- Qt: Fix load recent from archive (fixes mgba.io/i/1325) - LR35902: Fix disassembly of several CB-prefix instructions - Qt: Fix overrides getting discarded (fixes mgba.io/i/1354) + - Qt: Fix saved scale not getting set on resize (fixes mgba.io/i/1074) + - CMake: Fix .deb imagemagick dependencies + - Qt: Fix crash in sprite viewer magnification (fixes mgba.io/i/1362) Misc: - GBA Savedata: EEPROM performance fixes - GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash
@@ -231,6 +231,8 @@ # Platform support
if(WIN32) set(WIN32_VERSION "${LIB_VERSION_MAJOR},${LIB_VERSION_MINOR},${LIB_VERSION_PATCH}") add_definitions(-D_WIN32_WINNT=0x0600) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -municode") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode") list(APPEND OS_LIB ws2_32 shlwapi) list(APPEND CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-fd.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/windows/vfs-w32.c) file(GLOB OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/windows/*.c)@@ -579,11 +581,11 @@ list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/imagemagick/imagemagick-gif-encoder.c")
list(APPEND DEPENDENCY_LIB ${MAGICKWAND_LIBRARIES}) string(REGEX MATCH "^[0-9]+\\.[0-9]+" MAGICKWAND_VERSION_PARTIAL ${MagickWand_VERSION}) string(REGEX MATCH "^[0-9]+" MAGICKWAND_VERSION_MAJOR ${MagickWand_VERSION}) - if(${MAGICKWAND_VERSION_PARTIAL} EQUAL "6.7") + if(${MAGICKWAND_VERSION_PARTIAL} STREQUAL "6.7") set(MAGICKWAND_DEB_VERSION "5") - elseif(${MagickWand_VERSION} EQUAL "6.9.10") + elseif(${MagickWand_VERSION} STREQUAL "6.9.10") set(MAGICKWAND_DEB_VERSION "-6.q16-6") - elseif(${MagickWand_VERSION} EQUAL "6.9.7") + elseif(${MagickWand_VERSION} STREQUAL "6.9.7") set(MAGICKWAND_DEB_VERSION "-6.q16-3") else() set(MAGICKWAND_DEB_VERSION "-6.q16-2")@@ -725,6 +727,7 @@ if(USE_EPOXY)
add_definitions(-DBUILD_GL -DBUILD_GLES2) list(APPEND FEATURES EPOXY) include_directories(AFTER ${EPOXY_INCLUDE_DIRS}) + link_directories(${EPOXY_LIBRARY_DIRS}) set(OPENGLES2_LIBRARY ${EPOXY_LIBRARIES}) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libepoxy0") endif()@@ -732,6 +735,7 @@
if(USE_SQLITE3) list(APPEND FEATURES SQLITE3) include_directories(AFTER ${SQLITE3_INCLUDE_DIRS}) + link_directories(${SQLITE3_LIBRARY_DIRS}) list(APPEND DEPENDENCY_LIB ${SQLITE3_LIBRARIES}) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libsqlite3-0") list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/sqlite3/no-intro.c")@@ -740,6 +744,7 @@
if(USE_ELF) list(APPEND FEATURES ELF) include_directories(AFTER ${LIBELF_INCLUDE_DIRS}) + link_directories(${LIBELF_LIBRARY_DIRS}) list(APPEND DEPENDENCY_LIB ${LIBELF_LIBRARIES}) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libelf1") endif()@@ -1068,7 +1073,15 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/platform/windows/setup/setup.iss.in" ${CMAKE_CURRENT_BINARY_DIR}/setup.iss)
endif() # Packaging -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/res/licenses DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT ${BINARY_NAME}) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/licenses/blip_buf.txt DESTINATION ${CMAKE_INSTALL_DOCDIR}/licenses COMPONENT ${BINARY_NAME}) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/licenses/inih.txt DESTINATION ${CMAKE_INSTALL_DOCDIR}/licenses COMPONENT ${BINARY_NAME}) +if(USE_DISCORD_RPC) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/licenses/discord-rpc.txt DESTINATION ${CMAKE_INSTALL_DOCDIR}/licenses COMPONENT ${BINARY_NAME}) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/licenses/rapidjson.txt DESTINATION ${CMAKE_INSTALL_DOCDIR}/licenses COMPONENT ${BINARY_NAME}) + if(WIN32) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/licenses/mingw-std-threads.txt DESTINATION ${CMAKE_INSTALL_DOCDIR}/licenses COMPONENT ${BINARY_NAME}) + endif() +endif() if(EXTRA_LICENSES) install(FILES ${EXTRA_LICENSES} DESTINATION ${CMAKE_INSTALL_DOCDIR}/licenses COMPONENT ${BINARY_NAME}) endif()@@ -1277,6 +1290,7 @@ message(STATUS " ZIP support: ${SUMMARY_ZIP}")
message(STATUS " 7-Zip support: ${USE_LZMA}") message(STATUS " SQLite3 game database: ${USE_SQLITE3}") message(STATUS " ELF loading support: ${USE_ELF}") + message(STATUS " Discord Rich Presence support: ${USE_DISCORD_RPC}") message(STATUS " OpenGL support: ${SUMMARY_GL}") message(STATUS "Frontends:") message(STATUS " Qt: ${BUILD_QT}")
@@ -34,12 +34,16 @@ #ifdef _WIN32
// WinSock2 gets very angry if it's included too late #include <winsock2.h> #endif + +#if defined(_MSC_VER) || defined(__cplusplus) +#define restrict __restrict +#endif + #ifdef _MSC_VER #include <Windows.h> #include <sys/types.h> typedef intptr_t ssize_t; #define PATH_MAX MAX_PATH -#define restrict __restrict #define strcasecmp _stricmp #define strncasecmp _strnicmp #define ftruncate _chsize
@@ -0,0 +1,24 @@
+Copyright (c) 2016, Mega Limited +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +
@@ -0,0 +1,95 @@
+/* Copyright (c) 2013-2019 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "DiscordCoordinator.h" + +#include "CoreController.h" +#include "GBAApp.h" + +#ifdef USE_SQLITE3 +#include "feature/sqlite3/no-intro.h" +#endif + +#include "discord_rpc.h" + +namespace QGBA { + +namespace DiscordCoordinator { + +static bool s_gameRunning = false; +static bool s_inited = false; +static QString s_title; + +static void updatePresence() { + if (!s_inited) { + return; + } + if (s_gameRunning) { + DiscordRichPresence discordPresence{}; + discordPresence.details = s_title.toUtf8().constData(); + discordPresence.instance = 1; + Discord_UpdatePresence(&discordPresence); + } else { + Discord_ClearPresence(); + } +} + +void init() { + if (s_inited) { + return; + } + DiscordEventHandlers handlers{}; + Discord_Initialize("554440738952183828", &handlers, 1, nullptr); + s_inited = true; + updatePresence(); +} + +void deinit() { + if (!s_inited) { + return; + } + Discord_ClearPresence(); + Discord_Shutdown(); + s_inited = false; + s_gameRunning = false; +} + +void gameStarted(std::shared_ptr<CoreController> controller) { + if (s_gameRunning) { + return; + } + s_gameRunning = true; + + CoreController::Interrupter interrupter(controller); + const NoIntroDB* db = GBAApp::app()->gameDB(); + NoIntroGame game{}; + uint32_t crc32 = 0; + controller->thread()->core->checksum(controller->thread()->core, &crc32, CHECKSUM_CRC32); + + char gameTitle[17] = { '\0' }; + mCore* core = controller->thread()->core; + core->getGameTitle(core, gameTitle); + s_title = gameTitle; + +#ifdef USE_SQLITE3 + if (db && crc32 && NoIntroDBLookupGameByCRC(db, crc32, &game)) { + s_title = QLatin1String(game.name); + } +#endif + + updatePresence(); +} + +void gameStopped() { + if (!s_gameRunning) { + return; + } + s_gameRunning = false; + updatePresence(); +} + +} + +}
@@ -0,0 +1,24 @@
+/* Copyright (c) 2013-2019 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#pragma once + +#include <memory> + +namespace QGBA { + +class CoreController; + +namespace DiscordCoordinator { + +void init(); +void deinit(); + +void gameStarted(std::shared_ptr<CoreController>); +void gameStopped(); + +} + +}
@@ -62,8 +62,11 @@ int h = (tiles + w - 1) * m_size / w;
setMinimumSize(m_size, h - (h % m_size)); } else { int w = minimumSize().width() / m_size; + if (!w) { + w = 1; + } int h = (tiles + w - 1) * m_size / w; - setMinimumSize(minimumSize().width(), h - (h % m_size)); + setMinimumSize(w * m_size, h - (h % m_size)); } resizeEvent(nullptr); }
@@ -591,9 +591,8 @@ }
if (m_screenWidget->width() % size.width() == 0 && m_screenWidget->height() % size.height() == 0 && m_screenWidget->width() / size.width() == m_screenWidget->height() / size.height()) { factor = m_screenWidget->width() / size.width(); - } else { - m_savedScale = 0; } + m_savedScale = factor; for (QMap<int, QAction*>::iterator iter = m_frameSizes.begin(); iter != m_frameSizes.end(); ++iter) { bool enableSignals = iter.value()->blockSignals(true); iter.value()->setChecked(iter.key() == factor);
@@ -104,3 +104,21 @@ w->show();
return application.exec(); } + +#ifdef _WIN32 +#include <mgba-util/string.h> +#include <vector> + +extern "C" +int wmain(int argc, wchar_t* argv[]) { + std::vector<char*> argv8; + for (int i = 0; i < argc; ++i) { + argv8.push_back(utf16to8(reinterpret_cast<uint16_t*>(argv[i]), wcslen(argv[i]) * 2)); + } + int ret = main(argc, argv8.data()); + for (char* ptr : argv8) { + free(ptr); + } + return ret; +} +#endif
@@ -179,6 +179,24 @@
return ret; } +#ifdef _WIN32 +#include <mgba-util/string.h> + +int wmain(int argc, wchar_t** argv) { + char** argv8 = malloc(sizeof(char*) * argc); + int i; + for (i = 0; i < argc; ++i) { + argv8[i] = utf16to8((uint16_t*) argv[i], wcslen(argv[i]) * 2); + } + int ret = main(argc, argv8); + for (i = 0; i < argc; ++i) { + free(argv8[i]); + } + free(argv8); + return ret; +} +#endif + int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { struct mCoreThread thread = { .core = renderer->core
@@ -0,0 +1,24 @@
+Copyright (c) 2016, Mega Limited +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +
@@ -0,0 +1,41 @@
+mingw-std-threads +================= + +Implementation of standard C++11 threading classes, which are currently still missing on MinGW GCC. + +Target Windows version +---------------------- +This implementation should work with Windows XP (regardless of service pack), or newer. +The library automatically detects the version of Windows that is being targeted (at compile time), and selects an implementation that takes advantage of available Windows features. +In MinGW GCC, the target Windows version may optionally be selected by the command-line option `-D _WIN32_WINNT=...`. +Use `0x0600` for Windows Vista, or `0x0601` for Windows 7. +See "[Modifying `WINVER` and `_WIN32_WINNT`](https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt)" for more details. + +Usage +----- + +This is a header-only library. To use, just include the corresponding `mingw.xxx.h file`, where `xxx` would be the name of the standard header that you would normally include. + +For example, `#include "mingw.thread.h"` replaces `#include <thread>`. + +Compatibility +------------- + +This code has been tested to work with MinGW-w64 5.3.0, but should work with any other MinGW version that has the `std` threading classes missing, has C++11 support for lambda functions, variadic templates, and has working mutex helper classes in `<mutex>`. + +Switching from the win32-pthread based implementation +----------------------------------------------------- +It seems that recent versions of MinGW-w64 include a Win32 port of pthreads, and have the `std::thread`, `std::mutex`, etc. classes implemented and working based on that compatibility +layer. +That is a somewhat heavier implementation, as it relies on an abstraction layer, so you may still want to use this implementation for efficiency purposes. +Unfortunately you can't use this library standalone and independent of the system `<mutex>` headers, as it relies on those headers for `std::unique_lock` and other non-trivial utility classes. +In that case you will need to edit the `c++-config.h` file of your MinGW setup and comment out the definition of _GLIBCXX_HAS_GTHREADS. +This will cause the system headers not to define the actual `thread`, `mutex`, etc. classes, but still define the necessary utility classes. + +Why MinGW has no threading classes +---------------------------------- +It seems that for cross-platform threading implementation, the GCC standard library relies on the gthreads/pthreads library. +If this library is not available, as is the case with MinGW, the classes `std::thread`, `std::mutex`, `std::condition_variable` are not defined. +However, various usable helper classes are still defined in the system headers. +Hence, this implementation does not re-define them, and instead includes those headers. +
@@ -0,0 +1,549 @@
+/** +* @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_next <condition_variable> + +#include <cassert> +#include <chrono> +#include <system_error> +#include <windows.h> + +#if (WINVER < _WIN32_WINNT_VISTA) +#include <atomic> +#endif + +#include <mutex> +#include <shared_mutex> + +#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<int> 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(CreateSemaphore(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 <class M> + bool wait_impl(M& lock, DWORD timeout) + { + { + lock_guard<recursive_mutex> guard(mMutex); + mNumWaiters++; + } + lock.unlock(); + DWORD ret = WaitForSingleObject(mSemaphore, timeout); + + mNumWaiters--; + SetEvent(mWakeEvent); + lock.lock(); + if (ret == WAIT_OBJECT_0) + return true; + else if (ret == WAIT_TIMEOUT) + return false; +//2 possible cases: +//1)The point in notify_all() where we determine the count to +//increment the semaphore with has not been reached yet: +//we just need to decrement mNumWaiters, but setting the event does not hurt +// +//2)Semaphore has just been released with mNumWaiters just before +//we decremented it. This means that the semaphore count +//after all waiters finish won't be 0 - because not all waiters +//woke up by acquiring the semaphore - we woke up by a timeout. +//The notify_all() must handle this gracefully +// + else + { + using namespace std; + throw system_error(make_error_code(errc::protocol_error)); + } + } +public: + template <class M> + void wait(M& lock) + { + wait_impl(lock, INFINITE); + } + template <class M, class Predicate> + void wait(M& lock, Predicate pred) + { + while(!pred()) + { + wait(lock); + }; + } + + void notify_all() noexcept + { + lock_guard<recursive_mutex> lock(mMutex); //block any further wait requests until all current waiters are unblocked + if (mNumWaiters.load() <= 0) + return; + + ReleaseSemaphore(mSemaphore, mNumWaiters, NULL); + while(mNumWaiters > 0) + { + auto ret = WaitForSingleObject(mWakeEvent, 1000); + if (ret == WAIT_FAILED || ret == WAIT_ABANDONED) + 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<recursive_mutex> 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 <class M, class Rep, class Period> + cv_status wait_for(M& lock, + const std::chrono::duration<Rep, Period>& rel_time) + { + using namespace std::chrono; + auto timeout = duration_cast<milliseconds>(rel_time).count(); + DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1); + bool ret = wait_impl(lock, waittime) || (timeout >= INFINITE); + return ret?cv_status::no_timeout:cv_status::timeout; + } + + template <class M, class Rep, class Period, class Predicate> + bool wait_for(M& lock, + const std::chrono::duration<Rep, Period>& rel_time, Predicate pred) + { + return wait_until(lock, std::chrono::steady_clock::now()+rel_time, pred); + } + template <class M, class Clock, class Duration> + cv_status wait_until (M& lock, + const std::chrono::time_point<Clock,Duration>& abs_time) + { + return wait_for(lock, abs_time - Clock::now()); + } + template <class M, class Clock, class Duration, class Predicate> + bool wait_until (M& lock, + const std::chrono::time_point<Clock, Duration>& abs_time, + Predicate pred) + { + 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<mutex> &lock) + { + base::wait(lock); + } + template <class Predicate> + void wait(unique_lock<mutex>& lock, Predicate pred) + { + base::wait(lock, pred); + } + template <class Rep, class Period> + cv_status wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time) + { + return base::wait_for(lock, rel_time); + } + template <class Rep, class Period, class Predicate> + bool wait_for(unique_lock<mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time, Predicate pred) + { + return base::wait_for(lock, rel_time, pred); + } + template <class Clock, class Duration> + cv_status wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock,Duration>& abs_time) + { + return base::wait_until(lock, abs_time); + } + template <class Clock, class Duration, class Predicate> + bool wait_until (unique_lock<mutex>& lock, const std::chrono::time_point<Clock, Duration>& abs_time, Predicate pred) + { + return base::wait_until(lock, abs_time, pred); + } +}; +#endif // 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 +{ +#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<typename MTX> + inline static void before_wait (MTX * pmutex) + { + pmutex->mOwnerThread.checkSetOwnerBeforeUnlock(); + } + template<typename MTX> + 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<xp::mutex> & lock, DWORD time) + { + using mutex_handle_type = typename xp::mutex::native_handle_type; + static_assert(std::is_same<mutex_handle_type, PCRITICAL_SECTION>::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<xp::mutex>(*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<windows7::mutex> & lock, DWORD time) + { + windows7::mutex * pmutex = lock.release(); + bool success = wait_unique(pmutex, time); + lock = unique_lock<windows7::mutex>(*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<mutex> & lock) + { + wait_impl(lock, INFINITE); + } + + template<class Predicate> + void wait (unique_lock<mutex> & lock, Predicate pred) + { + while (!pred()) + wait(lock); + } + + template <class Rep, class Period> + cv_status wait_for(unique_lock<mutex>& lock, + const std::chrono::duration<Rep, Period>& rel_time) + { + using namespace std::chrono; + auto timeout = duration_cast<milliseconds>(rel_time).count(); + DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1); + bool result = wait_impl(lock, waittime) || (timeout >= INFINITE); + return result ? cv_status::no_timeout : cv_status::timeout; + } + + template <class Rep, class Period, class Predicate> + bool wait_for(unique_lock<mutex>& lock, + const std::chrono::duration<Rep, Period>& rel_time, + Predicate pred) + { + return wait_until(lock, + std::chrono::steady_clock::now() + rel_time, + std::move(pred)); + } + template <class Clock, class Duration> + cv_status wait_until (unique_lock<mutex>& lock, + const std::chrono::time_point<Clock,Duration>& abs_time) + { + return wait_for(lock, abs_time - Clock::now()); + } + template <class Clock, class Duration, class Predicate> + bool wait_until (unique_lock<mutex>& lock, + const std::chrono::time_point<Clock, Duration>& abs_time, + Predicate pred) + { + while (!pred()) + { + if (wait_until(lock, abs_time) == cv_status::timeout) + { + return pred(); + } + } + return true; + } +}; + +class condition_variable_any +{ + 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<class L> + bool wait_impl (L & lock, DWORD time) + { + unique_lock<decltype(internal_mutex_)> 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<mutex> & 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<native_shared_mutex> & lock, DWORD time) + { + native_shared_mutex * pmutex = lock.release(); + bool success = internal_cv_.wait_unique(pmutex, time); + lock = unique_lock<native_shared_mutex>(*pmutex, adopt_lock); + return success; + } + bool wait_impl (shared_lock<native_shared_mutex> & 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<native_shared_mutex>(*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<class L> + void wait (L & lock) + { + wait_impl(lock, INFINITE); + } + + template<class L, class Predicate> + void wait (L & lock, Predicate pred) + { + while (!pred()) + wait(lock); + } + + template <class L, class Rep, class Period> + cv_status wait_for(L& lock, const std::chrono::duration<Rep,Period>& period) + { + using namespace std::chrono; + auto timeout = duration_cast<milliseconds>(period).count(); + DWORD waittime = (timeout < INFINITE) ? ((timeout < 0) ? 0 : static_cast<DWORD>(timeout)) : (INFINITE - 1); + bool result = wait_impl(lock, waittime) || (timeout >= INFINITE); + return result ? cv_status::no_timeout : cv_status::timeout; + } + + template <class L, class Rep, class Period, class Predicate> + bool wait_for(L& lock, const std::chrono::duration<Rep, Period>& period, + Predicate pred) + { + return wait_until(lock, std::chrono::steady_clock::now() + period, + std::move(pred)); + } + template <class L, class Clock, class Duration> + cv_status wait_until (L& lock, + const std::chrono::time_point<Clock,Duration>& abs_time) + { + return wait_for(lock, abs_time - Clock::now()); + } + template <class L, class Clock, class Duration, class Predicate> + bool wait_until (L& lock, + const std::chrono::time_point<Clock, Duration>& 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
@@ -0,0 +1,474 @@
+/** +* @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 <chrono> +#include <system_error> +#include <atomic> +#include_next <mutex> //need for call_once() + +#if STDMUTEX_RECURSION_CHECKS || !defined(NDEBUG) +#include <cstdio> +#endif + +#include <windows.h> + +// Need for the implementation of invoke +#include <thread> + +#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<DWORD> 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. +#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 +{ + inline bool try_lock_internal (DWORD ms) noexcept + { + DWORD ret = WaitForSingleObject(mHandle, ms); +#ifndef NDEBUG + if (ret == WAIT_ABANDONED) + { + using namespace std; + fprintf(stderr, "FATAL: Thread terminated while holding a mutex."); + terminate(); + } +#endif + return (ret == WAIT_OBJECT_0) || (ret == WAIT_ABANDONED); + } +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, INFINITE); +// If (ret == WAIT_ABANDONED), then the thread that held ownership was +// terminated. Behavior is undefined, but Windows will pass ownership to this +// thread. +#ifndef NDEBUG + if (ret == WAIT_ABANDONED) + { + using namespace std; + fprintf(stderr, "FATAL: Thread terminated while holding a mutex."); + terminate(); + } +#endif + if ((ret != WAIT_OBJECT_0) && (ret != WAIT_ABANDONED)) + { + 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 <class Rep, class Period> + bool try_lock_for(const std::chrono::duration<Rep,Period>& dur) + { + using namespace std::chrono; + auto timeout = duration_cast<milliseconds>(dur).count(); + while (timeout > 0) + { + constexpr auto kMaxStep = static_cast<decltype(timeout)>(INFINITE-1); + auto step = (timeout < kMaxStep) ? timeout : kMaxStep; + if (try_lock_internal(static_cast<DWORD>(step))) + return true; + timeout -= step; + } + return false; + } + template <class Clock, class Duration> + bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time) + { + return try_lock_for(timeout_time - Clock::now()); + } +}; + +// Override if, and only if, it is necessary for error-checking. +#if STDMUTEX_RECURSION_CHECKS +class timed_mutex: recursive_timed_mutex +{ +public: + 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 <class Rep, class Period> + bool try_lock_for(const std::chrono::duration<Rep,Period>& dur) + { + DWORD self = mOwnerThread.checkOwnerBeforeLock(); + bool ret = recursive_timed_mutex::try_lock_for(dur); + if (ret) + mOwnerThread.setOwnerAfterLock(self); + return ret; + } + template <class Clock, class Duration> + bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time) + { + return try_lock_for(timeout_time - Clock::now()); + } + 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<class Callable, class... Args> + friend void call_once(once_flag& once, Callable&& f, Args&&... args); +public: + constexpr once_flag() noexcept: mMutex(), mHasRun(false) {} +}; + +template<class Callable, class... Args> +void call_once(once_flag& flag, Callable&& func, Args&&... args) +{ + if (flag.mHasRun.load(std::memory_order_acquire)) + return; + lock_guard<decltype(flag.mMutex)> lock(flag.mMutex); + if (flag.mHasRun.load(std::memory_order_acquire)) + return; + detail::invoke(std::forward<Callable>(func),std::forward<Args>(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
@@ -0,0 +1,410 @@
+/** +* @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_next <thread> + +#include <cstddef> // For std::size_t +#include <cerrno> // Detect error type. +#include <exception> // For std::terminate +#include <system_error> // For std::system_error +#include <functional> // For std::hash +#include <tuple> // For std::tuple +#include <chrono> // For sleep timing. +#include <memory> // For std::unique_ptr +#include <ostream> // Stream output for thread ids. +#include <utility> // For std::swap, std::forward + +// For the invoke implementation only: +#include <type_traits> // For std::result_of, etc. +//#include <utility> // For std::forward +//#include <functional> // For std::reference_wrapper + +#include <windows.h> +#include <process.h> // For _beginthreadex + +#ifndef NDEBUG +#include <cstdio> +#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 +{ +// For compatibility, implement std::invoke for C++11 and C++14 +#if __cplusplus < 201703L + template<bool PMemFunc, bool PMemData> + struct Invoker + { + template<class F, class... Args> + inline static typename std::result_of<F(Args...)>::type invoke (F&& f, Args&&... args) + { + return std::forward<F>(f)(std::forward<Args>(args)...); + } + }; + template<bool> + struct InvokerHelper; + + template<> + struct InvokerHelper<false> + { + template<class T1> + inline static auto get (T1&& t1) -> decltype(*std::forward<T1>(t1)) + { + return *std::forward<T1>(t1); + } + + template<class T1> + inline static auto get (const std::reference_wrapper<T1>& t1) -> decltype(t1.get()) + { + return t1.get(); + } + }; + + template<> + struct InvokerHelper<true> + { + template<class T1> + inline static auto get (T1&& t1) -> decltype(std::forward<T1>(t1)) + { + return std::forward<T1>(t1); + } + }; + + template<> + struct Invoker<true, false> + { + template<class T, class F, class T1, class... Args> + inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\ + decltype((InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f)(std::forward<Args>(args)...)) + { + return (InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(std::forward<T1>(t1)).*f)(std::forward<Args>(args)...); + } + }; + + template<> + struct Invoker<false, true> + { + template<class T, class F, class T1, class... Args> + inline static auto invoke (F T::* f, T1&& t1, Args&&... args) ->\ + decltype(InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f) + { + return InvokerHelper<std::is_base_of<T,typename std::decay<T1>::type>::value>::get(t1).*f; + } + }; + + template<class F, class... Args> + struct InvokeResult + { + typedef Invoker<std::is_member_function_pointer<typename std::remove_reference<F>::type>::value, + std::is_member_object_pointer<typename std::remove_reference<F>::type>::value && + (sizeof...(Args) == 1)> invoker; + inline static auto invoke (F&& f, Args&&... args) -> decltype(invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...)) + { + return invoker::invoke(std::forward<F>(f), std::forward<Args>(args)...); + }; + }; + + template<class F, class...Args> + auto invoke (F&& f, Args&&... args) -> decltype(InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...)) + { + return InvokeResult<F, Args...>::invoke(std::forward<F>(f), std::forward<Args>(args)...); + } +#else + using std::invoke; +#endif + + template<std::size_t...> + struct IntSeq {}; + + template<std::size_t N, std::size_t... S> + struct GenIntSeq : GenIntSeq<N-1, N-1, S...> { }; + + template<std::size_t... S> + struct GenIntSeq<0, S...> { typedef IntSeq<S...> type; }; + + // We can't define the Call struct in the function - the standard forbids template methods in that case + template<class Func, typename... Args> + class ThreadFuncCall + { + typedef std::tuple<Args...> Tuple; + Func mFunc; + Tuple mArgs; + + template <std::size_t... S> + void callFunc(detail::IntSeq<S...>) + { + detail::invoke(std::forward<Func>(mFunc), std::get<S>(std::forward<Tuple>(mArgs)) ...); + } + public: + ThreadFuncCall(Func&& aFunc, Args&&... aArgs) + :mFunc(std::forward<Func>(aFunc)), mArgs(std::forward<Args>(aArgs)...){} + + void callFunc() + { + callFunc(typename detail::GenIntSeq<sizeof...(Args)>::type()); + } + }; + +} // Namespace "detail" + +class thread +{ +public: + class id + { + DWORD mId; + void clear() {mId = 0;} + friend class thread; + friend class std::hash<id>; + public: + explicit id(DWORD aId=0) noexcept : mId(aId){} + 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<class _CharT, class _Traits> + 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; + HANDLE mHandle; + id mThreadId; + + template <class Call> + static unsigned __stdcall threadfunc(void* arg) + { + std::unique_ptr<Call> call(static_cast<Call*>(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.clear(); + } + + thread(const thread &other)=delete; + + template<class Func, typename... Args> + explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId() + { + typedef detail::ThreadFuncCall<Func, Args...> Call; + auto call = new Call( + std::forward<Func>(func), std::forward<Args>(args)...); + auto int_handle = _beginthreadex(NULL, 0, threadfunc<Call>, + static_cast<LPVOID>(call), 0, + reinterpret_cast<unsigned*>(&(mThreadId.mId))); + 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 + mHandle = reinterpret_cast<HANDLE>(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, INFINITE); + CloseHandle(mHandle); + mHandle = kInvalidHandle; + mThreadId.clear(); + } + + ~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<thread>(other)); + return *this; + } + void swap(thread&& other) noexcept + { + std::swap(mHandle, other.mHandle); + std::swap(mThreadId.mId, other.mThreadId.mId); + } + + static unsigned int hardware_concurrency() noexcept + { + static 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.clear(); + } +}; + +namespace this_thread +{ + inline thread::id get_id() noexcept {return thread::id(GetCurrentThreadId());} + inline void yield() noexcept {Sleep(0);} + template< class Rep, class Period > + void sleep_for( const std::chrono::duration<Rep,Period>& sleep_duration) + { + using namespace std::chrono; + using rep = milliseconds::rep; + rep ms = duration_cast<milliseconds>(sleep_duration).count(); + while (ms > 0) + { + constexpr rep kMaxRep = static_cast<rep>(INFINITE - 1); + auto sleepTime = (ms < kMaxRep) ? ms : kMaxRep; + Sleep(static_cast<DWORD>(sleepTime)); + ms -= sleepTime; + } + } + template <class Clock, class Duration> + void sleep_until(const std::chrono::time_point<Clock,Duration>& 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<mingw_stdthread::thread::id> +{ + 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
@@ -22,6 +22,9 @@
if(WIN32) add_definitions(-DDISCORD_WINDOWS) set(BASE_RPC_SRC ${BASE_RPC_SRC} connection_win.cpp discord_register_win.cpp) + if (NOT MSVC) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include/mingw-std-threads) + endif() add_library(discord-rpc STATIC ${BASE_RPC_SRC}) if (MSVC) if(USE_STATIC_CRT)@@ -58,6 +61,7 @@ set(BASE_RPC_SRC ${BASE_RPC_SRC} connection_unix.cpp)
if (APPLE) add_definitions(-DDISCORD_OSX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") set(BASE_RPC_SRC ${BASE_RPC_SRC} discord_register_osx.m) else (APPLE) add_definitions(-DDISCORD_LINUX)