diff --git a/client/tracy_readerwriterqueue.h b/client/tracy_readerwriterqueue.h index 6a080fcd..40478c2c 100644 --- a/client/tracy_readerwriterqueue.h +++ b/client/tracy_readerwriterqueue.h @@ -1,8 +1,3 @@ -// This license applies to all the code in this repository except that written by third -// parties, namely the files in benchmarks/ext, which have their own licenses, and Jeff -// Preshing's semaphore implementation (used in the blocking queues) which has a zlib -// license (embedded in atomicops.h). -// // Simplified BSD License: // // Copyright (c) 2013-2021, Cameron Desrochers @@ -37,8 +32,6 @@ // ©2013-2016 Cameron Desrochers. // Distributed under the simplified BSD license (see the license file that // should have come with this header). -// Uses Jeff Preshing's semaphore implementation (under the terms of its -// separate zlib license, embedded below). #pragma once @@ -368,345 +361,6 @@ private: } // end namespace moodycamel - - // Portable single-producer, single-consumer semaphore below: - -#if defined(_WIN32) - // Avoid including windows.h in a header; we only need a handful of - // items, so we'll redeclare them here (this is relatively safe since - // the API generally has to remain stable between Windows versions). - // I know this is an ugly hack but it still beats polluting the global - // namespace with thousands of generic names or adding a .cpp for nothing. -extern "C" { - struct _SECURITY_ATTRIBUTES; - __declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName); - __declspec(dllimport) int __stdcall CloseHandle(void* hObject); - __declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds); - __declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount); -} -#elif defined(__MACH__) -#include -#elif defined(__unix__) -#include -#endif - -namespace tracy -{ -// Code in the spsc_sema namespace below is an adaptation of Jeff Preshing's -// portable + lightweight semaphore implementations, originally from -// https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h -// LICENSE: -// Copyright (c) 2015 Jeff Preshing -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgement in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. -namespace spsc_sema -{ -#if defined(_WIN32) -class Semaphore -{ -private: - void* m_hSema; - - Semaphore(const Semaphore& other); - Semaphore& operator=(const Semaphore& other); - -public: - AE_NO_TSAN Semaphore(int initialCount = 0) : m_hSema() - { - assert(initialCount >= 0); - const long maxLong = 0x7fffffff; - m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr); - assert(m_hSema); - } - - AE_NO_TSAN ~Semaphore() - { - CloseHandle(m_hSema); - } - - bool wait() AE_NO_TSAN - { - const unsigned long infinite = 0xffffffff; - return WaitForSingleObject(m_hSema, infinite) == 0; - } - - bool try_wait() AE_NO_TSAN - { - return WaitForSingleObject(m_hSema, 0) == 0; - } - - bool timed_wait(std::uint64_t usecs) AE_NO_TSAN - { - return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0; - } - - void signal(int count = 1) AE_NO_TSAN - { - while (!ReleaseSemaphore(m_hSema, count, nullptr)); - } -}; -#elif defined(__MACH__) -//--------------------------------------------------------- -// Semaphore (Apple iOS and OSX) -// Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html -//--------------------------------------------------------- -class Semaphore -{ -private: - semaphore_t m_sema; - - Semaphore(const Semaphore& other); - Semaphore& operator=(const Semaphore& other); - -public: - AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() - { - assert(initialCount >= 0); - kern_return_t rc = semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount); - assert(rc == KERN_SUCCESS); - AE_UNUSED(rc); - } - - AE_NO_TSAN ~Semaphore() - { - semaphore_destroy(mach_task_self(), m_sema); - } - - bool wait() AE_NO_TSAN - { - return semaphore_wait(m_sema) == KERN_SUCCESS; - } - - bool try_wait() AE_NO_TSAN - { - return timed_wait(0); - } - - bool timed_wait(std::uint64_t timeout_usecs) AE_NO_TSAN - { - mach_timespec_t ts; - ts.tv_sec = static_cast(timeout_usecs / 1000000); - ts.tv_nsec = static_cast((timeout_usecs % 1000000) * 1000); - - // added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html - kern_return_t rc = semaphore_timedwait(m_sema, ts); - return rc == KERN_SUCCESS; - } - - void signal() AE_NO_TSAN - { - while (semaphore_signal(m_sema) != KERN_SUCCESS); - } - - void signal(int count) AE_NO_TSAN - { - while (count-- > 0) - { - while (semaphore_signal(m_sema) != KERN_SUCCESS); - } - } -}; -#elif defined(__unix__) -//--------------------------------------------------------- -// Semaphore (POSIX, Linux) -//--------------------------------------------------------- -class Semaphore -{ -private: - sem_t m_sema; - - Semaphore(const Semaphore& other); - Semaphore& operator=(const Semaphore& other); - -public: - AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() - { - assert(initialCount >= 0); - int rc = sem_init(&m_sema, 0, static_cast(initialCount)); - assert(rc == 0); - AE_UNUSED(rc); - } - - AE_NO_TSAN ~Semaphore() - { - sem_destroy(&m_sema); - } - - bool wait() AE_NO_TSAN - { - // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error - int rc; - do - { - rc = sem_wait(&m_sema); - } - while (rc == -1 && errno == EINTR); - return rc == 0; - } - - bool try_wait() AE_NO_TSAN - { - int rc; - do { - rc = sem_trywait(&m_sema); - } while (rc == -1 && errno == EINTR); - return rc == 0; - } - - bool timed_wait(std::uint64_t usecs) AE_NO_TSAN - { - struct timespec ts; - const int usecs_in_1_sec = 1000000; - const int nsecs_in_1_sec = 1000000000; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += static_cast(usecs / usecs_in_1_sec); - ts.tv_nsec += static_cast(usecs % usecs_in_1_sec) * 1000; - // sem_timedwait bombs if you have more than 1e9 in tv_nsec - // so we have to clean things up before passing it in - if (ts.tv_nsec >= nsecs_in_1_sec) { - ts.tv_nsec -= nsecs_in_1_sec; - ++ts.tv_sec; - } - - int rc; - do { - rc = sem_timedwait(&m_sema, &ts); - } while (rc == -1 && errno == EINTR); - return rc == 0; - } - - void signal() AE_NO_TSAN - { - while (sem_post(&m_sema) == -1); - } - - void signal(int count) AE_NO_TSAN - { - while (count-- > 0) - { - while (sem_post(&m_sema) == -1); - } - } -}; -#else -#error Unsupported platform! (No semaphore wrapper available) -#endif - -//--------------------------------------------------------- -// LightweightSemaphore -//--------------------------------------------------------- -class LightweightSemaphore -{ -public: - typedef std::make_signed::type ssize_t; - -private: - weak_atomic m_count; - Semaphore m_sema; - - bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1) AE_NO_TSAN - { - ssize_t oldCount; - // Is there a better way to set the initial spin count? - // If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC, - // as threads start hitting the kernel semaphore. - int spin = 1024; - while (--spin >= 0) - { - if (m_count.load() > 0) - { - m_count.fetch_add_acquire(-1); - return true; - } - compiler_fence(memory_order_acquire); // Prevent the compiler from collapsing the loop. - } - oldCount = m_count.fetch_add_acquire(-1); - if (oldCount > 0) - return true; - if (timeout_usecs < 0) - { - if (m_sema.wait()) - return true; - } - if (timeout_usecs > 0 && m_sema.timed_wait(static_cast(timeout_usecs))) - return true; - // At this point, we've timed out waiting for the semaphore, but the - // count is still decremented indicating we may still be waiting on - // it. So we have to re-adjust the count, but only if the semaphore - // wasn't signaled enough times for us too since then. If it was, we - // need to release the semaphore too. - while (true) - { - oldCount = m_count.fetch_add_release(1); - if (oldCount < 0) - return false; // successfully restored things to the way they were - // Oh, the producer thread just signaled the semaphore after all. Try again: - oldCount = m_count.fetch_add_acquire(-1); - if (oldCount > 0 && m_sema.try_wait()) - return true; - } - } - -public: - AE_NO_TSAN LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount), m_sema() - { - assert(initialCount >= 0); - } - - bool tryWait() AE_NO_TSAN - { - if (m_count.load() > 0) - { - m_count.fetch_add_acquire(-1); - return true; - } - return false; - } - - bool wait() AE_NO_TSAN - { - return tryWait() || waitWithPartialSpinning(); - } - - bool wait(std::int64_t timeout_usecs) AE_NO_TSAN - { - return tryWait() || waitWithPartialSpinning(timeout_usecs); - } - - void signal(ssize_t count = 1) AE_NO_TSAN - { - assert(count >= 0); - ssize_t oldCount = m_count.fetch_add_release(count); - assert(oldCount >= -1); - if (oldCount < 0) - { - m_sema.signal(1); - } - } - - std::size_t availableApprox() const AE_NO_TSAN - { - ssize_t count = m_count.load(); - return count > 0 ? static_cast(count) : 0; - } -}; -} // end namespace spsc_sema -} // end namespace moodycamel - #if defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli)) #pragma warning(pop) #ifdef __cplusplus_cli @@ -1465,224 +1119,6 @@ private: #endif }; -// Like ReaderWriterQueue, but also providees blocking operations -template -class BlockingReaderWriterQueue -{ -private: - typedef ::tracy::ReaderWriterQueue ReaderWriterQueue; - -public: - explicit BlockingReaderWriterQueue(size_t size = 15) AE_NO_TSAN - : inner(size), sema(new spsc_sema::LightweightSemaphore()) - { } - - BlockingReaderWriterQueue(BlockingReaderWriterQueue&& other) AE_NO_TSAN - : inner(std::move(other.inner)), sema(std::move(other.sema)) - { } - - BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue&& other) AE_NO_TSAN - { - std::swap(sema, other.sema); - std::swap(inner, other.inner); - return *this; - } - - - // Enqueues a copy of element if there is room in the queue. - // Returns true if the element was enqueued, false otherwise. - // Does not allocate memory. - AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN - { - if (inner.try_enqueue(element)) { - sema->signal(); - return true; - } - return false; - } - - // Enqueues a moved copy of element if there is room in the queue. - // Returns true if the element was enqueued, false otherwise. - // Does not allocate memory. - AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN - { - if (inner.try_enqueue(std::forward(element))) { - sema->signal(); - return true; - } - return false; - } - -#if MOODYCAMEL_HAS_EMPLACE - // Like try_enqueue() but with emplace semantics (i.e. construct-in-place). - template - AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN - { - if (inner.try_emplace(std::forward(args)...)) { - sema->signal(); - return true; - } - return false; - } -#endif - - - // Enqueues a copy of element on the queue. - // Allocates an additional block of memory if needed. - // Only fails (returns false) if memory allocation fails. - AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN - { - if (inner.enqueue(element)) { - sema->signal(); - return true; - } - return false; - } - - // Enqueues a moved copy of element on the queue. - // Allocates an additional block of memory if needed. - // Only fails (returns false) if memory allocation fails. - AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN - { - if (inner.enqueue(std::forward(element))) { - sema->signal(); - return true; - } - return false; - } - -#if MOODYCAMEL_HAS_EMPLACE - // Like enqueue() but with emplace semantics (i.e. construct-in-place). - template - AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN - { - if (inner.emplace(std::forward(args)...)) { - sema->signal(); - return true; - } - return false; - } -#endif - - - // Attempts to dequeue an element; if the queue is empty, - // returns false instead. If the queue has at least one element, - // moves front to result using operator=, then returns true. - template - bool try_dequeue(U& result) AE_NO_TSAN - { - if (sema->tryWait()) { - bool success = inner.try_dequeue(result); - assert(success); - AE_UNUSED(success); - return true; - } - return false; - } - - - // Attempts to dequeue an element; if the queue is empty, - // waits until an element is available, then dequeues it. - template - void wait_dequeue(U& result) AE_NO_TSAN - { - while (!sema->wait()); - bool success = inner.try_dequeue(result); - AE_UNUSED(result); - assert(success); - AE_UNUSED(success); - } - - - // Attempts to dequeue an element; if the queue is empty, - // waits until an element is available up to the specified timeout, - // then dequeues it and returns true, or returns false if the timeout - // expires before an element can be dequeued. - // Using a negative timeout indicates an indefinite timeout, - // and is thus functionally equivalent to calling wait_dequeue. - template - bool wait_dequeue_timed(U& result, std::int64_t timeout_usecs) AE_NO_TSAN - { - if (!sema->wait(timeout_usecs)) { - return false; - } - bool success = inner.try_dequeue(result); - AE_UNUSED(result); - assert(success); - AE_UNUSED(success); - return true; - } - - -#if __cplusplus > 199711L || _MSC_VER >= 1700 - // Attempts to dequeue an element; if the queue is empty, - // waits until an element is available up to the specified timeout, - // then dequeues it and returns true, or returns false if the timeout - // expires before an element can be dequeued. - // Using a negative timeout indicates an indefinite timeout, - // and is thus functionally equivalent to calling wait_dequeue. - template - inline bool wait_dequeue_timed(U& result, std::chrono::duration const& timeout) AE_NO_TSAN - { - return wait_dequeue_timed(result, std::chrono::duration_cast(timeout).count()); - } -#endif - - - // Returns a pointer to the front element in the queue (the one that - // would be removed next by a call to `try_dequeue` or `pop`). If the - // queue appears empty at the time the method is called, nullptr is - // returned instead. - // Must be called only from the consumer thread. - AE_FORCEINLINE T* peek() const AE_NO_TSAN - { - return inner.peek(); - } - - // Removes the front element from the queue, if any, without returning it. - // Returns true on success, or false if the queue appeared empty at the time - // `pop` was called. - AE_FORCEINLINE bool pop() AE_NO_TSAN - { - if (sema->tryWait()) { - bool result = inner.pop(); - assert(result); - AE_UNUSED(result); - return true; - } - return false; - } - - // Returns the approximate number of items currently in the queue. - // Safe to call from both the producer and consumer threads. - AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN - { - return sema->availableApprox(); - } - - // Returns the total number of items that could be enqueued without incurring - // an allocation when this queue is empty. - // Safe to call from both the producer and consumer threads. - // - // NOTE: The actual capacity during usage may be different depending on the consumer. - // If the consumer is removing elements concurrently, the producer cannot add to - // the block the consumer is removing from until it's completely empty, except in - // the case where the producer was writing to the same block the consumer was - // reading from the whole time. - AE_FORCEINLINE size_t max_capacity() const { - return inner.max_capacity(); - } - -private: - // Disable copying & assignment - BlockingReaderWriterQueue(BlockingReaderWriterQueue const&) { } - BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue const&) { } - -private: - ReaderWriterQueue inner; - std::unique_ptr sema; -}; - } // end namespace moodycamel #ifdef AE_VCPP