diff --git a/build.sh b/build.sh index 1413134cc..ae7aa09bd 100755 --- a/build.sh +++ b/build.sh @@ -41,6 +41,7 @@ HAVE_CURL=0 HAVE_EXPAT=0 HAVE_GIT2=1 HAVE_LSTREAM=1 +HAVE_CPP20=0 RUBYINCLUDE="" RUBYINCLUDE2="" @@ -217,6 +218,9 @@ while [ "$*" != "" ]; do -nolstream) HAVE_LSTREAM=0 ;; + -cpp20) + HAVE_CPP20=1 + ;; -qt5) echo "*** WARNING: -qt5 option is ignored - Qt version is auto-detected now." ;; @@ -275,6 +279,7 @@ while [ "$*" != "" ]; do echo " -libpng Use libpng instead of Qt for PNG generation" echo " -nolibgit2 Do not include libgit2 for Git package support" echo " -nolstream Do not include the LStream plugin" + echo " -cpp20 Uses some C++20 features (e.g. atomics)" echo "" echo "Environment Variables:" echo "" @@ -670,6 +675,7 @@ qmake_options=( HAVE_PNG="$HAVE_PNG" HAVE_GIT2="$HAVE_GIT2" HAVE_LSTREAM="$HAVE_LSTREAM" + HAVE_CPP20="$HAVE_CPP20" PREFIX="$BIN" RPATH="$RPATH" KLAYOUT_VERSION="$KLAYOUT_VERSION" diff --git a/src/klayout.pri b/src/klayout.pri index 067942aec..c21ae29d8 100644 --- a/src/klayout.pri +++ b/src/klayout.pri @@ -204,12 +204,16 @@ msvc { # -Wno-reserved-user-defined-literal \ # - lessThan(QT_MAJOR_VERSION, 6) { - # because we use unordered_map/unordered_set: - QMAKE_CXXFLAGS += -std=c++11 + equals(HAVE_CPP20, "1") { + QMAKE_CXXFLAGS += -std=c++20 + DEFINES += HAVE_CPP20 } else { - # because we use unordered_map/unordered_set: - QMAKE_CXXFLAGS += -std=c++17 + lessThan(QT_MAJOR_VERSION, 6) { + # because we use unordered_map/unordered_set: + QMAKE_CXXFLAGS += -std=c++11 + } else { + QMAKE_CXXFLAGS += -std=c++17 + } } win32 { diff --git a/src/tl/tl/atomic/atomic.h b/src/tl/tl/atomic/atomic.h deleted file mode 100644 index 45f2af681..000000000 --- a/src/tl/tl/atomic/atomic.h +++ /dev/null @@ -1,203 +0,0 @@ -//---------------------------------------------------------------------------- -// This is free and unencumbered software released into the public domain. -// -// Anyone is free to copy, modify, publish, use, compile, sell, or distribute -// this software, either in source code form or as a compiled binary, for any -// purpose, commercial or non-commercial, and by any means. -// -// In jurisdictions that recognize copyright laws, the author or authors of -// this software dedicate any and all copyright interest in the software to the -// public domain. We make this dedication for the benefit of the public at -// large and to the detriment of our heirs and successors. We intend this -// dedication to be an overt act of relinquishment in perpetuity of all present -// and future rights to this software under copyright law. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// For more information, please refer to -//----------------------------------------------------------------------------- - -#ifndef ATOMIC_ATOMIC_H_ -#define ATOMIC_ATOMIC_H_ - -// Macro for disallowing copying of an object. -#if __cplusplus >= 201103L -#define ATOMIC_DISALLOW_COPY(T) \ - T(const T&) = delete; \ - T& operator=(const T&) = delete; -#else -#define ATOMIC_DISALLOW_COPY(T) \ - T(const T&); \ - T& operator=(const T&); -#endif - -// A portable static assert. -#if __cplusplus >= 201103L -#define ATOMIC_STATIC_ASSERT(condition, message) \ - static_assert((condition), message) -#else -// Based on: http://stackoverflow.com/a/809465/5778708 -#define ATOMIC_STATIC_ASSERT(condition, message) \ - _impl_STATIC_ASSERT_LINE(condition, __LINE__) -#define _impl_PASTE(a, b) a##b -#ifdef __GNUC__ -#define _impl_UNUSED __attribute__((__unused__)) -#else -#define _impl_UNUSED -#endif -#define _impl_STATIC_ASSERT_LINE(condition, line) \ - typedef char _impl_PASTE( \ - STATIC_ASSERT_failed_, \ - line)[(2 * static_cast(!!(condition))) - 1] _impl_UNUSED -#endif - -#if defined(__GNUC__) || defined(__clang__) || defined(__xlc__) -#define ATOMIC_USE_GCC_INTRINSICS -#elif defined(_MSC_VER) -#define ATOMIC_USE_MSVC_INTRINSICS -#include "atomic_msvc.h" -#elif __cplusplus >= 201103L -#define ATOMIC_USE_CPP11_ATOMIC -#include -#else -#error Unsupported compiler / system. -#endif - -namespace atomic { -template -class atomic { -public: - ATOMIC_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || - sizeof(T) == 8, - "Only types of size 1, 2, 4 or 8 are supported"); - - atomic() : value_(static_cast(0)) {} - - explicit atomic(const T value) : value_(value) {} - - /// @brief Performs an atomic increment operation (value + 1). - /// @returns The new value of the atomic object. - T operator++() { -#if defined(ATOMIC_USE_GCC_INTRINSICS) - return __atomic_add_fetch(&value_, 1, __ATOMIC_SEQ_CST); -#elif defined(ATOMIC_USE_MSVC_INTRINSICS) - return msvc::interlocked::increment(&value_); -#else - return ++value_; -#endif - } - - /// @brief Performs an atomic decrement operation (value - 1). - /// @returns The new value of the atomic object. - T operator--() { -#if defined(ATOMIC_USE_GCC_INTRINSICS) - return __atomic_sub_fetch(&value_, 1, __ATOMIC_SEQ_CST); -#elif defined(ATOMIC_USE_MSVC_INTRINSICS) - return msvc::interlocked::decrement(&value_); -#else - return --value_; -#endif - } - - /// @brief Performs an atomic compare-and-swap (CAS) operation. - /// - /// The value of the atomic object is only updated to the new value if the - /// old value of the atomic object matches @c expected_val. - /// - /// @param expected_val The expected value of the atomic object. - /// @param new_val The new value to write to the atomic object. - /// @returns True if new_value was written to the atomic object. - bool compare_exchange(const T expected_val, const T new_val) { -#if defined(ATOMIC_USE_GCC_INTRINSICS) - T e = expected_val; - return __atomic_compare_exchange_n( - &value_, &e, new_val, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); -#elif defined(ATOMIC_USE_MSVC_INTRINSICS) - const T old_val = - msvc::interlocked::compare_exchange(&value_, new_val, expected_val); - return (old_val == expected_val); -#else - T e = expected_val; - return value_.compare_exchange_weak(e, new_val); -#endif - } - - /// @brief Performs an atomic set operation. - /// - /// The value of the atomic object is unconditionally updated to the new - /// value. - /// - /// @param new_val The new value to write to the atomic object. - void store(const T new_val) { -#if defined(ATOMIC_USE_GCC_INTRINSICS) - __atomic_store_n(&value_, new_val, __ATOMIC_SEQ_CST); -#elif defined(ATOMIC_USE_MSVC_INTRINSICS) - (void)msvc::interlocked::exchange(&value_, new_val); -#else - value_.store(new_val); -#endif - } - - /// @returns the current value of the atomic object. - /// @note Be careful about how this is used, since any operations on the - /// returned value are inherently non-atomic. - T load() const { -#if defined(ATOMIC_USE_GCC_INTRINSICS) - return __atomic_load_n(&value_, __ATOMIC_SEQ_CST); -#elif defined(ATOMIC_USE_MSVC_INTRINSICS) - // TODO(m): Is there a better solution for MSVC? - return value_; -#else - return value_; -#endif - } - - /// @brief Performs an atomic exchange operation. - /// - /// The value of the atomic object is unconditionally updated to the new - /// value, and the old value is returned. - /// - /// @param new_val The new value to write to the atomic object. - /// @returns the old value. - T exchange(const T new_val) { -#if defined(ATOMIC_USE_GCC_INTRINSICS) - return __atomic_exchange_n(&value_, new_val, __ATOMIC_SEQ_CST); -#elif defined(ATOMIC_USE_MSVC_INTRINSICS) - return msvc::interlocked::exchange(&value_, new_val); -#else - return value_.exchange(new_val); -#endif - } - - T operator=(const T new_value) { - store(new_value); - return new_value; - } - - operator T() const { - return load(); - } - -private: -#if defined(ATOMIC_USE_GCC_INTRINSICS) || defined(ATOMIC_USE_MSVC_INTRINSICS) - volatile T value_; -#else - std::atomic value_; -#endif - - ATOMIC_DISALLOW_COPY(atomic) -}; - -} // namespace atomic - -// Undef temporary defines. -#undef ATOMIC_USE_GCC_INTRINSICS -#undef ATOMIC_USE_MSVC_INTRINSICS -#undef ATOMIC_USE_CPP11_ATOMIC - -#endif // ATOMIC_ATOMIC_H_ diff --git a/src/tl/tl/atomic/atomic_msvc.h b/src/tl/tl/atomic/atomic_msvc.h deleted file mode 100644 index d17254f48..000000000 --- a/src/tl/tl/atomic/atomic_msvc.h +++ /dev/null @@ -1,238 +0,0 @@ -//----------------------------------------------------------------------------- -// This is free and unencumbered software released into the public domain. -// -// Anyone is free to copy, modify, publish, use, compile, sell, or distribute -// this software, either in source code form or as a compiled binary, for any -// purpose, commercial or non-commercial, and by any means. -// -// In jurisdictions that recognize copyright laws, the author or authors of -// this software dedicate any and all copyright interest in the software to the -// public domain. We make this dedication for the benefit of the public at -// large and to the detriment of our heirs and successors. We intend this -// dedication to be an overt act of relinquishment in perpetuity of all present -// and future rights to this software under copyright law. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// For more information, please refer to -//----------------------------------------------------------------------------- - -#ifndef ATOMIC_ATOMIC_MSVC_H_ -#define ATOMIC_ATOMIC_MSVC_H_ - -// Define which functions we need (don't include ). -extern "C" { -short _InterlockedIncrement16(short volatile*); -long _InterlockedIncrement(long volatile*); -__int64 _InterlockedIncrement64(__int64 volatile*); - -short _InterlockedDecrement16(short volatile*); -long _InterlockedDecrement(long volatile*); -__int64 _InterlockedDecrement64(__int64 volatile*); - -char _InterlockedExchange8(char volatile*, char); -short _InterlockedExchange16(short volatile*, short); -long __cdecl _InterlockedExchange(long volatile*, long); -__int64 _InterlockedExchange64(__int64 volatile*, __int64); - -char _InterlockedCompareExchange8(char volatile*, char, char); -short _InterlockedCompareExchange16(short volatile*, short, short); -long __cdecl _InterlockedCompareExchange(long volatile*, long, long); -__int64 _InterlockedCompareExchange64(__int64 volatile*, __int64, __int64); -}; - -// Define which functions we want to use as inline intriniscs. -#pragma intrinsic(_InterlockedIncrement) -#pragma intrinsic(_InterlockedIncrement16) - -#pragma intrinsic(_InterlockedDecrement) -#pragma intrinsic(_InterlockedDecrement16) - -#pragma intrinsic(_InterlockedCompareExchange) -#pragma intrinsic(_InterlockedCompareExchange8) -#pragma intrinsic(_InterlockedCompareExchange16) - -#pragma intrinsic(_InterlockedExchange) -#pragma intrinsic(_InterlockedExchange8) -#pragma intrinsic(_InterlockedExchange16) - -#if defined(_M_X64) -#pragma intrinsic(_InterlockedIncrement64) -#pragma intrinsic(_InterlockedDecrement64) -#pragma intrinsic(_InterlockedCompareExchange64) -#pragma intrinsic(_InterlockedExchange64) -#endif // _M_X64 - -namespace atomic { -namespace msvc { -template -struct interlocked { -}; - -template -struct interlocked { - static inline T increment(T volatile* x) { - // There's no _InterlockedIncrement8(). - char old_val, new_val; - do { - old_val = static_cast(*x); - new_val = old_val + static_cast(1); - } while (_InterlockedCompareExchange8(reinterpret_cast(x), - new_val, - old_val) != old_val); - return static_cast(new_val); - } - - static inline T decrement(T volatile* x) { - // There's no _InterlockedDecrement8(). - char old_val, new_val; - do { - old_val = static_cast(*x); - new_val = old_val - static_cast(1); - } while (_InterlockedCompareExchange8(reinterpret_cast(x), - new_val, - old_val) != old_val); - return static_cast(new_val); - } - - static inline T compare_exchange(T volatile* x, - const T new_val, - const T expected_val) { - return static_cast( - _InterlockedCompareExchange8(reinterpret_cast(x), - static_cast(new_val), - static_cast(expected_val))); - } - - static inline T exchange(T volatile* x, const T new_val) { - return static_cast(_InterlockedExchange8( - reinterpret_cast(x), static_cast(new_val))); - } -}; - -template -struct interlocked { - static inline T increment(T volatile* x) { - return static_cast( - _InterlockedIncrement16(reinterpret_cast(x))); - } - - static inline T decrement(T volatile* x) { - return static_cast( - _InterlockedDecrement16(reinterpret_cast(x))); - } - - static inline T compare_exchange(T volatile* x, - const T new_val, - const T expected_val) { - return static_cast( - _InterlockedCompareExchange16(reinterpret_cast(x), - static_cast(new_val), - static_cast(expected_val))); - } - - static inline T exchange(T volatile* x, const T new_val) { - return static_cast( - _InterlockedExchange16(reinterpret_cast(x), - static_cast(new_val))); - } -}; - -template -struct interlocked { - static inline T increment(T volatile* x) { - return static_cast( - _InterlockedIncrement(reinterpret_cast(x))); - } - - static inline T decrement(T volatile* x) { - return static_cast( - _InterlockedDecrement(reinterpret_cast(x))); - } - - static inline T compare_exchange(T volatile* x, - const T new_val, - const T expected_val) { - return static_cast( - _InterlockedCompareExchange(reinterpret_cast(x), - static_cast(new_val), - static_cast(expected_val))); - } - - static inline T exchange(T volatile* x, const T new_val) { - return static_cast(_InterlockedExchange( - reinterpret_cast(x), static_cast(new_val))); - } -}; - -template -struct interlocked { - static inline T increment(T volatile* x) { -#if defined(_M_X64) - return static_cast( - _InterlockedIncrement64(reinterpret_cast(x))); -#else - // There's no _InterlockedIncrement64() for 32-bit x86. - __int64 old_val, new_val; - do { - old_val = static_cast<__int64>(*x); - new_val = old_val + static_cast<__int64>(1); - } while (_InterlockedCompareExchange64( - reinterpret_cast(x), new_val, old_val) != - old_val); - return static_cast(new_val); -#endif // _M_X64 - } - - static inline T decrement(T volatile* x) { -#if defined(_M_X64) - return static_cast( - _InterlockedDecrement64(reinterpret_cast(x))); -#else - // There's no _InterlockedDecrement64() for 32-bit x86. - __int64 old_val, new_val; - do { - old_val = static_cast<__int64>(*x); - new_val = old_val - static_cast<__int64>(1); - } while (_InterlockedCompareExchange64( - reinterpret_cast(x), new_val, old_val) != - old_val); - return static_cast(new_val); -#endif // _M_X64 - } - - static inline T compare_exchange(T volatile* x, - const T new_val, - const T expected_val) { - return static_cast(_InterlockedCompareExchange64( - reinterpret_cast(x), - static_cast(new_val), - static_cast(expected_val))); - } - - static inline T exchange(T volatile* x, const T new_val) { -#if defined(_M_X64) - return static_cast( - _InterlockedExchange64(reinterpret_cast(x), - static_cast(new_val))); -#else - // There's no _InterlockedExchange64 for 32-bit x86. - __int64 old_val; - do { - old_val = static_cast<__int64>(*x); - } while (_InterlockedCompareExchange64( - reinterpret_cast(x), new_val, old_val) != - old_val); - return static_cast(old_val); -#endif // _M_X64 - } -}; -} // namespace msvc -} // namespace atomic - -#endif // ATOMIC_ATOMIC_MSVC_H_ diff --git a/src/tl/tl/atomic/spinlock.h b/src/tl/tl/atomic/spinlock.h deleted file mode 100644 index 9e9653b04..000000000 --- a/src/tl/tl/atomic/spinlock.h +++ /dev/null @@ -1,78 +0,0 @@ -//----------------------------------------------------------------------------- -// This is free and unencumbered software released into the public domain. -// -// Anyone is free to copy, modify, publish, use, compile, sell, or distribute -// this software, either in source code form or as a compiled binary, for any -// purpose, commercial or non-commercial, and by any means. -// -// In jurisdictions that recognize copyright laws, the author or authors of -// this software dedicate any and all copyright interest in the software to the -// public domain. We make this dedication for the benefit of the public at -// large and to the detriment of our heirs and successors. We intend this -// dedication to be an overt act of relinquishment in perpetuity of all present -// and future rights to this software under copyright law. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// For more information, please refer to -//----------------------------------------------------------------------------- - -#ifndef ATOMIC_SPINLOCK_H_ -#define ATOMIC_SPINLOCK_H_ - -#include "atomic.h" - -namespace atomic { -class spinlock { -public: - spinlock() : value_(0) {} - - /// @brief Acquire the lock (blocking). - /// @note Trying to acquire a lock that is already held by the calling thread - /// will dead-lock (block indefinitely). - void lock() { - while (!value_.compare_exchange(UNLOCKED, LOCKED)) - ; - } - - /// @brief Release the lock. - /// @note It is an error to release a lock that has not been previously - /// acquired. - void unlock() { value_.store(UNLOCKED); } - -private: - static const int UNLOCKED = 0; - static const int LOCKED = 1; - - atomic value_; - - ATOMIC_DISALLOW_COPY(spinlock) -}; - -class lock_guard { -public: - /// @brief The constructor acquires the lock. - /// @param lock The spinlock that will be locked. - explicit lock_guard(spinlock& lock) : lock_(lock) { - lock_.lock(); - } - - /// @brief The destructor releases the lock. - ~lock_guard() { - lock_.unlock(); - } - -private: - spinlock& lock_; - - ATOMIC_DISALLOW_COPY(lock_guard) -}; - -} // namespace atomic - -#endif // ATOMIC_SPINLOCK_H_ diff --git a/src/tl/tl/tlThreads.cc b/src/tl/tl/tlThreads.cc index 671c28ddd..19ff4fa77 100644 --- a/src/tl/tl/tlThreads.cc +++ b/src/tl/tl/tlThreads.cc @@ -20,15 +20,17 @@ */ -#if !defined(HAVE_QT) || defined(HAVE_PTHREADS) - #include "tlThreads.h" #include "tlUtils.h" #include "tlTimer.h" +#include "tlSleep.h" #include "tlLog.h" #include "tlInternational.h" #include + +#if defined(HAVE_PTHREADS) + #define _TIMESPEC_DEFINED // avoids errors with pthread-win and MSVC2017 #include #include @@ -39,12 +41,71 @@ # include #endif +#endif + namespace tl { // ------------------------------------------------------------------------------- // WaitCondition implementation +#if defined(HAVE_CPP20) || !defined(HAVE_QT) || defined(HAVE_PTHREADS) + +#if defined(HAVE_CPP20) + +class WaitConditionPrivate +{ +public: + WaitConditionPrivate () + : m_condition () + { + } + + ~WaitConditionPrivate () + { + } + + bool wait (Mutex *mutex, unsigned long time) + { + bool result = true; + m_condition.clear (std::memory_order_release); + + mutex->unlock (); + + if (time != std::numeric_limits::max ()) { + while (time > 0 && ! m_condition.test (std::memory_order_acquire)) { + tl::usleep (1000); + --time; + } + result = (time != 0); + } else { + m_condition.wait (false); + } + + mutex->lock (); + + return result; + } + + void wake_all () + { + if (! m_condition.test_and_set (std::memory_order_acquire)) { + m_condition.notify_all (); + } + } + + void wake_one () + { + if (! m_condition.test_and_set (std::memory_order_acquire)) { + m_condition.notify_one (); + } + } + +private: + std::atomic_flag m_condition; +}; + +#else class WaitConditionPrivate { @@ -139,6 +200,8 @@ private: bool m_initialized; }; +#endif + WaitCondition::WaitCondition () { mp_data = new WaitConditionPrivate (); @@ -165,9 +228,13 @@ void WaitCondition::wakeOne () mp_data->wake_one (); } +#endif + // ------------------------------------------------------------------------------- // Thread implementation +#if !(defined(HAVE_QT) && !defined(HAVE_PTHREADS)) + class ThreadPrivateData { public: @@ -401,6 +468,6 @@ ThreadStorageHolderBase *ThreadStorageBase::holder () } } -} - #endif + +} diff --git a/src/tl/tl/tlThreads.h b/src/tl/tl/tlThreads.h index 28155f863..53fcdadc4 100644 --- a/src/tl/tl/tlThreads.h +++ b/src/tl/tl/tlThreads.h @@ -33,8 +33,7 @@ # include # include #else -// atomics taken from https://github.com/mbitsnbites/atomic -# include "atomic/spinlock.h" +# include #endif namespace tl @@ -60,11 +59,21 @@ public: class TL_PUBLIC Mutex { public: - Mutex () : m_spinlock () { } - void lock () { m_spinlock.lock (); } - void unlock () { m_spinlock.unlock (); } + Mutex () { } + + void lock () + { + while (flag.test_and_set (std::memory_order_acquire)) + ; + } + + void unlock () + { + flag.clear (std::memory_order_release); + } + private: - atomic::spinlock m_spinlock; + std::atomic_flag flag = ATOMIC_FLAG_INIT; }; #endif @@ -75,7 +84,7 @@ private: * available. */ -#if defined(HAVE_QT) && !defined(HAVE_PTHREADS) +#if defined(HAVE_QT) && !defined(HAVE_PTHREADS) && !defined(HAVE_CPP20) class TL_PUBLIC WaitCondition : public QWaitCondition