mirror of https://github.com/KLayout/klayout.git
Preparing to use C++20 atomics for thread locking, which is slightly more efficient. Right now, that's an experimental option.
This commit is contained in:
parent
a8119c5a53
commit
9534209240
6
build.sh
6
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"
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 <http://unlicense.org/>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#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<int>(!!(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 <atomic>
|
||||
#else
|
||||
#error Unsupported compiler / system.
|
||||
#endif
|
||||
|
||||
namespace atomic {
|
||||
template <typename T>
|
||||
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<T>(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<T>::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<T>::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<T>::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<T>::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<T>::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<T> 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_
|
||||
|
|
@ -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 <http://unlicense.org/>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef ATOMIC_ATOMIC_MSVC_H_
|
||||
#define ATOMIC_ATOMIC_MSVC_H_
|
||||
|
||||
// Define which functions we need (don't include <intrin.h>).
|
||||
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 <typename T, size_t N = sizeof(T)>
|
||||
struct interlocked {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct interlocked<T, 1> {
|
||||
static inline T increment(T volatile* x) {
|
||||
// There's no _InterlockedIncrement8().
|
||||
char old_val, new_val;
|
||||
do {
|
||||
old_val = static_cast<char>(*x);
|
||||
new_val = old_val + static_cast<char>(1);
|
||||
} while (_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(x),
|
||||
new_val,
|
||||
old_val) != old_val);
|
||||
return static_cast<T>(new_val);
|
||||
}
|
||||
|
||||
static inline T decrement(T volatile* x) {
|
||||
// There's no _InterlockedDecrement8().
|
||||
char old_val, new_val;
|
||||
do {
|
||||
old_val = static_cast<char>(*x);
|
||||
new_val = old_val - static_cast<char>(1);
|
||||
} while (_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(x),
|
||||
new_val,
|
||||
old_val) != old_val);
|
||||
return static_cast<T>(new_val);
|
||||
}
|
||||
|
||||
static inline T compare_exchange(T volatile* x,
|
||||
const T new_val,
|
||||
const T expected_val) {
|
||||
return static_cast<T>(
|
||||
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(x),
|
||||
static_cast<const char>(new_val),
|
||||
static_cast<const char>(expected_val)));
|
||||
}
|
||||
|
||||
static inline T exchange(T volatile* x, const T new_val) {
|
||||
return static_cast<T>(_InterlockedExchange8(
|
||||
reinterpret_cast<volatile char*>(x), static_cast<const char>(new_val)));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct interlocked<T, 2> {
|
||||
static inline T increment(T volatile* x) {
|
||||
return static_cast<T>(
|
||||
_InterlockedIncrement16(reinterpret_cast<volatile short*>(x)));
|
||||
}
|
||||
|
||||
static inline T decrement(T volatile* x) {
|
||||
return static_cast<T>(
|
||||
_InterlockedDecrement16(reinterpret_cast<volatile short*>(x)));
|
||||
}
|
||||
|
||||
static inline T compare_exchange(T volatile* x,
|
||||
const T new_val,
|
||||
const T expected_val) {
|
||||
return static_cast<T>(
|
||||
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(x),
|
||||
static_cast<const short>(new_val),
|
||||
static_cast<const short>(expected_val)));
|
||||
}
|
||||
|
||||
static inline T exchange(T volatile* x, const T new_val) {
|
||||
return static_cast<T>(
|
||||
_InterlockedExchange16(reinterpret_cast<volatile short*>(x),
|
||||
static_cast<const short>(new_val)));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct interlocked<T, 4> {
|
||||
static inline T increment(T volatile* x) {
|
||||
return static_cast<T>(
|
||||
_InterlockedIncrement(reinterpret_cast<volatile long*>(x)));
|
||||
}
|
||||
|
||||
static inline T decrement(T volatile* x) {
|
||||
return static_cast<T>(
|
||||
_InterlockedDecrement(reinterpret_cast<volatile long*>(x)));
|
||||
}
|
||||
|
||||
static inline T compare_exchange(T volatile* x,
|
||||
const T new_val,
|
||||
const T expected_val) {
|
||||
return static_cast<T>(
|
||||
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(x),
|
||||
static_cast<const long>(new_val),
|
||||
static_cast<const long>(expected_val)));
|
||||
}
|
||||
|
||||
static inline T exchange(T volatile* x, const T new_val) {
|
||||
return static_cast<T>(_InterlockedExchange(
|
||||
reinterpret_cast<volatile long*>(x), static_cast<const long>(new_val)));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct interlocked<T, 8> {
|
||||
static inline T increment(T volatile* x) {
|
||||
#if defined(_M_X64)
|
||||
return static_cast<T>(
|
||||
_InterlockedIncrement64(reinterpret_cast<volatile __int64*>(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<volatile __int64*>(x), new_val, old_val) !=
|
||||
old_val);
|
||||
return static_cast<T>(new_val);
|
||||
#endif // _M_X64
|
||||
}
|
||||
|
||||
static inline T decrement(T volatile* x) {
|
||||
#if defined(_M_X64)
|
||||
return static_cast<T>(
|
||||
_InterlockedDecrement64(reinterpret_cast<volatile __int64*>(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<volatile __int64*>(x), new_val, old_val) !=
|
||||
old_val);
|
||||
return static_cast<T>(new_val);
|
||||
#endif // _M_X64
|
||||
}
|
||||
|
||||
static inline T compare_exchange(T volatile* x,
|
||||
const T new_val,
|
||||
const T expected_val) {
|
||||
return static_cast<T>(_InterlockedCompareExchange64(
|
||||
reinterpret_cast<volatile __int64*>(x),
|
||||
static_cast<const __int64>(new_val),
|
||||
static_cast<const __int64>(expected_val)));
|
||||
}
|
||||
|
||||
static inline T exchange(T volatile* x, const T new_val) {
|
||||
#if defined(_M_X64)
|
||||
return static_cast<T>(
|
||||
_InterlockedExchange64(reinterpret_cast<volatile __int64*>(x),
|
||||
static_cast<const __int64>(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<volatile __int64*>(x), new_val, old_val) !=
|
||||
old_val);
|
||||
return static_cast<T>(old_val);
|
||||
#endif // _M_X64
|
||||
}
|
||||
};
|
||||
} // namespace msvc
|
||||
} // namespace atomic
|
||||
|
||||
#endif // ATOMIC_ATOMIC_MSVC_H_
|
||||
|
|
@ -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 <http://unlicense.org/>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#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<int> 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_
|
||||
|
|
@ -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 <map>
|
||||
|
||||
#if defined(HAVE_PTHREADS)
|
||||
|
||||
#define _TIMESPEC_DEFINED // avoids errors with pthread-win and MSVC2017
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
|
|
@ -39,12 +41,71 @@
|
|||
# include <unistd.h>
|
||||
#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<unsigned long>::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
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@
|
|||
# include <QThread>
|
||||
# include <QThreadStorage>
|
||||
#else
|
||||
// atomics taken from https://github.com/mbitsnbites/atomic
|
||||
# include "atomic/spinlock.h"
|
||||
# include <atomic>
|
||||
#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
|
||||
|
|
|
|||
Loading…
Reference in New Issue