pax_global_header00006660000000000000000000000064141060252130014504gustar00rootroot0000000000000052 comment=8e7627d18c2108aca178888d88514179899a044f readerwriterqueue-1.0.6/000077500000000000000000000000001410602521300152545ustar00rootroot00000000000000readerwriterqueue-1.0.6/.gitignore000066400000000000000000000007701410602521300172500ustar00rootroot00000000000000*.ipch *.suo *.user *.sdf *.opensdf *.exe *.VC.db .vs/ tests/stabtest/msvc*/Debug/ tests/stabtest/msvc*/Release/ tests/stabtest/msvc*/obj/ tests/stabtest/msvc*/log.txt tests/stabtest/log.txt tests/unittests/msvc*/Debug/ tests/unittests/msvc*/Release/ tests/unittests/msvc*/obj/ tests/CDSChecker/model-checker/ benchmarks/msvc*/Debug/ benchmarks/msvc*/Release/ benchmarks/msvc*/obj/ test/ # Linux binaries benchmarks/benchmarks tests/stabtest/stabtest tests/unittests/unittests readerwriterqueue-1.0.6/CMakeLists.txt000066400000000000000000000005731410602521300200210ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.9) project(readerwriterqueue VERSION 1.0.0) include(GNUInstallDirs) add_library(${PROJECT_NAME} INTERFACE) target_include_directories(readerwriterqueue INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) install(FILES atomicops.h readerwriterqueue.h readerwritercircularbuffer.h LICENSE.md DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}) readerwriterqueue-1.0.6/LICENSE.md000066400000000000000000000031171410602521300166620ustar00rootroot00000000000000This 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 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. readerwriterqueue-1.0.6/README.md000066400000000000000000000140671410602521300165430ustar00rootroot00000000000000 # A single-producer, single-consumer lock-free queue for C++ This mini-repository has my very own implementation of a lock-free queue (that I designed from scratch) for C++. It only supports a two-thread use case (one consuming, and one producing). The threads can't switch roles, though you could use this queue completely from a single thread if you wish (but that would sort of defeat the purpose!). Note: If you need a general-purpose multi-producer, multi-consumer lock free queue, I have [one of those too][mpmc]. This repository also includes a [circular-buffer SPSC queue][circular] which supports blocking on enqueue as well as dequeue. ## Features - [Blazing fast][benchmarks] - Compatible with C++11 (supports moving objects instead of making copies) - Fully generic (templated container of any type) -- just like `std::queue`, you never need to allocate memory for elements yourself (which saves you the hassle of writing a lock-free memory manager to hold the elements you're queueing) - Allocates memory up front, in contiguous blocks - Provides a `try_enqueue` method which is guaranteed never to allocate memory (the queue starts with an initial capacity) - Also provides an `enqueue` method which can dynamically grow the size of the queue as needed - Also provides `try_emplace`/`emplace` convenience methods - Has a blocking version with `wait_dequeue` - Completely "wait-free" (no compare-and-swap loop). Enqueue and dequeue are always O(1) (not counting memory allocation) - On x86, the memory barriers compile down to no-ops, meaning enqueue and dequeue are just a simple series of loads and stores (and branches) ## Use Simply drop the readerwriterqueue.h (or readerwritercircularbuffer.h) and atomicops.h files into your source code and include them :-) A modern compiler is required (MSVC2010+, GCC 4.7+, ICC 13+, or any C++11 compliant compiler should work). Note: If you're using GCC, you really do need GCC 4.7 or above -- [4.6 has a bug][gcc46bug] that prevents the atomic fence primitives from working correctly. Example: ```cpp using namespace moodycamel; ReaderWriterQueue q(100); // Reserve space for at least 100 elements up front q.enqueue(17); // Will allocate memory if the queue is full bool succeeded = q.try_enqueue(18); // Will only succeed if the queue has an empty slot (never allocates) assert(succeeded); int number; succeeded = q.try_dequeue(number); // Returns false if the queue was empty assert(succeeded && number == 17); // You can also peek at the front item of the queue (consumer only) int* front = q.peek(); assert(*front == 18); succeeded = q.try_dequeue(number); assert(succeeded && number == 18); front = q.peek(); assert(front == nullptr); // Returns nullptr if the queue was empty ``` The blocking version has the exact same API, with the addition of `wait_dequeue` and `wait_dequeue_timed` methods: ```cpp BlockingReaderWriterQueue q; std::thread reader([&]() { int item; #if 1 for (int i = 0; i != 100; ++i) { // Fully-blocking: q.wait_dequeue(item); } #else for (int i = 0; i != 100; ) { // Blocking with timeout if (q.wait_dequeue_timed(item, std::chrono::milliseconds(5))) ++i; } #endif }); std::thread writer([&]() { for (int i = 0; i != 100; ++i) { q.enqueue(i); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } }); writer.join(); reader.join(); assert(q.size_approx() == 0); ``` Note that `wait_dequeue` will block indefinitely while the queue is empty; this means care must be taken to only call `wait_dequeue` if you're sure another element will come along eventually, or if the queue has a static lifetime. This is because destroying the queue while a thread is waiting on it will invoke undefined behaviour. The blocking circular buffer has a fixed number of slots, but is otherwise quite similar to use: ```cpp BlockingReaderWriterCircularBuffer q(1024); // pass initial capacity q.try_enqueue(1); int number; q.try_dequeue(number); assert(number == 1); q.wait_enqueue(123); q.wait_dequeue(number); assert(number == 123); q.wait_dequeue_timed(number, std::chrono::milliseconds(10)); ``` ## CMake ### Using targets in your project Using this project as a part of an existing CMake project is easy. In your CMakeLists.txt: ``` include(FetchContent) FetchContent_Declare( readerwriterqueue GIT_REPOSITORY https://github.com/cameron314/readerwriterqueue GIT_TAG master ) FetchContent_MakeAvailable(readerwriterqueue) add_library(my_target main.cpp) target_link_libraries(my_target PUBLIC readerwriterqueue) ``` In main.cpp: ```cpp #include int main() { moodycamel::ReaderWriterQueue q(100); } ``` ### Installing into system directories As an alternative to including the source files in your project directly, you can use CMake to install the library in your system's include directory: ``` mkdir build cd build cmake .. make install ``` Then, you can include it from your source code: ``` #include ``` ## Disclaimers The queue should only be used on platforms where aligned integer and pointer access is atomic; fortunately, that includes all modern processors (e.g. x86/x86-64, ARM, and PowerPC). *Not* for use with a DEC Alpha processor (which has very weak memory ordering) :-) Note that it's only been tested on x86(-64); if someone has access to other processors I'd love to run some tests on anything that's not x86-based. ## More info See the [LICENSE.md][license] file for the license (simplified BSD). My [blog post][blog] introduces the context that led to this code, and may be of interest if you're curious about lock-free programming. [blog]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++ [license]: LICENSE.md [benchmarks]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++#benchmarks [gcc46bug]: http://stackoverflow.com/questions/16429669/stdatomic-thread-fence-has-undefined-reference [mpmc]: https://github.com/cameron314/concurrentqueue [circular]: readerwritercircularbuffer.h readerwriterqueue-1.0.6/atomicops.h000066400000000000000000000545211410602521300174320ustar00rootroot00000000000000// ©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 // Provides portable (VC++2010+, Intel ICC 13, GCC 4.7+, and anything C++11 compliant) implementation // of low-level memory barriers, plus a few semi-portable utility macros (for inlining and alignment). // Also has a basic atomic type (limited to hardware-supported atomics with no memory ordering guarantees). // Uses the AE_* prefix for macros (historical reasons), and the "moodycamel" namespace for symbols. #include #include #include #include #include #include // Platform detection #if defined(__INTEL_COMPILER) #define AE_ICC #elif defined(_MSC_VER) #define AE_VCPP #elif defined(__GNUC__) #define AE_GCC #endif #if defined(_M_IA64) || defined(__ia64__) #define AE_ARCH_IA64 #elif defined(_WIN64) || defined(__amd64__) || defined(_M_X64) || defined(__x86_64__) #define AE_ARCH_X64 #elif defined(_M_IX86) || defined(__i386__) #define AE_ARCH_X86 #elif defined(_M_PPC) || defined(__powerpc__) #define AE_ARCH_PPC #else #define AE_ARCH_UNKNOWN #endif // AE_UNUSED #define AE_UNUSED(x) ((void)x) // AE_NO_TSAN/AE_TSAN_ANNOTATE_* #if defined(__has_feature) #if __has_feature(thread_sanitizer) #if __cplusplus >= 201703L // inline variables require C++17 namespace moodycamel { inline int ae_tsan_global; } #define AE_TSAN_ANNOTATE_RELEASE() AnnotateHappensBefore(__FILE__, __LINE__, (void *)(&::moodycamel::ae_tsan_global)) #define AE_TSAN_ANNOTATE_ACQUIRE() AnnotateHappensAfter(__FILE__, __LINE__, (void *)(&::moodycamel::ae_tsan_global)) extern "C" void AnnotateHappensBefore(const char*, int, void*); extern "C" void AnnotateHappensAfter(const char*, int, void*); #else // when we can't work with tsan, attempt to disable its warnings #define AE_NO_TSAN __attribute__((no_sanitize("thread"))) #endif #endif #endif #ifndef AE_NO_TSAN #define AE_NO_TSAN #endif #ifndef AE_TSAN_ANNOTATE_RELEASE #define AE_TSAN_ANNOTATE_RELEASE() #define AE_TSAN_ANNOTATE_ACQUIRE() #endif // AE_FORCEINLINE #if defined(AE_VCPP) || defined(AE_ICC) #define AE_FORCEINLINE __forceinline #elif defined(AE_GCC) //#define AE_FORCEINLINE __attribute__((always_inline)) #define AE_FORCEINLINE inline #else #define AE_FORCEINLINE inline #endif // AE_ALIGN #if defined(AE_VCPP) || defined(AE_ICC) #define AE_ALIGN(x) __declspec(align(x)) #elif defined(AE_GCC) #define AE_ALIGN(x) __attribute__((aligned(x))) #else // Assume GCC compliant syntax... #define AE_ALIGN(x) __attribute__((aligned(x))) #endif // Portable atomic fences implemented below: namespace moodycamel { enum memory_order { memory_order_relaxed, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst, // memory_order_sync: Forces a full sync: // #LoadLoad, #LoadStore, #StoreStore, and most significantly, #StoreLoad memory_order_sync = memory_order_seq_cst }; } // end namespace moodycamel #if (defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))) || (defined(AE_ICC) && __INTEL_COMPILER < 1600) // VS2010 and ICC13 don't support std::atomic_*_fence, implement our own fences #include #if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) #define AeFullSync _mm_mfence #define AeLiteSync _mm_mfence #elif defined(AE_ARCH_IA64) #define AeFullSync __mf #define AeLiteSync __mf #elif defined(AE_ARCH_PPC) #include #define AeFullSync __sync #define AeLiteSync __lwsync #endif #ifdef AE_VCPP #pragma warning(push) #pragma warning(disable: 4365) // Disable erroneous 'conversion from long to unsigned int, signed/unsigned mismatch' error when using `assert` #ifdef __cplusplus_cli #pragma managed(push, off) #endif #endif namespace moodycamel { AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN { switch (order) { case memory_order_relaxed: break; case memory_order_acquire: _ReadBarrier(); break; case memory_order_release: _WriteBarrier(); break; case memory_order_acq_rel: _ReadWriteBarrier(); break; case memory_order_seq_cst: _ReadWriteBarrier(); break; default: assert(false); } } // x86/x64 have a strong memory model -- all loads and stores have // acquire and release semantics automatically (so only need compiler // barriers for those). #if defined(AE_ARCH_X86) || defined(AE_ARCH_X64) AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN { switch (order) { case memory_order_relaxed: break; case memory_order_acquire: _ReadBarrier(); break; case memory_order_release: _WriteBarrier(); break; case memory_order_acq_rel: _ReadWriteBarrier(); break; case memory_order_seq_cst: _ReadWriteBarrier(); AeFullSync(); _ReadWriteBarrier(); break; default: assert(false); } } #else AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN { // Non-specialized arch, use heavier memory barriers everywhere just in case :-( switch (order) { case memory_order_relaxed: break; case memory_order_acquire: _ReadBarrier(); AeLiteSync(); _ReadBarrier(); break; case memory_order_release: _WriteBarrier(); AeLiteSync(); _WriteBarrier(); break; case memory_order_acq_rel: _ReadWriteBarrier(); AeLiteSync(); _ReadWriteBarrier(); break; case memory_order_seq_cst: _ReadWriteBarrier(); AeFullSync(); _ReadWriteBarrier(); break; default: assert(false); } } #endif } // end namespace moodycamel #else // Use standard library of atomics #include namespace moodycamel { AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN { switch (order) { case memory_order_relaxed: break; case memory_order_acquire: std::atomic_signal_fence(std::memory_order_acquire); break; case memory_order_release: std::atomic_signal_fence(std::memory_order_release); break; case memory_order_acq_rel: std::atomic_signal_fence(std::memory_order_acq_rel); break; case memory_order_seq_cst: std::atomic_signal_fence(std::memory_order_seq_cst); break; default: assert(false); } } AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN { switch (order) { case memory_order_relaxed: break; case memory_order_acquire: AE_TSAN_ANNOTATE_ACQUIRE(); std::atomic_thread_fence(std::memory_order_acquire); break; case memory_order_release: AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_release); break; case memory_order_acq_rel: AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_acq_rel); break; case memory_order_seq_cst: AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_seq_cst); break; default: assert(false); } } } // end namespace moodycamel #endif #if !defined(AE_VCPP) || (_MSC_VER >= 1700 && !defined(__cplusplus_cli)) #define AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC #endif #ifdef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC #include #endif #include // WARNING: *NOT* A REPLACEMENT FOR std::atomic. READ CAREFULLY: // Provides basic support for atomic variables -- no memory ordering guarantees are provided. // The guarantee of atomicity is only made for types that already have atomic load and store guarantees // at the hardware level -- on most platforms this generally means aligned pointers and integers (only). namespace moodycamel { template class weak_atomic { public: AE_NO_TSAN weak_atomic() : value() { } #ifdef AE_VCPP #pragma warning(push) #pragma warning(disable: 4100) // Get rid of (erroneous) 'unreferenced formal parameter' warning #endif template AE_NO_TSAN weak_atomic(U&& x) : value(std::forward(x)) { } #ifdef __cplusplus_cli // Work around bug with universal reference/nullptr combination that only appears when /clr is on AE_NO_TSAN weak_atomic(nullptr_t) : value(nullptr) { } #endif AE_NO_TSAN weak_atomic(weak_atomic const& other) : value(other.load()) { } AE_NO_TSAN weak_atomic(weak_atomic&& other) : value(std::move(other.load())) { } #ifdef AE_VCPP #pragma warning(pop) #endif AE_FORCEINLINE operator T() const AE_NO_TSAN { return load(); } #ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC template AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN { value = std::forward(x); return *this; } AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN { value = other.value; return *this; } AE_FORCEINLINE T load() const AE_NO_TSAN { return value; } AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN { #if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment); #if defined(_M_AMD64) else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment); #endif #else #error Unsupported platform #endif assert(false && "T must be either a 32 or 64 bit type"); return value; } AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN { #if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment); #if defined(_M_AMD64) else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment); #endif #else #error Unsupported platform #endif assert(false && "T must be either a 32 or 64 bit type"); return value; } #else template AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN { value.store(std::forward(x), std::memory_order_relaxed); return *this; } AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN { value.store(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed); return *this; } AE_FORCEINLINE T load() const AE_NO_TSAN { return value.load(std::memory_order_relaxed); } AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN { return value.fetch_add(increment, std::memory_order_acquire); } AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN { return value.fetch_add(increment, std::memory_order_release); } #endif private: #ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC // No std::atomic support, but still need to circumvent compiler optimizations. // `volatile` will make memory access slow, but is guaranteed to be reliable. volatile T value; #else std::atomic value; #endif }; } // 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 #elif defined(FREERTOS) #include #include #include #endif namespace moodycamel { // 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); } } }; #elif defined(FREERTOS) //--------------------------------------------------------- // Semaphore (FreeRTOS) //--------------------------------------------------------- class Semaphore { private: SemaphoreHandle_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); m_sema = xSemaphoreCreateCounting(static_cast(~0ull), static_cast(initialCount)); assert(m_sema); } AE_NO_TSAN ~Semaphore() { vSemaphoreDelete(m_sema); } bool wait() AE_NO_TSAN { return xSemaphoreTake(m_sema, portMAX_DELAY) == pdTRUE; } bool try_wait() AE_NO_TSAN { // Note: In an ISR context, if this causes a task to unblock, // the caller won't know about it if (xPortIsInsideInterrupt()) return xSemaphoreTakeFromISR(m_sema, NULL) == pdTRUE; return xSemaphoreTake(m_sema, 0) == pdTRUE; } bool timed_wait(std::uint64_t usecs) AE_NO_TSAN { std::uint64_t msecs = usecs / 1000; TickType_t ticks = static_cast(msecs / portTICK_PERIOD_MS); if (ticks == 0) return try_wait(); return xSemaphoreTake(m_sema, ticks) == pdTRUE; } void signal() AE_NO_TSAN { // Note: In an ISR context, if this causes a task to unblock, // the caller won't know about it BaseType_t rc; if (xPortIsInsideInterrupt()) rc = xSemaphoreGiveFromISR(m_sema, NULL); else rc = xSemaphoreGive(m_sema); assert(rc == pdTRUE); AE_UNUSED(rc); } void signal(int count) AE_NO_TSAN { while (count-- > 0) signal(); } }; #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 #pragma managed(pop) #endif #endif readerwriterqueue-1.0.6/benchmarks/000077500000000000000000000000001410602521300173715ustar00rootroot00000000000000readerwriterqueue-1.0.6/benchmarks/bench.cpp000066400000000000000000000375511410602521300211670ustar00rootroot00000000000000// ©2013-2015 Cameron Desrochers. // Distributed under the simplified BSD license (see the LICENSE file that // should have come with this file). // Benchmarks for moodycamel::ReaderWriterQueue. #if defined(_MSC_VER) && _MSC_VER < 1700 #define NO_FOLLY_SUPPORT #endif #if defined(_MSC_VER) && _MSC_VER < 1700 #define NO_CIRCULAR_BUFFER_SUPPORT #endif #if !defined(__amd64__) && !defined(_M_X64) && !defined(__x86_64__) && !defined(_M_IX86) && !defined(__i386__) #define NO_SPSC_SUPPORT // SPSC implementation is for x86 only #endif #include "ext/1024cores/spscqueue.h" // Dmitry's (on Intel site) #ifndef NO_FOLLY_SUPPORT #include "ext/folly/ProducerConsumerQueue.h" // Facebook's folly (GitHub) #endif #include "../readerwriterqueue.h" // Mine #ifndef NO_CIRCULAR_BUFFER_SUPPORT #include "../readerwritercircularbuffer.h" // Mine template class BlockingReaderWriterCircularBufferAdapter : public moodycamel::BlockingReaderWriterCircularBuffer { public: BlockingReaderWriterCircularBufferAdapter(std::size_t capacity) : moodycamel::BlockingReaderWriterCircularBuffer(capacity) { } void enqueue(T const& x) { this->wait_enqueue(x); } }; #endif #include "systemtime.h" #include "../tests/common/simplethread.h" #include #include #include // For std::accumulate #include #include #include #ifndef UNUSED #define UNUSED(x) ((void)x); #endif using namespace moodycamel; #ifndef NO_FOLLY_SUPPORT using namespace folly; #endif typedef std::minstd_rand RNG_t; enum BenchmarkType { bench_raw_add, bench_raw_remove, bench_empty_remove, bench_single_threaded, bench_mostly_add, bench_mostly_remove, bench_heavy_concurrent, bench_random_concurrent, BENCHMARK_COUNT }; // Returns the number of seconds elapsed (high-precision), and the number of enqueue/dequeue // operations performed (in the out_Ops parameter) template double runBenchmark(BenchmarkType benchmark, unsigned int randomSeed, double& out_Ops); const int BENCHMARK_NAME_MAX = 17; // Not including null terminator const char* benchmarkName(BenchmarkType benchmark); int main(int argc, char** argv) { #ifdef NDEBUG const int TEST_COUNT = 25; #else const int TEST_COUNT = 2; #endif assert(TEST_COUNT >= 2); const double FASTEST_PERCENT_CONSIDERED = 20; // Consider only the fastest runs in the top 20% double rwqResults[BENCHMARK_COUNT][TEST_COUNT]; double brwcbResults[BENCHMARK_COUNT][TEST_COUNT]; double spscResults[BENCHMARK_COUNT][TEST_COUNT]; double follyResults[BENCHMARK_COUNT][TEST_COUNT]; // Also calculate a rough heuristic of "ops/s" (across all runs, not just fastest) double rwqOps[BENCHMARK_COUNT][TEST_COUNT]; double brwcbOps[BENCHMARK_COUNT][TEST_COUNT]; double spscOps[BENCHMARK_COUNT][TEST_COUNT]; double follyOps[BENCHMARK_COUNT][TEST_COUNT]; // Make sure the randomness of each benchmark run is identical unsigned int randSeeds[BENCHMARK_COUNT]; for (unsigned int i = 0; i != BENCHMARK_COUNT; ++i) { randSeeds[i] = ((unsigned int)time(NULL)) * i; } // Run benchmarks for (int benchmark = 0; benchmark < BENCHMARK_COUNT; ++benchmark) { for (int i = 0; i < TEST_COUNT; ++i) { rwqResults[benchmark][i] = runBenchmark>((BenchmarkType)benchmark, randSeeds[benchmark], rwqOps[benchmark][i]); } #ifndef NO_CIRCULAR_BUFFER_SUPPORT for (int i = 0; i < TEST_COUNT; ++i) { brwcbResults[benchmark][i] = runBenchmark>((BenchmarkType)benchmark, randSeeds[benchmark], brwcbOps[benchmark][i]); } #else for (int i = 0; i < TEST_COUNT; ++i) { brwcbResults[benchmark][i] = 0; brwcbOps[benchmark][i] = 0; } #endif #ifndef NO_SPSC_SUPPORT for (int i = 0; i < TEST_COUNT; ++i) { spscResults[benchmark][i] = runBenchmark>((BenchmarkType)benchmark, randSeeds[benchmark], spscOps[benchmark][i]); } #else for (int i = 0; i < TEST_COUNT; ++i) { spscResults[benchmark][i] = 0; spscOps[benchmark][i] = 0; } #endif #ifndef NO_FOLLY_SUPPORT for (int i = 0; i < TEST_COUNT; ++i) { follyResults[benchmark][i] = runBenchmark>((BenchmarkType)benchmark, randSeeds[benchmark], follyOps[benchmark][i]); } #else for (int i = 0; i < TEST_COUNT; ++i) { follyResults[benchmark][i] = 0; follyOps[benchmark][i] = 0; } #endif } // Sort results for (int benchmark = 0; benchmark < BENCHMARK_COUNT; ++benchmark) { std::sort(&rwqResults[benchmark][0], &rwqResults[benchmark][0] + TEST_COUNT); std::sort(&brwcbResults[benchmark][0], &brwcbResults[benchmark][0] + TEST_COUNT); std::sort(&spscResults[benchmark][0], &spscResults[benchmark][0] + TEST_COUNT); std::sort(&follyResults[benchmark][0], &follyResults[benchmark][0] + TEST_COUNT); } // Display results int max = std::max(2, (int)(TEST_COUNT * FASTEST_PERCENT_CONSIDERED / 100)); assert(max > 0); #ifdef NO_CIRCULAR_BUFFER_SUPPORT std::cout << "Note: BRWCB queue not supported on this platform, discount its timings" << std::endl; #endif #ifdef NO_SPSC_SUPPORT std::cout << "Note: SPSC queue not supported on this platform, discount its timings" << std::endl; #endif #ifdef NO_FOLLY_SUPPORT std::cout << "Note: Folly queue not supported by this compiler, discount its timings" << std::endl; #endif std::cout << std::setw(BENCHMARK_NAME_MAX) << " " << " |---------------- Min -----------------|----------------- Max -----------------|----------------- Avg -----------------|\n"; std::cout << std::left << std::setw(BENCHMARK_NAME_MAX) << "Benchmark" << " | RWQ | BRWCB | SPSC | Folly | RWQ | BRWCB | SPSC | Folly | RWQ | BRWCB | SPSC | Folly | xSPSC | xFolly\n"; std::cout.fill('-'); std::cout << std::setw(BENCHMARK_NAME_MAX) << "---------" << "-+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+-------+-------\n"; std::cout.fill(' '); double rwqOpsPerSec = 0, brwcbOpsPerSec = 0, spscOpsPerSec = 0, follyOpsPerSec = 0; int opTimedBenchmarks = 0; for (int benchmark = 0; benchmark < BENCHMARK_COUNT; ++benchmark) { double rwqMin = rwqResults[benchmark][0], rwqMax = rwqResults[benchmark][max - 1]; double brwcbMin = brwcbResults[benchmark][0], brwcbMax = brwcbResults[benchmark][max - 1]; double spscMin = spscResults[benchmark][0], spscMax = spscResults[benchmark][max - 1]; double follyMin = follyResults[benchmark][0], follyMax = follyResults[benchmark][max - 1]; double rwqAvg = std::accumulate(&rwqResults[benchmark][0], &rwqResults[benchmark][0] + max, 0.0) / max; double brwcbAvg = std::accumulate(&brwcbResults[benchmark][0], &brwcbResults[benchmark][0] + max, 0.0) / max; double spscAvg = std::accumulate(&spscResults[benchmark][0], &spscResults[benchmark][0] + max, 0.0) / max; double follyAvg = std::accumulate(&follyResults[benchmark][0], &follyResults[benchmark][0] + max, 0.0) / max; double spscMult = rwqAvg < 0.00001 ? 0 : spscAvg / rwqAvg; double follyMult = follyAvg < 0.00001 ? 0 : follyAvg / rwqAvg; if (rwqResults[benchmark][0] != -1) { double rwqTotalAvg = std::accumulate(&rwqResults[benchmark][0], &rwqResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT; double brwcbTotalAvg = std::accumulate(&brwcbResults[benchmark][0], &brwcbResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT; double spscTotalAvg = std::accumulate(&spscResults[benchmark][0], &spscResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT; double follyTotalAvg = std::accumulate(&follyResults[benchmark][0], &follyResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT; rwqOpsPerSec += rwqTotalAvg == 0 ? 0 : std::accumulate(&rwqOps[benchmark][0], &rwqOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / rwqTotalAvg; brwcbOpsPerSec += brwcbTotalAvg == 0 ? 0 : std::accumulate(&brwcbOps[benchmark][0], &brwcbOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / brwcbTotalAvg; spscOpsPerSec += spscTotalAvg == 0 ? 0 : std::accumulate(&spscOps[benchmark][0], &spscOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / spscTotalAvg; follyOpsPerSec += follyTotalAvg == 0 ? 0 : std::accumulate(&follyOps[benchmark][0], &follyOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / follyTotalAvg; ++opTimedBenchmarks; } std::cout << std::left << std::setw(BENCHMARK_NAME_MAX) << benchmarkName((BenchmarkType)benchmark) << " | " << std::fixed << std::setprecision(4) << rwqMin << "s | " << std::fixed << std::setprecision(4) << brwcbMin << "s | " << std::fixed << std::setprecision(4) << spscMin << "s | " << std::fixed << std::setprecision(4) << follyMin << "s | " << std::fixed << std::setprecision(4) << rwqMax << "s | " << std::fixed << std::setprecision(4) << brwcbMax << "s | " << std::fixed << std::setprecision(4) << spscMax << "s | " << std::fixed << std::setprecision(4) << follyMax << "s | " << std::fixed << std::setprecision(4) << rwqAvg << "s | " << std::fixed << std::setprecision(4) << brwcbAvg << "s | " << std::fixed << std::setprecision(4) << spscAvg << "s | " << std::fixed << std::setprecision(4) << follyAvg << "s | " << std::fixed << std::setprecision(2) << spscMult << "x | " << std::fixed << std::setprecision(2) << follyMult << "x" << "\n" ; } rwqOpsPerSec /= opTimedBenchmarks; brwcbOpsPerSec /= opTimedBenchmarks; spscOpsPerSec /= opTimedBenchmarks; follyOpsPerSec /= opTimedBenchmarks; std::cout << "\nAverage ops/s:\n" << " ReaderWriterQueue: " << std::fixed << std::setprecision(2) << rwqOpsPerSec / 1000000 << " million\n" << " BlockingReaderWriterCircularBuffer: " << std::fixed << std::setprecision(2) << brwcbOpsPerSec / 1000000 << " million\n" << " SPSC queue: " << std::fixed << std::setprecision(2) << spscOpsPerSec / 1000000 << " million\n" << " Folly queue: " << std::fixed << std::setprecision(2) << follyOpsPerSec / 1000000 << " million\n" ; std::cout << std::endl; return 0; } template double runBenchmark(BenchmarkType benchmark, unsigned int randomSeed, double& out_Ops) { typedef unsigned long long counter_t; SystemTime start; double result = 0; volatile int forceNoOptimizeDummy; switch (benchmark) { case bench_raw_add: { const counter_t MAX = 100 * 1000; out_Ops = MAX; TQueue q(MAX); int num = 0; start = getSystemTime(); for (counter_t i = 0; i != MAX; ++i) { q.enqueue(num); ++num; } result = getTimeDelta(start); int temp = -1; q.try_dequeue(temp); forceNoOptimizeDummy = temp; } break; case bench_raw_remove: { const counter_t MAX = 100 * 1000; out_Ops = MAX; TQueue q(MAX); int num = 0; for (counter_t i = 0; i != MAX; ++i) { q.enqueue(num); ++num; } int element = -1; int total = 0; num = 0; start = getSystemTime(); for (counter_t i = 0; i != MAX; ++i) { bool success = q.try_dequeue(element); assert(success && num++ == element); UNUSED(success); total += element; } result = getTimeDelta(start); assert(!q.try_dequeue(element)); forceNoOptimizeDummy = total; } break; case bench_empty_remove: { const counter_t MAX = 2000 * 1000; out_Ops = MAX; TQueue q(MAX); int total = 0; start = getSystemTime(); SimpleThread consumer([&]() { int element; for (counter_t i = 0; i != MAX; ++i) { if (q.try_dequeue(element)) { total += element; } } }); SimpleThread producer([&]() { int num = 0; for (counter_t i = 0; i != MAX / 2; ++i) { if ((i & 32767) == 0) { // Just to make sure the loops aren't optimized out entirely q.enqueue(num); ++num; } } }); producer.join(); consumer.join(); result = getTimeDelta(start); forceNoOptimizeDummy = total; } break; case bench_single_threaded: { const counter_t MAX = 200 * 1000; out_Ops = MAX; RNG_t rng(randomSeed); std::uniform_int_distribution rand(0, 1); TQueue q(MAX); int num = 0; int element = -1; start = getSystemTime(); for (counter_t i = 0; i != MAX; ++i) { if (rand(rng) == 1) { q.enqueue(num); ++num; } else { q.try_dequeue(element); } } result = getTimeDelta(start); forceNoOptimizeDummy = (int)(q.try_dequeue(element)); } break; case bench_mostly_add: { const counter_t MAX = 1200 * 1000; out_Ops = MAX; int readOps = 0; RNG_t rng(randomSeed); std::uniform_int_distribution rand(0, 3); TQueue q(MAX); int element = -1; start = getSystemTime(); SimpleThread consumer([&]() { for (counter_t i = 0; i != MAX / 10; ++i) { if (rand(rng) == 0) { q.try_dequeue(element); ++readOps; } } }); SimpleThread producer([&]() { int num = 0; for (counter_t i = 0; i != MAX; ++i) { q.enqueue(num); ++num; } }); producer.join(); consumer.join(); result = getTimeDelta(start); forceNoOptimizeDummy = (int)(q.try_dequeue(element)); out_Ops += readOps; } break; case bench_mostly_remove: { const counter_t MAX = 1200 * 1000; out_Ops = MAX; int writeOps = 0; RNG_t rng(randomSeed); std::uniform_int_distribution rand(0, 3); TQueue q(MAX); int element = -1; start = getSystemTime(); SimpleThread consumer([&]() { for (counter_t i = 0; i != MAX; ++i) { q.try_dequeue(element); } }); SimpleThread producer([&]() { int num = 0; for (counter_t i = 0; i != MAX / 10; ++i) { if (rand(rng) == 0) { q.enqueue(num); ++num; } } writeOps = num; }); producer.join(); consumer.join(); result = getTimeDelta(start); forceNoOptimizeDummy = (int)(q.try_dequeue(element)); out_Ops += writeOps; } break; case bench_heavy_concurrent: { const counter_t MAX = 1000 * 1000; out_Ops = MAX * 2; TQueue q(MAX); int element = -1; start = getSystemTime(); SimpleThread consumer([&]() { for (counter_t i = 0; i != MAX; ++i) { q.try_dequeue(element); } }); SimpleThread producer([&]() { int num = 0; for (counter_t i = 0; i != MAX; ++i) { q.enqueue(num); ++num; } }); producer.join(); consumer.join(); result = getTimeDelta(start); forceNoOptimizeDummy = (int)(q.try_dequeue(element)); } break; case bench_random_concurrent: { const counter_t MAX = 800 * 1000; int readOps = 0, writeOps = 0; TQueue q(MAX); int element = -1; start = getSystemTime(); SimpleThread consumer([&]() { RNG_t rng(randomSeed); std::uniform_int_distribution rand(0, 15); for (counter_t i = 0; i != MAX; ++i) { if (rand(rng) == 0) { q.try_dequeue(element); ++readOps; } } }); SimpleThread producer([&]() { RNG_t rng(randomSeed * 3 - 1); std::uniform_int_distribution rand(0, 15); int num = 0; for (counter_t i = 0; i != MAX; ++i) { if (rand(rng) == 0) { q.enqueue(num); ++num; } } writeOps = num; }); producer.join(); consumer.join(); result = getTimeDelta(start); forceNoOptimizeDummy = (int)(q.try_dequeue(element)); out_Ops = readOps + writeOps; } break; default: assert(false); out_Ops = 0; return 0; } UNUSED(forceNoOptimizeDummy); return result / 1000.0; } const char* benchmarkName(BenchmarkType benchmark) { switch (benchmark) { case bench_raw_add: return "Raw add"; case bench_raw_remove: return "Raw remove"; case bench_empty_remove: return "Raw empty remove"; case bench_single_threaded: return "Single-threaded"; case bench_mostly_add: return "Mostly add"; case bench_mostly_remove: return "Mostly remove"; case bench_heavy_concurrent: return "Heavy concurrent"; case bench_random_concurrent: return "Random concurrent"; default: return ""; } } readerwriterqueue-1.0.6/benchmarks/ext/000077500000000000000000000000001410602521300201715ustar00rootroot00000000000000readerwriterqueue-1.0.6/benchmarks/ext/1024cores/000077500000000000000000000000001410602521300216135ustar00rootroot00000000000000readerwriterqueue-1.0.6/benchmarks/ext/1024cores/spscqueue.h000066400000000000000000000066471410602521300240160ustar00rootroot00000000000000#include "../../../atomicops.h" #include // For std::size_t // From http://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue // (and http://software.intel.com/en-us/articles/single-producer-single-consumer-queue) // load with 'consume' (data-dependent) memory ordering template T load_consume(T const* addr) { // hardware fence is implicit on x86 T v = *const_cast(addr); moodycamel::compiler_fence(moodycamel::memory_order_seq_cst); return v; } // store with 'release' memory ordering template void store_release(T* addr, T v) { // hardware fence is implicit on x86 moodycamel::compiler_fence(moodycamel::memory_order_seq_cst); *const_cast(addr) = v; } // cache line size on modern x86 processors (in bytes) size_t const cache_line_size = 64; // single-producer/single-consumer queue template class spsc_queue { public: spsc_queue() { node* n = new node; n->next_ = 0; tail_ = head_ = first_= tail_copy_ = n; } explicit spsc_queue(size_t prealloc) { node* n = new node; n->next_ = 0; tail_ = head_ = first_ = tail_copy_ = n; // [CD] Not (at all) the most efficient way to pre-allocate memory, but it works T dummy = T(); for (size_t i = 0; i != prealloc; ++i) { enqueue(dummy); } for (size_t i = 0; i != prealloc; ++i) { try_dequeue(dummy); } } ~spsc_queue() { node* n = first_; do { node* next = n->next_; delete n; n = next; } while (n); } void enqueue(T v) { node* n = alloc_node(); n->next_ = 0; n->value_ = v; store_release(&head_->next_, n); head_ = n; } // returns 'false' if queue is empty bool try_dequeue(T& v) { if (load_consume(&tail_->next_)) { v = tail_->next_->value_; store_release(&tail_, tail_->next_); return true; } else { return false; } } private: // internal node structure struct node { node* next_; T value_; }; // consumer part // accessed mainly by consumer, infrequently be producer node* tail_; // tail of the queue // delimiter between consumer part and producer part, // so that they situated on different cache lines char cache_line_pad_ [cache_line_size]; // producer part // accessed only by producer node* head_; // head of the queue node* first_; // last unused node (tail of node cache) node* tail_copy_; // helper (points somewhere between first_ and tail_) node* alloc_node() { // first tries to allocate node from internal node cache, // if attempt fails, allocates node via ::operator new() if (first_ != tail_copy_) { node* n = first_; first_ = first_->next_; return n; } tail_copy_ = load_consume(&tail_); if (first_ != tail_copy_) { node* n = first_; first_ = first_->next_; return n; } node* n = new node; return n; } spsc_queue(spsc_queue const&); spsc_queue& operator = (spsc_queue const&); }; readerwriterqueue-1.0.6/benchmarks/ext/folly/000077500000000000000000000000001410602521300213165ustar00rootroot00000000000000readerwriterqueue-1.0.6/benchmarks/ext/folly/ProducerConsumerQueue.h000066400000000000000000000114261410602521300257770ustar00rootroot00000000000000// Adapted from https://github.com/facebook/folly/blob/master/folly/ProducerConsumerQueue.h /* * Copyright 2013 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // @author Bo Hu (bhu@fb.com) // @author Jordan DeLong (delong.j@fb.com) #ifndef PRODUCER_CONSUMER_QUEUE_H_ #define PRODUCER_CONSUMER_QUEUE_H_ #include #include #include #include #include #include #include //#include namespace folly { /* * ProducerConsumerQueue is a one producer and one consumer queue * without locks. */ template struct ProducerConsumerQueue { typedef T value_type; // size must be >= 1. explicit ProducerConsumerQueue(uint32_t size) : size_(size + 1) // +1 because one slot is always empty , records_(static_cast(std::malloc(sizeof(T) * (size + 1)))) , readIndex_(0) , writeIndex_(0) { assert(size >= 1); if (!records_) { throw std::bad_alloc(); } } ~ProducerConsumerQueue() { // We need to destruct anything that may still exist in our queue. // (No real synchronization needed at destructor time: only one // thread can be doing this.) if (!std::is_trivially_destructible::value) { int read = readIndex_; int end = writeIndex_; while (read != end) { records_[read].~T(); if (++read == size_) { read = 0; } } } std::free(records_); } template bool enqueue(Args&&... recordArgs) { auto const currentWrite = writeIndex_.load(std::memory_order_relaxed); auto nextRecord = currentWrite + 1; if (nextRecord == size_) { nextRecord = 0; } if (nextRecord != readIndex_.load(std::memory_order_acquire)) { new (&records_[currentWrite]) T(std::forward(recordArgs)...); writeIndex_.store(nextRecord, std::memory_order_release); return true; } // queue is full return false; } // move (or copy) the value at the front of the queue to given variable bool try_dequeue(T& record) { auto const currentRead = readIndex_.load(std::memory_order_relaxed); if (currentRead == writeIndex_.load(std::memory_order_acquire)) { // queue is empty return false; } auto nextRecord = currentRead + 1; if (nextRecord == size_) { nextRecord = 0; } record = std::move(records_[currentRead]); records_[currentRead].~T(); readIndex_.store(nextRecord, std::memory_order_release); return true; } // pointer to the value at the front of the queue (for use in-place) or // nullptr if empty. T* frontPtr() { auto const currentRead = readIndex_.load(std::memory_order_relaxed); if (currentRead == writeIndex_.load(std::memory_order_acquire)) { // queue is empty return nullptr; } return &records_[currentRead]; } // queue must not be empty void popFront() { auto const currentRead = readIndex_.load(std::memory_order_relaxed); assert(currentRead != writeIndex_.load(std::memory_order_acquire)); auto nextRecord = currentRead + 1; if (nextRecord == size_) { nextRecord = 0; } records_[currentRead].~T(); readIndex_.store(nextRecord, std::memory_order_release); } bool isEmpty() const { return readIndex_.load(std::memory_order_consume) == writeIndex_.load(std::memory_order_consume); } bool isFull() const { auto nextRecord = writeIndex_.load(std::memory_order_consume) + 1; if (nextRecord == size_) { nextRecord = 0; } if (nextRecord != readIndex_.load(std::memory_order_consume)) { return false; } // queue is full return true; } // * If called by consumer, then true size may be more (because producer may // be adding items concurrently). // * If called by producer, then true size may be less (because consumer may // be removing items concurrently). // * It is undefined to call this from any other thread. size_t sizeGuess() const { int ret = writeIndex_.load(std::memory_order_consume) - readIndex_.load(std::memory_order_consume); if (ret < 0) { ret += size_; } return ret; } private: const uint32_t size_; T* const records_; std::atomic readIndex_; std::atomic writeIndex_; }; } #endif readerwriterqueue-1.0.6/benchmarks/makefile000066400000000000000000000013101410602521300210640ustar00rootroot00000000000000# ©2014 Cameron Desrochers ifeq ($(OS),Windows_NT) EXT=.exe PLATFORM_OPTS=-static else EXT= UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Darwin) PLATFORM_OPTS= else PLATFORM_OPTS=-Wl,--no-as-needed -lrt endif endif default: benchmarks$(EXT) benchmarks$(EXT): bench.cpp ../readerwriterqueue.h ../readerwritercircularbuffer.h ../atomicops.h ext/1024cores/spscqueue.h ext/folly/ProducerConsumerQueue.h ../tests/common/simplethread.h ../tests/common/simplethread.cpp systemtime.h systemtime.cpp makefile g++ -std=c++11 -Wpedantic -Wall -DNDEBUG -O3 -g bench.cpp ../tests/common/simplethread.cpp systemtime.cpp -o benchmarks$(EXT) -pthread $(PLATFORM_OPTS) run: benchmarks$(EXT) ./benchmarks$(EXT) readerwriterqueue-1.0.6/benchmarks/msvc10/000077500000000000000000000000001410602521300205025ustar00rootroot00000000000000readerwriterqueue-1.0.6/benchmarks/msvc10/winbench-intel.vcxproj000066400000000000000000000200101410602521300250160ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9} Win32Proj winbenchintel Application true Unicode Intel C++ Compiler XE 13.0 Application true Unicode Intel C++ Compiler XE 13.0 Application false true Unicode Intel C++ Compiler XE 13.0 Application false true Unicode Intel C++ Compiler XE 13.0 true $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ true $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ false $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ false $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true readerwriterqueue-1.0.6/benchmarks/msvc10/winbench-intel.vcxproj.filters000066400000000000000000000031771410602521300265040ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files readerwriterqueue-1.0.6/benchmarks/msvc10/winbench.sln000066400000000000000000000035041410602521300230170ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench", "winbench.vcxproj", "{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench-intel", "winbench-intel.vcxproj", "{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.ActiveCfg = Debug|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.Build.0 = Debug|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.ActiveCfg = Debug|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.Build.0 = Debug|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.ActiveCfg = Release|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.Build.0 = Release|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.ActiveCfg = Release|x64 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.Build.0 = Release|x64 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.ActiveCfg = Debug|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.Build.0 = Debug|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|x64.ActiveCfg = Debug|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.ActiveCfg = Release|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.Build.0 = Release|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|x64.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal readerwriterqueue-1.0.6/benchmarks/msvc10/winbench.vcxproj000066400000000000000000000173661410602521300237310ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2} Win32Proj winbench Application true Unicode Application true Unicode Application false true Unicode Application false true Unicode true $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ true $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ false $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ false $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true readerwriterqueue-1.0.6/benchmarks/msvc10/winbench.vcxproj.filters000066400000000000000000000031771410602521300253730ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files readerwriterqueue-1.0.6/benchmarks/msvc12/000077500000000000000000000000001410602521300205045ustar00rootroot00000000000000readerwriterqueue-1.0.6/benchmarks/msvc12/winbench-intel.vcxproj000066400000000000000000000200101410602521300250200ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9} Win32Proj winbenchintel Application true Unicode Intel C++ Compiler XE 13.0 Application true Unicode Intel C++ Compiler XE 13.0 Application false true Unicode Intel C++ Compiler XE 13.0 Application false true Unicode Intel C++ Compiler XE 13.0 true $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ true $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ false $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ false $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true readerwriterqueue-1.0.6/benchmarks/msvc12/winbench-intel.vcxproj.filters000066400000000000000000000031771410602521300265060ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files readerwriterqueue-1.0.6/benchmarks/msvc12/winbench.sln000066400000000000000000000036231410602521300230230ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.30501.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench", "winbench.vcxproj", "{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench-intel", "winbench-intel.vcxproj", "{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.ActiveCfg = Debug|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.Build.0 = Debug|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.ActiveCfg = Debug|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.Build.0 = Debug|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.ActiveCfg = Release|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.Build.0 = Release|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.ActiveCfg = Release|x64 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.Build.0 = Release|x64 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.ActiveCfg = Debug|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.Build.0 = Debug|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|x64.ActiveCfg = Debug|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.ActiveCfg = Release|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.Build.0 = Release|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|x64.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal readerwriterqueue-1.0.6/benchmarks/msvc12/winbench.vcxproj000066400000000000000000000176531410602521300237320ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2} Win32Proj winbench Application true Unicode v120 Application true Unicode v120 Application false true Unicode v120 Application false true Unicode v120 true $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ true $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ false $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ false $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true readerwriterqueue-1.0.6/benchmarks/msvc12/winbench.vcxproj.filters000066400000000000000000000035021410602521300253650ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files readerwriterqueue-1.0.6/benchmarks/msvc14/000077500000000000000000000000001410602521300205065ustar00rootroot00000000000000readerwriterqueue-1.0.6/benchmarks/msvc14/winbench-intel.vcxproj000066400000000000000000000175441410602521300250440ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9} Win32Proj winbenchintel Application true Unicode Intel C++ Compiler XE 13.0 Application true Unicode Intel C++ Compiler XE 13.0 Application false true Unicode Intel C++ Compiler XE 13.0 Application false true Unicode Intel C++ Compiler XE 13.0 true $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ true $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ false $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ false $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true readerwriterqueue-1.0.6/benchmarks/msvc14/winbench-intel.vcxproj.filters000066400000000000000000000031231410602521300264770ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files readerwriterqueue-1.0.6/benchmarks/msvc14/winbench.sln000066400000000000000000000035551410602521300230310ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench", "winbench.vcxproj", "{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench-intel", "winbench-intel.vcxproj", "{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.ActiveCfg = Debug|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.Build.0 = Debug|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.ActiveCfg = Debug|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.Build.0 = Debug|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.ActiveCfg = Release|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.Build.0 = Release|Win32 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.ActiveCfg = Release|x64 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.Build.0 = Release|x64 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.ActiveCfg = Debug|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.Build.0 = Debug|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|x64.ActiveCfg = Debug|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.ActiveCfg = Release|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.Build.0 = Release|Win32 {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|x64.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal readerwriterqueue-1.0.6/benchmarks/msvc14/winbench.vcxproj000066400000000000000000000174071410602521300237310ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2} Win32Proj winbench Application true Unicode v140 Application true Unicode v140 Application false true Unicode v140 Application false true Unicode v140 true $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ true $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ false $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ false $(SolutionDir)$(Configuration)\$(Platform)\ obj\$(Configuration)\$(Platform)\ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true readerwriterqueue-1.0.6/benchmarks/msvc14/winbench.vcxproj.filters000066400000000000000000000034231410602521300253710ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files readerwriterqueue-1.0.6/benchmarks/systemtime.cpp000066400000000000000000000050741410602521300223060ustar00rootroot00000000000000// ©2013-2014 Cameron Desrochers #include "systemtime.h" #include #if defined(_MSC_VER) && _MSC_VER < 1700 #include #define CompilerMemBar() _ReadWriteBarrier() #else #include #define CompilerMemBar() std::atomic_signal_fence(std::memory_order_seq_cst) #endif #if defined(ST_WINDOWS) #include namespace moodycamel { void sleep(int milliseconds) { ::Sleep(milliseconds); } SystemTime getSystemTime() { LARGE_INTEGER t; CompilerMemBar(); if (!QueryPerformanceCounter(&t)) { return static_cast(-1); } CompilerMemBar(); return static_cast(t.QuadPart); } double getTimeDelta(SystemTime start) { LARGE_INTEGER t; CompilerMemBar(); if (start == static_cast(-1) || !QueryPerformanceCounter(&t)) { return -1; } CompilerMemBar(); auto now = static_cast(t.QuadPart); LARGE_INTEGER f; if (!QueryPerformanceFrequency(&f)) { return -1; } return static_cast(static_cast<__int64>(now - start)) / f.QuadPart * 1000; } } // end namespace moodycamel #elif defined(ST_APPLE) #include #include #include #include namespace moodycamel { void sleep(int milliseconds) { ::usleep(milliseconds * 1000); } SystemTime getSystemTime() { CompilerMemBar(); std::uint64_t result = mach_absolute_time(); CompilerMemBar(); return result; } double getTimeDelta(SystemTime start) { CompilerMemBar(); std::uint64_t end = mach_absolute_time(); CompilerMemBar(); mach_timebase_info_data_t tb = { 0 }; mach_timebase_info(&tb); double toNano = static_cast(tb.numer) / tb.denom; return static_cast(end - start) * toNano * 0.000001; } } // end namespace moodycamel #elif defined(ST_NIX) #include namespace moodycamel { void sleep(int milliseconds) { ::usleep(milliseconds * 1000); } SystemTime getSystemTime() { timespec t; CompilerMemBar(); if (clock_gettime(CLOCK_MONOTONIC_RAW, &t) != 0) { t.tv_sec = (time_t)-1; t.tv_nsec = -1; } CompilerMemBar(); return t; } double getTimeDelta(SystemTime start) { timespec t; CompilerMemBar(); if ((start.tv_sec == (time_t)-1 && start.tv_nsec == -1) || clock_gettime(CLOCK_MONOTONIC_RAW, &t) != 0) { return -1; } CompilerMemBar(); return static_cast(static_cast(t.tv_sec) - static_cast(start.tv_sec)) * 1000 + double(t.tv_nsec - start.tv_nsec) / 1000000; } } // end namespace moodycamel #endif readerwriterqueue-1.0.6/benchmarks/systemtime.h000066400000000000000000000013731410602521300217510ustar00rootroot00000000000000// ©2013-2014 Cameron Desrochers #pragma once #if defined(_WIN32) #define ST_WINDOWS #elif defined(__APPLE__) && defined(__MACH__) #define ST_APPLE #elif defined(__linux__) || defined(__FreeBSD__) || defined(BSD) #define ST_NIX #else #error "Unknown platform" #endif #if defined(ST_WINDOWS) namespace moodycamel { typedef unsigned long long SystemTime; } #elif defined(ST_APPLE) #include namespace moodycamel { typedef std::uint64_t SystemTime; } #elif defined(ST_NIX) #include namespace moodycamel { typedef timespec SystemTime; } #endif namespace moodycamel { void sleep(int milliseconds); SystemTime getSystemTime(); // Returns the delta time, in milliseconds double getTimeDelta(SystemTime start); } readerwriterqueue-1.0.6/readerwritercircularbuffer.h000066400000000000000000000251641410602521300230530ustar00rootroot00000000000000// ©2020 Cameron Desrochers. // Distributed under the simplified BSD license (see the license file that // should have come with this header). // Provides a C++11 implementation of a single-producer, single-consumer wait-free concurrent // circular buffer (fixed-size queue). #pragma once #include #include #include #include #include #include // Note that this implementation is fully modern C++11 (not compatible with old MSVC versions) // but we still include atomicops.h for its LightweightSemaphore implementation. #include "atomicops.h" #ifndef MOODYCAMEL_CACHE_LINE_SIZE #define MOODYCAMEL_CACHE_LINE_SIZE 64 #endif namespace moodycamel { template class BlockingReaderWriterCircularBuffer { public: typedef T value_type; public: explicit BlockingReaderWriterCircularBuffer(std::size_t capacity) : maxcap(capacity), mask(), rawData(), data(), slots_(new spsc_sema::LightweightSemaphore(static_cast(capacity))), items(new spsc_sema::LightweightSemaphore(0)), nextSlot(0), nextItem(0) { // Round capacity up to power of two to compute modulo mask. // Adapted from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 --capacity; capacity |= capacity >> 1; capacity |= capacity >> 2; capacity |= capacity >> 4; for (std::size_t i = 1; i < sizeof(std::size_t); i <<= 1) capacity |= capacity >> (i << 3); mask = capacity++; rawData = static_cast(std::malloc(capacity * sizeof(T) + std::alignment_of::value - 1)); data = align_for(rawData); } BlockingReaderWriterCircularBuffer(BlockingReaderWriterCircularBuffer&& other) : maxcap(0), mask(0), rawData(nullptr), data(nullptr), slots_(new spsc_sema::LightweightSemaphore(0)), items(new spsc_sema::LightweightSemaphore(0)), nextSlot(), nextItem() { swap(other); } BlockingReaderWriterCircularBuffer(BlockingReaderWriterCircularBuffer const&) = delete; // Note: The queue should not be accessed concurrently while it's // being deleted. It's up to the user to synchronize this. ~BlockingReaderWriterCircularBuffer() { for (std::size_t i = 0, n = items->availableApprox(); i != n; ++i) reinterpret_cast(data)[(nextItem + i) & mask].~T(); std::free(rawData); } BlockingReaderWriterCircularBuffer& operator=(BlockingReaderWriterCircularBuffer&& other) noexcept { swap(other); return *this; } BlockingReaderWriterCircularBuffer& operator=(BlockingReaderWriterCircularBuffer const&) = delete; // Swaps the contents of this buffer with the contents of another. // Not thread-safe. void swap(BlockingReaderWriterCircularBuffer& other) noexcept { std::swap(maxcap, other.maxcap); std::swap(mask, other.mask); std::swap(rawData, other.rawData); std::swap(data, other.data); std::swap(slots_, other.slots_); std::swap(items, other.items); std::swap(nextSlot, other.nextSlot); std::swap(nextItem, other.nextItem); } // Enqueues a single item (by copying it). // Fails if not enough room to enqueue. // Thread-safe when called by producer thread. // No exception guarantee (state will be corrupted) if constructor of T throws. bool try_enqueue(T const& item) { if (!slots_->tryWait()) return false; inner_enqueue(item); return true; } // Enqueues a single item (by moving it, if possible). // Fails if not enough room to enqueue. // Thread-safe when called by producer thread. // No exception guarantee (state will be corrupted) if constructor of T throws. bool try_enqueue(T&& item) { if (!slots_->tryWait()) return false; inner_enqueue(std::move(item)); return true; } // Blocks the current thread until there's enough space to enqueue the given item, // then enqueues it (via copy). // Thread-safe when called by producer thread. // No exception guarantee (state will be corrupted) if constructor of T throws. void wait_enqueue(T const& item) { while (!slots_->wait()); inner_enqueue(item); } // Blocks the current thread until there's enough space to enqueue the given item, // then enqueues it (via move, if possible). // Thread-safe when called by producer thread. // No exception guarantee (state will be corrupted) if constructor of T throws. void wait_enqueue(T&& item) { while (!slots_->wait()); inner_enqueue(std::move(item)); } // Blocks the current thread until there's enough space to enqueue the given item, // or the timeout expires. Returns false without enqueueing the item if the timeout // expires, otherwise enqueues the item (via copy) and returns true. // Thread-safe when called by producer thread. // No exception guarantee (state will be corrupted) if constructor of T throws. bool wait_enqueue_timed(T const& item, std::int64_t timeout_usecs) { if (!slots_->wait(timeout_usecs)) return false; inner_enqueue(item); return true; } // Blocks the current thread until there's enough space to enqueue the given item, // or the timeout expires. Returns false without enqueueing the item if the timeout // expires, otherwise enqueues the item (via move, if possible) and returns true. // Thread-safe when called by producer thread. // No exception guarantee (state will be corrupted) if constructor of T throws. bool wait_enqueue_timed(T&& item, std::int64_t timeout_usecs) { if (!slots_->wait(timeout_usecs)) return false; inner_enqueue(std::move(item)); return true; } // Blocks the current thread until there's enough space to enqueue the given item, // or the timeout expires. Returns false without enqueueing the item if the timeout // expires, otherwise enqueues the item (via copy) and returns true. // Thread-safe when called by producer thread. // No exception guarantee (state will be corrupted) if constructor of T throws. template inline bool wait_enqueue_timed(T const& item, std::chrono::duration const& timeout) { return wait_enqueue_timed(item, std::chrono::duration_cast(timeout).count()); } // Blocks the current thread until there's enough space to enqueue the given item, // or the timeout expires. Returns false without enqueueing the item if the timeout // expires, otherwise enqueues the item (via move, if possible) and returns true. // Thread-safe when called by producer thread. // No exception guarantee (state will be corrupted) if constructor of T throws. template inline bool wait_enqueue_timed(T&& item, std::chrono::duration const& timeout) { return wait_enqueue_timed(std::move(item), std::chrono::duration_cast(timeout).count()); } // Attempts to dequeue a single item. // Returns false if the buffer is empty. // Thread-safe when called by consumer thread. // No exception guarantee (state will be corrupted) if assignment operator of U throws. template bool try_dequeue(U& item) { if (!items->tryWait()) return false; inner_dequeue(item); return true; } // Blocks the current thread until there's something to dequeue, then dequeues it. // Thread-safe when called by consumer thread. // No exception guarantee (state will be corrupted) if assignment operator of U throws. template void wait_dequeue(U& item) { while (!items->wait()); inner_dequeue(item); } // Blocks the current thread until either there's something to dequeue // or the timeout expires. Returns false without setting `item` if the // timeout expires, otherwise assigns to `item` and returns true. // Thread-safe when called by consumer thread. // No exception guarantee (state will be corrupted) if assignment operator of U throws. template bool wait_dequeue_timed(U& item, std::int64_t timeout_usecs) { if (!items->wait(timeout_usecs)) return false; inner_dequeue(item); return true; } // Blocks the current thread until either there's something to dequeue // or the timeout expires. Returns false without setting `item` if the // timeout expires, otherwise assigns to `item` and returns true. // Thread-safe when called by consumer thread. // No exception guarantee (state will be corrupted) if assignment operator of U throws. template inline bool wait_dequeue_timed(U& item, std::chrono::duration const& timeout) { return wait_dequeue_timed(item, std::chrono::duration_cast(timeout).count()); } // Returns a (possibly outdated) snapshot of the total number of elements currently in the buffer. // Thread-safe. inline std::size_t size_approx() const { return items->availableApprox(); } // Returns the maximum number of elements that this circular buffer can hold at once. // Thread-safe. inline std::size_t max_capacity() const { return maxcap; } private: template void inner_enqueue(U&& item) { std::size_t i = nextSlot++; new (reinterpret_cast(data) + (i & mask)) T(std::forward(item)); items->signal(); } template void inner_dequeue(U& item) { std::size_t i = nextItem++; T& element = reinterpret_cast(data)[i & mask]; item = std::move(element); element.~T(); slots_->signal(); } template static inline char* align_for(char* ptr) { const std::size_t alignment = std::alignment_of::value; return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; } private: std::size_t maxcap; // actual (non-power-of-two) capacity std::size_t mask; // circular buffer capacity mask (for cheap modulo) char* rawData; // raw circular buffer memory char* data; // circular buffer memory aligned to element alignment std::unique_ptr slots_; // number of slots currently free (named with underscore to accommodate Qt's 'slots' macro) std::unique_ptr items; // number of elements currently enqueued char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(char*) * 2 - sizeof(std::size_t) * 2 - sizeof(std::unique_ptr) * 2]; std::size_t nextSlot; // index of next free slot to enqueue into char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(std::size_t)]; std::size_t nextItem; // index of next element to dequeue from }; } readerwriterqueue-1.0.6/readerwriterqueue.h000066400000000000000000000776041410602521300212070ustar00rootroot00000000000000// ©2013-2020 Cameron Desrochers. // Distributed under the simplified BSD license (see the license file that // should have come with this header). #pragma once #include "atomicops.h" #include #include #include #include #include #include #include #include // For malloc/free/abort & size_t #include #if __cplusplus > 199711L || _MSC_VER >= 1700 // C++11 or VS2012 #include #endif // A lock-free queue for a single-consumer, single-producer architecture. // The queue is also wait-free in the common path (except if more memory // needs to be allocated, in which case malloc is called). // Allocates memory sparingly, and only once if the original maximum size // estimate is never exceeded. // Tested on x86/x64 processors, but semantics should be correct for all // architectures (given the right implementations in atomicops.h), provided // that aligned integer and pointer accesses are naturally atomic. // Note that there should only be one consumer thread and producer thread; // Switching roles of the threads, or using multiple consecutive threads for // one role, is not safe unless properly synchronized. // Using the queue exclusively from one thread is fine, though a bit silly. #ifndef MOODYCAMEL_CACHE_LINE_SIZE #define MOODYCAMEL_CACHE_LINE_SIZE 64 #endif #ifndef MOODYCAMEL_EXCEPTIONS_ENABLED #if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__)) #define MOODYCAMEL_EXCEPTIONS_ENABLED #endif #endif #ifndef MOODYCAMEL_HAS_EMPLACE #if !defined(_MSC_VER) || _MSC_VER >= 1800 // variadic templates: either a non-MS compiler or VS >= 2013 #define MOODYCAMEL_HAS_EMPLACE 1 #endif #endif #ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE #if defined (__APPLE__) && defined (__MACH__) && __cplusplus >= 201703L // This is required to find out what deployment target we are using #include #if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_14 // C++17 new(size_t, align_val_t) is not backwards-compatible with older versions of macOS, so we can't support over-alignment in this case #define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE #endif #endif #endif #ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE #define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE AE_ALIGN(MOODYCAMEL_CACHE_LINE_SIZE) #endif #ifdef AE_VCPP #pragma warning(push) #pragma warning(disable: 4324) // structure was padded due to __declspec(align()) #pragma warning(disable: 4820) // padding was added #pragma warning(disable: 4127) // conditional expression is constant #endif namespace moodycamel { template class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue { // Design: Based on a queue-of-queues. The low-level queues are just // circular buffers with front and tail indices indicating where the // next element to dequeue is and where the next element can be enqueued, // respectively. Each low-level queue is called a "block". Each block // wastes exactly one element's worth of space to keep the design simple // (if front == tail then the queue is empty, and can't be full). // The high-level queue is a circular linked list of blocks; again there // is a front and tail, but this time they are pointers to the blocks. // The front block is where the next element to be dequeued is, provided // the block is not empty. The back block is where elements are to be // enqueued, provided the block is not full. // The producer thread owns all the tail indices/pointers. The consumer // thread owns all the front indices/pointers. Both threads read each // other's variables, but only the owning thread updates them. E.g. After // the consumer reads the producer's tail, the tail may change before the // consumer is done dequeuing an object, but the consumer knows the tail // will never go backwards, only forwards. // If there is no room to enqueue an object, an additional block (of // equal size to the last block) is added. Blocks are never removed. public: typedef T value_type; // Constructs a queue that can hold at least `size` elements without further // allocations. If more than MAX_BLOCK_SIZE elements are requested, // then several blocks of MAX_BLOCK_SIZE each are reserved (including // at least one extra buffer block). AE_NO_TSAN explicit ReaderWriterQueue(size_t size = 15) #ifndef NDEBUG : enqueuing(false) ,dequeuing(false) #endif { assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) && "MAX_BLOCK_SIZE must be a power of 2"); assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2"); Block* firstBlock = nullptr; largestBlockSize = ceilToPow2(size + 1); // We need a spare slot to fit size elements in the block if (largestBlockSize > MAX_BLOCK_SIZE * 2) { // We need a spare block in case the producer is writing to a different block the consumer is reading from, and // wants to enqueue the maximum number of elements. We also need a spare element in each block to avoid the ambiguity // between front == tail meaning "empty" and "full". // So the effective number of slots that are guaranteed to be usable at any time is the block size - 1 times the // number of blocks - 1. Solving for size and applying a ceiling to the division gives us (after simplifying): size_t initialBlockCount = (size + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1); largestBlockSize = MAX_BLOCK_SIZE; Block* lastBlock = nullptr; for (size_t i = 0; i != initialBlockCount; ++i) { auto block = make_block(largestBlockSize); if (block == nullptr) { #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED throw std::bad_alloc(); #else abort(); #endif } if (firstBlock == nullptr) { firstBlock = block; } else { lastBlock->next = block; } lastBlock = block; block->next = firstBlock; } } else { firstBlock = make_block(largestBlockSize); if (firstBlock == nullptr) { #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED throw std::bad_alloc(); #else abort(); #endif } firstBlock->next = firstBlock; } frontBlock = firstBlock; tailBlock = firstBlock; // Make sure the reader/writer threads will have the initialized memory setup above: fence(memory_order_sync); } // Note: The queue should not be accessed concurrently while it's // being moved. It's up to the user to synchronize this. AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue&& other) : frontBlock(other.frontBlock.load()), tailBlock(other.tailBlock.load()), largestBlockSize(other.largestBlockSize) #ifndef NDEBUG ,enqueuing(false) ,dequeuing(false) #endif { other.largestBlockSize = 32; Block* b = other.make_block(other.largestBlockSize); if (b == nullptr) { #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED throw std::bad_alloc(); #else abort(); #endif } b->next = b; other.frontBlock = b; other.tailBlock = b; } // Note: The queue should not be accessed concurrently while it's // being moved. It's up to the user to synchronize this. ReaderWriterQueue& operator=(ReaderWriterQueue&& other) AE_NO_TSAN { Block* b = frontBlock.load(); frontBlock = other.frontBlock.load(); other.frontBlock = b; b = tailBlock.load(); tailBlock = other.tailBlock.load(); other.tailBlock = b; std::swap(largestBlockSize, other.largestBlockSize); return *this; } // Note: The queue should not be accessed concurrently while it's // being deleted. It's up to the user to synchronize this. AE_NO_TSAN ~ReaderWriterQueue() { // Make sure we get the latest version of all variables from other CPUs: fence(memory_order_sync); // Destroy any remaining objects in queue and free memory Block* frontBlock_ = frontBlock; Block* block = frontBlock_; do { Block* nextBlock = block->next; size_t blockFront = block->front; size_t blockTail = block->tail; for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) { auto element = reinterpret_cast(block->data + i * sizeof(T)); element->~T(); (void)element; } auto rawBlock = block->rawThis; block->~Block(); std::free(rawBlock); block = nextBlock; } while (block != frontBlock_); } // 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 { return inner_enqueue(element); } // 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 { return inner_enqueue(std::forward(element)); } #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 { return inner_enqueue(std::forward(args)...); } #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 { return inner_enqueue(element); } // 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 { return inner_enqueue(std::forward(element)); } #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 { return inner_enqueue(std::forward(args)...); } #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 { #ifndef NDEBUG ReentrantGuard guard(this->dequeuing); #endif // High-level pseudocode: // Remember where the tail block is // If the front block has an element in it, dequeue it // Else // If front block was the tail block when we entered the function, return false // Else advance to next block and dequeue the item there // Note that we have to use the value of the tail block from before we check if the front // block is full or not, in case the front block is empty and then, before we check if the // tail block is at the front block or not, the producer fills up the front block *and // moves on*, which would make us skip a filled block. Seems unlikely, but was consistently // reproducible in practice. // In order to avoid overhead in the common case, though, we do a double-checked pattern // where we have the fast path if the front block is not empty, then read the tail block, // then re-read the front block and check if it's not empty again, then check if the tail // block has advanced. Block* frontBlock_ = frontBlock.load(); size_t blockTail = frontBlock_->localTail; size_t blockFront = frontBlock_->front.load(); if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { fence(memory_order_acquire); non_empty_front_block: // Front block not empty, dequeue from here auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); result = std::move(*element); element->~T(); blockFront = (blockFront + 1) & frontBlock_->sizeMask; fence(memory_order_release); frontBlock_->front = blockFront; } else if (frontBlock_ != tailBlock.load()) { fence(memory_order_acquire); frontBlock_ = frontBlock.load(); blockTail = frontBlock_->localTail = frontBlock_->tail.load(); blockFront = frontBlock_->front.load(); fence(memory_order_acquire); if (blockFront != blockTail) { // Oh look, the front block isn't empty after all goto non_empty_front_block; } // Front block is empty but there's another block ahead, advance to it Block* nextBlock = frontBlock_->next; // Don't need an acquire fence here since next can only ever be set on the tailBlock, // and we're not the tailBlock, and we did an acquire earlier after reading tailBlock which // ensures next is up-to-date on this CPU in case we recently were at tailBlock. size_t nextBlockFront = nextBlock->front.load(); size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); fence(memory_order_acquire); // Since the tailBlock is only ever advanced after being written to, // we know there's for sure an element to dequeue on it assert(nextBlockFront != nextBlockTail); AE_UNUSED(nextBlockTail); // We're done with this block, let the producer use it if it needs fence(memory_order_release); // Expose possibly pending changes to frontBlock->front from last dequeue frontBlock = frontBlock_ = nextBlock; compiler_fence(memory_order_release); // Not strictly needed auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); result = std::move(*element); element->~T(); nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; fence(memory_order_release); frontBlock_->front = nextBlockFront; } else { // No elements in current block and no other block to advance to return false; } return true; } // 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. T* peek() const AE_NO_TSAN { #ifndef NDEBUG ReentrantGuard guard(this->dequeuing); #endif // See try_dequeue() for reasoning Block* frontBlock_ = frontBlock.load(); size_t blockTail = frontBlock_->localTail; size_t blockFront = frontBlock_->front.load(); if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { fence(memory_order_acquire); non_empty_front_block: return reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); } else if (frontBlock_ != tailBlock.load()) { fence(memory_order_acquire); frontBlock_ = frontBlock.load(); blockTail = frontBlock_->localTail = frontBlock_->tail.load(); blockFront = frontBlock_->front.load(); fence(memory_order_acquire); if (blockFront != blockTail) { goto non_empty_front_block; } Block* nextBlock = frontBlock_->next; size_t nextBlockFront = nextBlock->front.load(); fence(memory_order_acquire); assert(nextBlockFront != nextBlock->tail.load()); return reinterpret_cast(nextBlock->data + nextBlockFront * sizeof(T)); } return nullptr; } // 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. bool pop() AE_NO_TSAN { #ifndef NDEBUG ReentrantGuard guard(this->dequeuing); #endif // See try_dequeue() for reasoning Block* frontBlock_ = frontBlock.load(); size_t blockTail = frontBlock_->localTail; size_t blockFront = frontBlock_->front.load(); if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { fence(memory_order_acquire); non_empty_front_block: auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); element->~T(); blockFront = (blockFront + 1) & frontBlock_->sizeMask; fence(memory_order_release); frontBlock_->front = blockFront; } else if (frontBlock_ != tailBlock.load()) { fence(memory_order_acquire); frontBlock_ = frontBlock.load(); blockTail = frontBlock_->localTail = frontBlock_->tail.load(); blockFront = frontBlock_->front.load(); fence(memory_order_acquire); if (blockFront != blockTail) { goto non_empty_front_block; } // Front block is empty but there's another block ahead, advance to it Block* nextBlock = frontBlock_->next; size_t nextBlockFront = nextBlock->front.load(); size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); fence(memory_order_acquire); assert(nextBlockFront != nextBlockTail); AE_UNUSED(nextBlockTail); fence(memory_order_release); frontBlock = frontBlock_ = nextBlock; compiler_fence(memory_order_release); auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); element->~T(); nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; fence(memory_order_release); frontBlock_->front = nextBlockFront; } else { // No elements in current block and no other block to advance to return false; } return true; } // Returns the approximate number of items currently in the queue. // Safe to call from both the producer and consumer threads. inline size_t size_approx() const AE_NO_TSAN { size_t result = 0; Block* frontBlock_ = frontBlock.load(); Block* block = frontBlock_; do { fence(memory_order_acquire); size_t blockFront = block->front.load(); size_t blockTail = block->tail.load(); result += (blockTail - blockFront) & block->sizeMask; block = block->next.load(); } while (block != frontBlock_); return result; } // 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. inline size_t max_capacity() const { size_t result = 0; Block* frontBlock_ = frontBlock.load(); Block* block = frontBlock_; do { fence(memory_order_acquire); result += block->sizeMask; block = block->next.load(); } while (block != frontBlock_); return result; } private: enum AllocationMode { CanAlloc, CannotAlloc }; #if MOODYCAMEL_HAS_EMPLACE template bool inner_enqueue(Args&&... args) AE_NO_TSAN #else template bool inner_enqueue(U&& element) AE_NO_TSAN #endif { #ifndef NDEBUG ReentrantGuard guard(this->enqueuing); #endif // High-level pseudocode (assuming we're allowed to alloc a new block): // If room in tail block, add to tail // Else check next block // If next block is not the head block, enqueue on next block // Else create a new block and enqueue there // Advance tail to the block we just enqueued to Block* tailBlock_ = tailBlock.load(); size_t blockFront = tailBlock_->localFront; size_t blockTail = tailBlock_->tail.load(); size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask; if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) { fence(memory_order_acquire); // This block has room for at least one more element char* location = tailBlock_->data + blockTail * sizeof(T); #if MOODYCAMEL_HAS_EMPLACE new (location) T(std::forward(args)...); #else new (location) T(std::forward(element)); #endif fence(memory_order_release); tailBlock_->tail = nextBlockTail; } else { fence(memory_order_acquire); if (tailBlock_->next.load() != frontBlock) { // Note that the reason we can't advance to the frontBlock and start adding new entries there // is because if we did, then dequeue would stay in that block, eventually reading the new values, // instead of advancing to the next full block (whose values were enqueued first and so should be // consumed first). fence(memory_order_acquire); // Ensure we get latest writes if we got the latest frontBlock // tailBlock is full, but there's a free block ahead, use it Block* tailBlockNext = tailBlock_->next.load(); size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load(); nextBlockTail = tailBlockNext->tail.load(); fence(memory_order_acquire); // This block must be empty since it's not the head block and we // go through the blocks in a circle assert(nextBlockFront == nextBlockTail); tailBlockNext->localFront = nextBlockFront; char* location = tailBlockNext->data + nextBlockTail * sizeof(T); #if MOODYCAMEL_HAS_EMPLACE new (location) T(std::forward(args)...); #else new (location) T(std::forward(element)); #endif tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask; fence(memory_order_release); tailBlock = tailBlockNext; } else if (canAlloc == CanAlloc) { // tailBlock is full and there's no free block ahead; create a new block auto newBlockSize = largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2; auto newBlock = make_block(newBlockSize); if (newBlock == nullptr) { // Could not allocate a block! return false; } largestBlockSize = newBlockSize; #if MOODYCAMEL_HAS_EMPLACE new (newBlock->data) T(std::forward(args)...); #else new (newBlock->data) T(std::forward(element)); #endif assert(newBlock->front == 0); newBlock->tail = newBlock->localTail = 1; newBlock->next = tailBlock_->next.load(); tailBlock_->next = newBlock; // Might be possible for the dequeue thread to see the new tailBlock->next // *without* seeing the new tailBlock value, but this is OK since it can't // advance to the next block until tailBlock is set anyway (because the only // case where it could try to read the next is if it's already at the tailBlock, // and it won't advance past tailBlock in any circumstance). fence(memory_order_release); tailBlock = newBlock; } else if (canAlloc == CannotAlloc) { // Would have had to allocate a new block to enqueue, but not allowed return false; } else { assert(false && "Should be unreachable code"); return false; } } return true; } // Disable copying ReaderWriterQueue(ReaderWriterQueue const&) { } // Disable assignment ReaderWriterQueue& operator=(ReaderWriterQueue const&) { } AE_FORCEINLINE static size_t ceilToPow2(size_t x) { // From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 --x; x |= x >> 1; x |= x >> 2; x |= x >> 4; for (size_t i = 1; i < sizeof(size_t); i <<= 1) { x |= x >> (i << 3); } ++x; return x; } template static AE_FORCEINLINE char* align_for(char* ptr) AE_NO_TSAN { const std::size_t alignment = std::alignment_of::value; return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; } private: #ifndef NDEBUG struct ReentrantGuard { AE_NO_TSAN ReentrantGuard(weak_atomic& _inSection) : inSection(_inSection) { assert(!inSection && "Concurrent (or re-entrant) enqueue or dequeue operation detected (only one thread at a time may hold the producer or consumer role)"); inSection = true; } AE_NO_TSAN ~ReentrantGuard() { inSection = false; } private: ReentrantGuard& operator=(ReentrantGuard const&); private: weak_atomic& inSection; }; #endif struct Block { // Avoid false-sharing by putting highly contended variables on their own cache lines weak_atomic front; // (Atomic) Elements are read from here size_t localTail; // An uncontended shadow copy of tail, owned by the consumer char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic) - sizeof(size_t)]; weak_atomic tail; // (Atomic) Elements are enqueued here size_t localFront; char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic) - sizeof(size_t)]; // next isn't very contended, but we don't want it on the same cache line as tail (which is) weak_atomic next; // (Atomic) char* data; // Contents (on heap) are aligned to T's alignment const size_t sizeMask; // size must be a power of two (and greater than 0) AE_NO_TSAN Block(size_t const& _size, char* _rawThis, char* _data) : front(0UL), localTail(0), tail(0UL), localFront(0), next(nullptr), data(_data), sizeMask(_size - 1), rawThis(_rawThis) { } private: // C4512 - Assignment operator could not be generated Block& operator=(Block const&); public: char* rawThis; }; static Block* make_block(size_t capacity) AE_NO_TSAN { // Allocate enough memory for the block itself, as well as all the elements it will contain auto size = sizeof(Block) + std::alignment_of::value - 1; size += sizeof(T) * capacity + std::alignment_of::value - 1; auto newBlockRaw = static_cast(std::malloc(size)); if (newBlockRaw == nullptr) { return nullptr; } auto newBlockAligned = align_for(newBlockRaw); auto newBlockData = align_for(newBlockAligned + sizeof(Block)); return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData); } private: weak_atomic frontBlock; // (Atomic) Elements are dequeued from this block char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic)]; weak_atomic tailBlock; // (Atomic) Elements are enqueued to this block size_t largestBlockSize; #ifndef NDEBUG weak_atomic enqueuing; mutable weak_atomic dequeuing; #endif }; // Like ReaderWriterQueue, but also providees blocking operations template class BlockingReaderWriterQueue { private: typedef ::moodycamel::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 #pragma warning(pop) #endif readerwriterqueue-1.0.6/tests/000077500000000000000000000000001410602521300164165ustar00rootroot00000000000000readerwriterqueue-1.0.6/tests/common/000077500000000000000000000000001410602521300177065ustar00rootroot00000000000000readerwriterqueue-1.0.6/tests/common/simplethread.cpp000066400000000000000000000033301410602521300230720ustar00rootroot00000000000000#include "simplethread.h" #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #include struct SimpleThread::ThreadRef { HANDLE handle; static DWORD WINAPI ThreadProc(LPVOID param) { auto threadRef = static_cast(param); threadRef->callbackFunc(threadRef->callbackObj); return 0; } ThreadRef(void* callbackObj, CallbackFunc callbackFunc) : callbackObj(callbackObj), callbackFunc(callbackFunc) { } void* callbackObj; CallbackFunc callbackFunc; }; void SimpleThread::startThread(void* callbackObj, CallbackFunc callbackFunc) { thread = new ThreadRef(callbackObj, callbackFunc); thread->handle = CreateThread(NULL, StackSize, &ThreadRef::ThreadProc, thread, 0, NULL); } void SimpleThread::join() { if (thread != nullptr && thread->handle != NULL) { WaitForSingleObject(thread->handle, INFINITE); CloseHandle(thread->handle); thread->handle = NULL; } } #else #include struct SimpleThread::ThreadRef { std::thread thread; static void threadProc(ThreadRef* threadRef) { threadRef->callbackFunc(threadRef->callbackObj); } ThreadRef(void* callbackObj, CallbackFunc callbackFunc) : callbackObj(callbackObj), callbackFunc(callbackFunc) { } void* callbackObj; CallbackFunc callbackFunc; }; void SimpleThread::startThread(void* callbackObj, CallbackFunc callbackFunc) { thread = new ThreadRef(callbackObj, callbackFunc); thread->thread = std::thread(&ThreadRef::threadProc, thread); } void SimpleThread::join() { if (thread != nullptr && thread->thread.joinable()) { thread->thread.join(); } } #endif SimpleThread::~SimpleThread() { if (thread != nullptr) { join(); delete thread; } } readerwriterqueue-1.0.6/tests/common/simplethread.h000066400000000000000000000117131410602521300225430ustar00rootroot00000000000000#pragma once // Like C++11's std::thread, but with a reduced API, and works on Windows with MSVC2010+. // Wraps std::thread on other OSes. Perhaps the most significant departure between // std::thread and this mini-library is that join() is called implicitly in the destructor, // if the thread is joinable. The thread callback functions should not throw exceptions. #include #include namespace details { template struct ArgWrapper { typename std::remove_reference::type arg1; typename std::remove_reference::type arg2; typename std::remove_reference::type arg3; template ArgWrapper(T&& a1, U&& a2, V&& a3) : arg1(std::forward(a1)), arg2(std::forward(a2)), arg3(std::forward(a3)) { } template void callCallback(TCallback&& callback) const { std::forward(callback)(std::move(arg1), std::move(arg2), std::move(arg3)); } }; template struct ArgWrapper { typename std::remove_reference::type arg1; typename std::remove_reference::type arg2; template ArgWrapper(T&& a1, U&& a2) : arg1(std::forward(a1)), arg2(std::forward(a2)) { } template void callCallback(TCallback&& callback) const { std::forward(callback)(std::move(arg1), std::move(arg2)); } }; template struct ArgWrapper { typename std::remove_reference::type arg1; template ArgWrapper(T&& a1) : arg1(std::forward(a1)) { } template void callCallback(TCallback&& callback) const { std::forward(callback)(std::move(arg1)); } }; template<> struct ArgWrapper { template void callCallback(TCallback&& callback) const { std::forward(callback)(); } }; } class SimpleThread { private: struct ThreadRef; template struct CallbackWrapper { template CallbackWrapper(TCallback&& callback, U&& args) : callback(std::forward(callback)), args(std::forward(args)) { } static void callAndDelete(void* wrapper) { auto typedWrapper = static_cast(wrapper); typedWrapper->args.callCallback(std::move(typedWrapper->callback)); delete typedWrapper; } typename std::decay::type callback; TArgs args; }; typedef void (*CallbackFunc)(void*); void startThread(void* callbackObj, CallbackFunc callbackFunc); public: static const int StackSize = 4 * 1024; // bytes SimpleThread() : thread(nullptr) { } SimpleThread(SimpleThread&& other) : thread(other.thread) { other.thread = nullptr; } SimpleThread& operator=(SimpleThread&& other) { thread = other.thread; other.thread = nullptr; return *this; } // Disable copying and copy-assignment private: SimpleThread(SimpleThread const&); SimpleThread& operator=(SimpleThread const&); public: template explicit SimpleThread(TCallback&& callback) { auto wrapper = new CallbackWrapper>( std::forward(callback), details::ArgWrapper<>() ); startThread(wrapper, &CallbackWrapper>::callAndDelete); } template explicit SimpleThread(TCallback&& callback, TArg1&& arg1) { auto wrapper = new CallbackWrapper>( std::forward(callback), details::ArgWrapper(std::forward(arg1)) ); startThread(wrapper, &CallbackWrapper>::callAndDelete); } template explicit SimpleThread(TCallback&& callback, TArg1&& arg1, TArg2&& arg2) { auto wrapper = new CallbackWrapper>( std::forward(callback), details::ArgWrapper(std::forward(arg1), std::forward(arg2)) ); startThread(wrapper, &CallbackWrapper>::callAndDelete); } template explicit SimpleThread(TCallback&& callback, TArg1&& arg1, TArg2&& arg2, TArg3&& arg3) { auto wrapper = new CallbackWrapper>( std::forward(callback), details::ArgWrapper(std::forward(arg1), std::forward(arg2), std::forward(arg3)) ); startThread(wrapper, &CallbackWrapper>::callAndDelete); } ~SimpleThread(); void join(); private: ThreadRef* thread; }; readerwriterqueue-1.0.6/tests/stabtest/000077500000000000000000000000001410602521300202475ustar00rootroot00000000000000readerwriterqueue-1.0.6/tests/stabtest/makefile000066400000000000000000000012141410602521300217450ustar00rootroot00000000000000ifeq ($(OS),Windows_NT) EXT=.exe PLATFORM_OPTS=-static PLATFORM_LD_OPTS=-Wl,--no-as-needed else UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Darwin) EXT= PLATFORM_OPTS= PLATFORM_LD_OPTS= else EXT= PLATFORM_OPTS= PLATFORM_LD_OPTS=-lrt -Wl,--no-as-needed endif endif default: stabtest$(EXT) stabtest$(EXT): stabtest.cpp ../../readerwriterqueue.h ../../atomicops.h ../common/simplethread.h ../common/simplethread.cpp makefile g++ $(PLATFORM_OPTS) -std=c++11 -Wsign-conversion -Wpedantic -Wall -DNDEBUG -O3 stabtest.cpp ../common/simplethread.cpp -o stabtest$(EXT) -pthread $(PLATFORM_LD_OPTS) run: stabtest$(EXT) ./stabtest$(EXT) readerwriterqueue-1.0.6/tests/stabtest/msvc10/000077500000000000000000000000001410602521300213605ustar00rootroot00000000000000readerwriterqueue-1.0.6/tests/stabtest/msvc10/stabtest.sln000066400000000000000000000023241410602521300237300ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stabtest", "stabtest.vcxproj", "{16E74A53-972D-4762-BC18-8946FB1EF452}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.ActiveCfg = Debug|Win32 {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.Build.0 = Debug|Win32 {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.ActiveCfg = Debug|x64 {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.Build.0 = Debug|x64 {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.ActiveCfg = Release|Win32 {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.Build.0 = Release|Win32 {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.ActiveCfg = Release|x64 {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal readerwriterqueue-1.0.6/tests/stabtest/msvc10/stabtest.vcxproj000066400000000000000000000170231410602521300246310ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {16E74A53-972D-4762-BC18-8946FB1EF452} Win32Proj stabtest Application true Unicode Application true Unicode Application false true Unicode Application false true Unicode true $(SolutionDir)$(Configuration)\$(Platform) obj\$(Configuration)\$(Platform) true $(SolutionDir)$(Configuration)\$(Platform) obj\$(Configuration)\$(Platform) false $(SolutionDir)$(Configuration)\$(Platform) obj\$(Configuration)\$(Platform) false $(SolutionDir)$(Configuration)\$(Platform) obj\$(Configuration)\$(Platform) Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true readerwriterqueue-1.0.6/tests/stabtest/msvc10/stabtest.vcxproj.filters000066400000000000000000000026231410602521300263000ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Header Files Header Files Header Files readerwriterqueue-1.0.6/tests/stabtest/msvc12/000077500000000000000000000000001410602521300213625ustar00rootroot00000000000000readerwriterqueue-1.0.6/tests/stabtest/msvc12/stabtest.sln000066400000000000000000000024431410602521300237340ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.30501.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stabtest", "stabtest.vcxproj", "{16E74A53-972D-4762-BC18-8946FB1EF452}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.ActiveCfg = Debug|Win32 {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.Build.0 = Debug|Win32 {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.ActiveCfg = Debug|x64 {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.Build.0 = Debug|x64 {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.ActiveCfg = Release|Win32 {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.Build.0 = Release|Win32 {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.ActiveCfg = Release|x64 {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal readerwriterqueue-1.0.6/tests/stabtest/msvc12/stabtest.vcxproj000066400000000000000000000173101410602521300246320ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {16E74A53-972D-4762-BC18-8946FB1EF452} Win32Proj stabtest Application true Unicode v120 Application true Unicode v120 Application false true Unicode v120 Application false true Unicode v120 true $(SolutionDir)$(Configuration)\$(Platform) obj\$(Configuration)\$(Platform) true $(SolutionDir)$(Configuration)\$(Platform) obj\$(Configuration)\$(Platform) false $(SolutionDir)$(Configuration)\$(Platform) obj\$(Configuration)\$(Platform) false $(SolutionDir)$(Configuration)\$(Platform) obj\$(Configuration)\$(Platform) Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true readerwriterqueue-1.0.6/tests/stabtest/msvc12/stabtest.vcxproj.filters000066400000000000000000000026231410602521300263020ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Header Files Header Files Header Files readerwriterqueue-1.0.6/tests/stabtest/stabtest.cpp000066400000000000000000000043051410602521300226060ustar00rootroot00000000000000#include "../../readerwriterqueue.h" #include "../common/simplethread.h" using namespace moodycamel; #include #include #include #include // rand() //#include // usleep() void unpredictableDelay(int extra = 0) { /* if ((rand() & 4095) == 0) { usleep(2000 + extra); // in microseconds }*/ } int main(int argc, char** argv) { // Disable buffering (so that when run in, e.g., Sublime Text, the output appears as it is written) std::setvbuf(stdout, nullptr, _IONBF, 0); std::printf("Running stability test for moodycamel::ReaderWriterQueue.\n"); std::printf("Logging to 'log.txt'. Press CTRL+C to quit.\n\n"); std::ofstream log("log.txt"); try { for (unsigned int i = 0; true; ++i) { log << "Test #" << i << std::endl; std::printf("Test #%d\n", i); ReaderWriterQueue q((rand() % 32) + 1); SimpleThread writer([&]() { for (unsigned long long j = 0; j < 1024ULL * 1024ULL * 32ULL; ++j) { unpredictableDelay(500); q.enqueue(j); } }); SimpleThread reader([&]() { bool canLog = true; unsigned long long element; for (unsigned long long j = 0; j < 1024ULL * 1024ULL * 32ULL;) { if (canLog && (j & (1024 * 1024 * 16 - 1)) == 0) { log << " ... iteration " << j << std::endl; std::printf(" ... iteration %llu\n", j); canLog = false; } unpredictableDelay(); if (q.try_dequeue(element)) { if (element != j) { log << " ERROR DETECTED: Expected to read " << j << " but found " << element << std::endl; std::printf(" ERROR DETECTED: Expected to read %llu but found %llu", j, element); } ++j; canLog = true; } } if (q.try_dequeue(element)) { log << " ERROR DETECTED: Expected queue to be empty" << std::endl; std::printf(" ERROR DETECTED: Expected queue to be empty\n"); } }); writer.join(); reader.join(); } } catch (std::exception const& ex) { log << " ERROR DETECTED: Exception thrown: " << ex.what() << std::endl; std::printf(" ERROR DETECTED: Exception thrown: %s\n", ex.what()); } return 0; } readerwriterqueue-1.0.6/tests/unittests/000077500000000000000000000000001410602521300204605ustar00rootroot00000000000000readerwriterqueue-1.0.6/tests/unittests/makefile000066400000000000000000000013071410602521300221610ustar00rootroot00000000000000 ifeq ($(OS),Windows_NT) EXT=.exe PLATFORM_OPTS=-static PLATFORM_LD_OPTS=-Wl,--no-as-needed else UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Darwin) EXT= PLATFORM_OPTS= PLATFORM_LD_OPTS= else EXT= PLATFORM_OPTS= PLATFORM_LD_OPTS=-lrt -Wl,--no-as-needed endif endif default: unittests$(EXT) unittests$(EXT): unittests.cpp ../../readerwriterqueue.h ../../readerwritercircularbuffer.h ../../atomicops.h ../common/simplethread.h ../common/simplethread.cpp minitest.h makefile g++ $(PLATFORM_OPTS) -std=c++11 -Wsign-conversion -Wpedantic -Wall -DNDEBUG -O3 -g unittests.cpp ../common/simplethread.cpp -o unittests$(EXT) -pthread $(PLATFORM_LD_OPTS) run: unittests$(EXT) ./unittests$(EXT) readerwriterqueue-1.0.6/tests/unittests/minitest.h000066400000000000000000000055061410602521300224730ustar00rootroot00000000000000// ©2013-2014 Cameron Desrochers. // Distributed under the simplified BSD license (see the LICENSE file that // should have come with this header). // Provides an extremely basic unit testing framework. #pragma once #include #include #include #include #include #include #ifdef __GNUG__ #include #include #endif #define REGISTER_TEST(testName) registerTest(#testName, &subclass_t::testName) #define ASSERT_OR_FAIL(expr) { if (!(expr)) { notifyTestFailed(__LINE__, #expr); return false; } } #define SUCCEED() { return true; } // Uses CRTP template class TestClass { public: static void notifyTestFailed(int line, const char* expr) { std::printf(" FAILED!\n ******* Assertion failed (line %d): %s\n\n", line, expr); } bool validateTestName(std::string const& which) const { return testMap.find(which) != testMap.end(); } void getAllTestNames(std::vector& names) const { for (auto it = testMap.cbegin(); it != testMap.cend(); ++it) { names.push_back(it->first); } } bool run(unsigned int iterations = 1) { bool success = true; for (auto it = testVec.cbegin(); it != testVec.cend(); ++it) { if (!execTest(*it, iterations)) { success = false; } } return success; } bool run(std::vector const& which, unsigned int iterations = 1) { bool success = true; for (auto it = which.begin(); it != which.end(); ++it) { if (!execTest(*testMap.find(*it), iterations)) { success = false; } } return success; } protected: typedef TSubclass subclass_t; void registerTest(const char* name, bool (subclass_t::* method)()) { testVec.push_back(std::make_pair(std::string(name), method)); testMap[std::string(name)] = method; } bool execTest(std::pair const& testRef, unsigned int iterations) { std::printf("%s::%s... \n", demangle_type_name(typeid(subclass_t).name()).c_str(), testRef.first.c_str()); bool result = true; for (unsigned int i = 0; i != iterations; ++i) { if (!(static_cast(this)->*testRef.second)()) { result = false; break; } } if (result) { std::printf(" passed\n\n"); } else { std::printf(" FAILED!\n\n"); } return result; } private: static std::string demangle_type_name(const char* name) { #ifdef __GNUG__ // Adapted from http://stackoverflow.com/a/4541470/21475 int status = -4; char* res = abi::__cxa_demangle(name, nullptr, nullptr, &status); const char* const demangled_name = (status == 0) ? res : name; std::string ret(demangled_name); std::free(res); return ret; #else return name; #endif } protected: std::vector > testVec; std::map testMap; }; readerwriterqueue-1.0.6/tests/unittests/msvc10/000077500000000000000000000000001410602521300215715ustar00rootroot00000000000000readerwriterqueue-1.0.6/tests/unittests/msvc10/unittests.sln000066400000000000000000000023261410602521300243540ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittests", "unittests.vcxproj", "{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|Win32.ActiveCfg = Debug|Win32 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|Win32.Build.0 = Debug|Win32 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|x64.ActiveCfg = Debug|x64 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|x64.Build.0 = Debug|x64 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|Win32.ActiveCfg = Release|Win32 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|Win32.Build.0 = Release|Win32 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|x64.ActiveCfg = Release|x64 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal readerwriterqueue-1.0.6/tests/unittests/msvc10/unittests.vcxproj000066400000000000000000000171101410602521300252500ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B} Win32Proj unittests Application true Unicode Application true Unicode Application false true Unicode Application false true Unicode true obj\$(Configuration)\$(Platform)\ $(SolutionDir)$(Configuration)\$(Platform)\ true obj\$(Configuration)\$(Platform)\ $(SolutionDir)$(Configuration)\$(Platform)\ false obj\$(Configuration)\$(Platform)\ $(SolutionDir)$(Configuration)\$(Platform)\ false obj\$(Configuration)\$(Platform)\ $(SolutionDir)$(Configuration)\$(Platform)\ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true readerwriterqueue-1.0.6/tests/unittests/msvc10/unittests.vcxproj.filters000066400000000000000000000027641410602521300267300ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Source Files Source Files readerwriterqueue-1.0.6/tests/unittests/msvc12/000077500000000000000000000000001410602521300215735ustar00rootroot00000000000000readerwriterqueue-1.0.6/tests/unittests/msvc12/unittests.sln000066400000000000000000000024451410602521300243600ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.30501.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittests", "unittests.vcxproj", "{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|Win32.ActiveCfg = Debug|Win32 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|Win32.Build.0 = Debug|Win32 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|x64.ActiveCfg = Debug|x64 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|x64.Build.0 = Debug|x64 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|Win32.ActiveCfg = Release|Win32 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|Win32.Build.0 = Release|Win32 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|x64.ActiveCfg = Release|x64 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal readerwriterqueue-1.0.6/tests/unittests/msvc12/unittests.vcxproj000066400000000000000000000173751410602521300252670ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B} Win32Proj unittests Application true Unicode v120 Application true Unicode v120 Application false true Unicode v120 Application false true Unicode v120 true obj\$(Configuration)\$(Platform)\ $(SolutionDir)$(Configuration)\$(Platform)\ true obj\$(Configuration)\$(Platform)\ $(SolutionDir)$(Configuration)\$(Platform)\ false obj\$(Configuration)\$(Platform)\ $(SolutionDir)$(Configuration)\$(Platform)\ false obj\$(Configuration)\$(Platform)\ $(SolutionDir)$(Configuration)\$(Platform)\ Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Console true true true readerwriterqueue-1.0.6/tests/unittests/msvc12/unittests.vcxproj.filters000066400000000000000000000027641410602521300267320ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Source Files Source Files readerwriterqueue-1.0.6/tests/unittests/unittests.cpp000066400000000000000000000477411410602521300232430ustar00rootroot00000000000000// ©2013-2015 Cameron Desrochers // Unit tests for moodycamel::ReaderWriterQueue #include #include #include #include #include #include "minitest.h" #include "../common/simplethread.h" #include "../../readerwriterqueue.h" #include "../../readerwritercircularbuffer.h" using namespace moodycamel; // *NOT* thread-safe struct Foo { Foo() : copied(false) { id = _id()++; } Foo(Foo const& other) : id(other.id), copied(true) { } Foo(Foo&& other) : id(other.id), copied(other.copied) { other.copied = true; } Foo& operator=(Foo&& other) { verify(); id = other.id, copied = other.copied; other.copied = true; return *this; } ~Foo() { verify(); } private: void verify() { if (copied) return; if (id != _last_destroyed_id() + 1) { _destroyed_in_order() = false; } _last_destroyed_id() = id; ++_destroy_count(); } public: static void reset() { _destroy_count() = 0; _id() = 0; _destroyed_in_order() = true; _last_destroyed_id() = -1; } static int destroy_count() { return _destroy_count(); } static bool destroyed_in_order() { return _destroyed_in_order(); } private: static int& _destroy_count() { static int c = 0; return c; } static int& _id() { static int i = 0; return i; } static bool& _destroyed_in_order() { static bool d = true; return d; } static int& _last_destroyed_id() { static int i = -1; return i; } int id; bool copied; }; #if MOODYCAMEL_HAS_EMPLACE class UniquePtrWrapper { public: UniquePtrWrapper() = default; UniquePtrWrapper(std::unique_ptr p) : m_p(std::move(p)) {} int get_value() const { return *m_p; } std::unique_ptr& get_ptr() { return m_p; } private: std::unique_ptr m_p; }; #endif /// Extracted from private static method of ReaderWriterQueue static size_t ceilToPow2(size_t x) { // From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 --x; x |= x >> 1; x |= x >> 2; x |= x >> 4; for (size_t i = 1; i < sizeof(size_t); i <<= 1) { x |= x >> (i << 3); } ++x; return x; } class ReaderWriterQueueTests : public TestClass { public: ReaderWriterQueueTests() { REGISTER_TEST(create_empty_queue); REGISTER_TEST(enqueue_one); REGISTER_TEST(enqueue_many); REGISTER_TEST(nonempty_destroy); REGISTER_TEST(try_enqueue); REGISTER_TEST(try_dequeue); REGISTER_TEST(peek); REGISTER_TEST(pop); REGISTER_TEST(size_approx); REGISTER_TEST(max_capacity); REGISTER_TEST(threaded); REGISTER_TEST(blocking); REGISTER_TEST(vector); #if MOODYCAMEL_HAS_EMPLACE REGISTER_TEST(emplace); REGISTER_TEST(try_enqueue_fail_workaround); REGISTER_TEST(try_emplace_fail); #endif REGISTER_TEST(blocking_circular_buffer); } bool create_empty_queue() { { ReaderWriterQueue q; } { ReaderWriterQueue q(1234); } return true; } bool enqueue_one() { int item; { item = 0; ReaderWriterQueue q(1); q.enqueue(12345); ASSERT_OR_FAIL(q.try_dequeue(item)); ASSERT_OR_FAIL(item == 12345); } { item = 0; ReaderWriterQueue q(1); ASSERT_OR_FAIL(q.try_enqueue(12345)); ASSERT_OR_FAIL(q.try_dequeue(item)); ASSERT_OR_FAIL(item == 12345); } return true; } bool enqueue_many() { int item = -1; { ReaderWriterQueue q(100); for (int i = 0; i != 100; ++i) { q.enqueue(i); } for (int i = 0; i != 100; ++i) { ASSERT_OR_FAIL(q.try_dequeue(item)); ASSERT_OR_FAIL(item == i); } } { ReaderWriterQueue q(100); for (int i = 0; i != 1200; ++i) { q.enqueue(i); } for (int i = 0; i != 1200; ++i) { ASSERT_OR_FAIL(q.try_dequeue(item)); ASSERT_OR_FAIL(item == i); } } return true; } bool nonempty_destroy() { // Some elements at beginning Foo::reset(); { ReaderWriterQueue q(31); for (int i = 0; i != 10; ++i) { q.enqueue(Foo()); } ASSERT_OR_FAIL(Foo::destroy_count() == 0); } ASSERT_OR_FAIL(Foo::destroy_count() == 10); ASSERT_OR_FAIL(Foo::destroyed_in_order()); // Entire block Foo::reset(); { ReaderWriterQueue q(31); for (int i = 0; i != 31; ++i) { q.enqueue(Foo()); } ASSERT_OR_FAIL(Foo::destroy_count() == 0); } ASSERT_OR_FAIL(Foo::destroy_count() == 31); ASSERT_OR_FAIL(Foo::destroyed_in_order()); // Multiple blocks Foo::reset(); { ReaderWriterQueue q(31); for (int i = 0; i != 94; ++i) { q.enqueue(Foo()); } ASSERT_OR_FAIL(Foo::destroy_count() == 0); } ASSERT_OR_FAIL(Foo::destroy_count() == 94); ASSERT_OR_FAIL(Foo::destroyed_in_order()); // Some elements in another block Foo::reset(); { ReaderWriterQueue q(31); Foo item; for (int i = 0; i != 42; ++i) { q.enqueue(Foo()); } ASSERT_OR_FAIL(Foo::destroy_count() == 0); for (int i = 0; i != 31; ++i) { ASSERT_OR_FAIL(q.try_dequeue(item)); } ASSERT_OR_FAIL(Foo::destroy_count() == 31); } ASSERT_OR_FAIL(Foo::destroy_count() == 43); ASSERT_OR_FAIL(Foo::destroyed_in_order()); // Some elements in multiple blocks Foo::reset(); { ReaderWriterQueue q(31); Foo item; for (int i = 0; i != 123; ++i) { q.enqueue(Foo()); } for (int i = 0; i != 25; ++i) { ASSERT_OR_FAIL(q.try_dequeue(item)); } for (int i = 0; i != 47; ++i) { q.enqueue(Foo()); } for (int i = 0; i != 140; ++i) { ASSERT_OR_FAIL(q.try_dequeue(item)); } for (int i = 0; i != 230; ++i) { q.enqueue(Foo()); } for (int i = 0; i != 130; ++i) { ASSERT_OR_FAIL(q.try_dequeue(item)); } for (int i = 0; i != 100; ++i) { q.enqueue(Foo()); } } ASSERT_OR_FAIL(Foo::destroy_count() == 501); ASSERT_OR_FAIL(Foo::destroyed_in_order()); return true; } bool try_enqueue() { ReaderWriterQueue q(31); int item; int size = 0; for (int i = 0; i < 10000; ++i) { if ((rand() & 1) == 1) { bool result = q.try_enqueue(i); if (size == 31) { ASSERT_OR_FAIL(!result); } else { ASSERT_OR_FAIL(result); ++size; } } else { bool result = q.try_dequeue(item); if (size == 0) { ASSERT_OR_FAIL(!result); } else { ASSERT_OR_FAIL(result); --size; } } } return true; } bool try_dequeue() { int item; { ReaderWriterQueue q(1); ASSERT_OR_FAIL(!q.try_dequeue(item)); } { ReaderWriterQueue q(10); ASSERT_OR_FAIL(!q.try_dequeue(item)); } return true; } bool threaded() { weak_atomic result; result = 1; ReaderWriterQueue q(100); SimpleThread reader([&]() { int item; int prevItem = -1; for (int i = 0; i != 1000000; ++i) { if (q.try_dequeue(item)) { if (item <= prevItem) { result = 0; } prevItem = item; } } }); SimpleThread writer([&]() { for (int i = 0; i != 1000000; ++i) { if (((i >> 7) & 1) == 0) { q.enqueue(i); } else { q.try_enqueue(i); } } }); writer.join(); reader.join(); return result.load() == 1 ? true : false; } bool peek() { weak_atomic result; result = 1; ReaderWriterQueue q(100); SimpleThread reader([&]() { int item; int prevItem = -1; int* peeked; for (int i = 0; i != 100000; ++i) { peeked = q.peek(); if (peeked != nullptr) { if (q.try_dequeue(item)) { if (item <= prevItem || item != *peeked) { result = 0; } prevItem = item; } else { result = 0; } } } }); SimpleThread writer([&]() { for (int i = 0; i != 100000; ++i) { if (((i >> 7) & 1) == 0) { q.enqueue(i); } else { q.try_enqueue(i); } } }); writer.join(); reader.join(); return result.load() == 1 ? true : false; } bool pop() { weak_atomic result; result = 1; ReaderWriterQueue q(100); SimpleThread reader([&]() { int item; int prevItem = -1; int* peeked; for (int i = 0; i != 100000; ++i) { peeked = q.peek(); if (peeked != nullptr) { item = *peeked; if (q.pop()) { if (item <= prevItem) { result = 0; } prevItem = item; } else { result = 0; } } } }); SimpleThread writer([&]() { for (int i = 0; i != 100000; ++i) { if (((i >> 7) & 1) == 0) { q.enqueue(i); } else { q.try_enqueue(i); } } }); writer.join(); reader.join(); return result.load() == 1 ? true : false; } bool size_approx() { weak_atomic result; weak_atomic front; weak_atomic tail; result = 1; front = 0; tail = 0; ReaderWriterQueue q(10); SimpleThread reader([&]() { int item; for (int i = 0; i != 100000; ++i) { if (q.try_dequeue(item)) { fence(memory_order_release); front = front.load() + 1; } int size = static_cast(q.size_approx()); fence(memory_order_acquire); int tail_ = tail.load(); int front_ = front.load(); if (size > tail_ - front_ || size < 0) { result = 0; } } }); SimpleThread writer([&]() { for (int i = 0; i != 100000; ++i) { tail = tail.load() + 1; fence(memory_order_release); q.enqueue(i); int tail_ = tail.load(); int front_ = front.load(); fence(memory_order_acquire); int size = static_cast(q.size_approx()); if (size > tail_ - front_ || size < 0) { result = 0; } } }); writer.join(); reader.join(); return result.load() == 1 ? true : false; } bool max_capacity() { { // this math for queue size estimation is only valid for q_size <= 256 for (size_t q_size = 2; q_size < 256; ++q_size) { ReaderWriterQueue q(q_size); ASSERT_OR_FAIL(q.max_capacity() == ceilToPow2(q_size+1)-1); const size_t start_cap = q.max_capacity(); for (size_t i = 0; i < start_cap+1; ++i) // fill 1 past capacity to resize q.enqueue(i); ASSERT_OR_FAIL(q.max_capacity() == 3*start_cap+1); } } return true; } bool blocking() { { BlockingReaderWriterQueue q; int item; q.enqueue(123); ASSERT_OR_FAIL(q.try_dequeue(item)); ASSERT_OR_FAIL(item == 123); ASSERT_OR_FAIL(q.size_approx() == 0); q.enqueue(234); ASSERT_OR_FAIL(q.size_approx() == 1); ASSERT_OR_FAIL(*q.peek() == 234); ASSERT_OR_FAIL(*q.peek() == 234); ASSERT_OR_FAIL(q.pop()); ASSERT_OR_FAIL(q.try_enqueue(345)); q.wait_dequeue(item); ASSERT_OR_FAIL(item == 345); ASSERT_OR_FAIL(!q.peek()); ASSERT_OR_FAIL(q.size_approx() == 0); ASSERT_OR_FAIL(!q.try_dequeue(item)); } weak_atomic result; result = 1; { BlockingReaderWriterQueue q(100); SimpleThread reader([&]() { int item = -1; int prevItem = -1; for (int i = 0; i != 1000000; ++i) { q.wait_dequeue(item); if (item <= prevItem) { result = 0; } prevItem = item; } }); SimpleThread writer([&]() { for (int i = 0; i != 1000000; ++i) { q.enqueue(i); } }); writer.join(); reader.join(); ASSERT_OR_FAIL(q.size_approx() == 0); ASSERT_OR_FAIL(result.load()); } { BlockingReaderWriterQueue q(100); SimpleThread reader([&]() { int item = -1; int prevItem = -1; for (int i = 0; i != 1000000; ++i) { if (!q.wait_dequeue_timed(item, 1000)) { --i; continue; } if (item <= prevItem) { result = 0; } prevItem = item; } }); SimpleThread writer([&]() { for (int i = 0; i != 1000000; ++i) { q.enqueue(i); for (volatile int x = 0; x != 100; ++x); } }); writer.join(); reader.join(); int item; ASSERT_OR_FAIL(q.size_approx() == 0); ASSERT_OR_FAIL(!q.wait_dequeue_timed(item, 0)); ASSERT_OR_FAIL(!q.wait_dequeue_timed(item, 1)); ASSERT_OR_FAIL(result.load()); } #if MOODYCAMEL_HAS_EMPLACE { BlockingReaderWriterQueue q(100); std::unique_ptr p { new int(123) }; q.emplace(std::move(p)); q.try_emplace(std::move(p)); UniquePtrWrapper item; ASSERT_OR_FAIL(q.wait_dequeue_timed(item, 0)); ASSERT_OR_FAIL(item.get_value() == 123); ASSERT_OR_FAIL(q.wait_dequeue_timed(item, 0)); ASSERT_OR_FAIL(item.get_ptr() == nullptr); ASSERT_OR_FAIL(q.size_approx() == 0); } #endif return true; } bool vector() { { std::vector> queues; queues.push_back(ReaderWriterQueue()); queues.emplace_back(); queues[0].enqueue(1); queues[1].enqueue(2); std::swap(queues[0], queues[1]); int item; ASSERT_OR_FAIL(queues[0].try_dequeue(item)); ASSERT_OR_FAIL(item == 2); ASSERT_OR_FAIL(queues[1].try_dequeue(item)); ASSERT_OR_FAIL(item == 1); } { std::vector> queues; queues.push_back(BlockingReaderWriterQueue()); queues.emplace_back(); queues[0].enqueue(1); queues[1].enqueue(2); std::swap(queues[0], queues[1]); int item; ASSERT_OR_FAIL(queues[0].try_dequeue(item)); ASSERT_OR_FAIL(item == 2); queues[1].wait_dequeue(item); ASSERT_OR_FAIL(item == 1); } return true; } #if MOODYCAMEL_HAS_EMPLACE bool emplace() { ReaderWriterQueue q(100); std::unique_ptr p { new int(123) }; q.emplace(std::move(p)); UniquePtrWrapper item; ASSERT_OR_FAIL(q.try_dequeue(item)); ASSERT_OR_FAIL(item.get_value() == 123); ASSERT_OR_FAIL(q.size_approx() == 0); return true; } // This is what you have to do to try_enqueue() a movable type, and demonstrates why try_emplace() is useful bool try_enqueue_fail_workaround() { ReaderWriterQueue q(0); { // A failed try_enqueue() will still delete p std::unique_ptr p { new int(123) }; q.try_enqueue(std::move(p)); ASSERT_OR_FAIL(q.size_approx() == 0); ASSERT_OR_FAIL(p == nullptr); } { // Workaround isn't pretty and potentially expensive - use try_emplace() instead std::unique_ptr p { new int(123) }; UniquePtrWrapper w(std::move(p)); q.try_enqueue(std::move(w)); p = std::move(w.get_ptr()); ASSERT_OR_FAIL(q.size_approx() == 0); ASSERT_OR_FAIL(p != nullptr); ASSERT_OR_FAIL(*p == 123); } return true; } bool try_emplace_fail() { ReaderWriterQueue q(0); std::unique_ptr p { new int(123) }; q.try_emplace(std::move(p)); ASSERT_OR_FAIL(q.size_approx() == 0); ASSERT_OR_FAIL(p != nullptr); ASSERT_OR_FAIL(*p == 123); return true; } #endif bool blocking_circular_buffer() { { // Basic enqueue BlockingReaderWriterCircularBuffer q(65); for (int iteration = 0; iteration != 128; ++iteration) { // check there's no problem with mismatch between nominal and allocated capacity ASSERT_OR_FAIL(q.max_capacity() == 65); ASSERT_OR_FAIL(q.size_approx() == 0); ASSERT_OR_FAIL(q.try_enqueue(0)); ASSERT_OR_FAIL(q.max_capacity() == 65); ASSERT_OR_FAIL(q.size_approx() == 1); for (int i = 1; i != 65; ++i) q.wait_enqueue(i); ASSERT_OR_FAIL(q.size_approx() == 65); ASSERT_OR_FAIL(!q.try_enqueue(65)); // Basic dequeue int item; ASSERT_OR_FAIL(q.try_dequeue(item)); ASSERT_OR_FAIL(item == 0); for (int i = 1; i != 65; ++i) { q.wait_dequeue(item); ASSERT_OR_FAIL(item == i); } ASSERT_OR_FAIL(!q.try_dequeue(item)); ASSERT_OR_FAIL(!q.wait_dequeue_timed(item, 1)); ASSERT_OR_FAIL(item == 64); } } { // Zero capacity BlockingReaderWriterCircularBuffer q(0); ASSERT_OR_FAIL(q.max_capacity() == 0); ASSERT_OR_FAIL(!q.try_enqueue(1)); ASSERT_OR_FAIL(!q.wait_enqueue_timed(1, 0)); } // Element lifetimes Foo::reset(); { BlockingReaderWriterCircularBuffer q(31); { Foo item; for (int i = 0; i != 23 + 32; ++i) { ASSERT_OR_FAIL(q.try_enqueue(Foo())); ASSERT_OR_FAIL(q.try_dequeue(item)); } ASSERT_OR_FAIL(Foo::destroy_count() == 23 + 32); ASSERT_OR_FAIL(Foo::destroyed_in_order()); } Foo::reset(); { Foo item; for (int i = 0; i != 10; ++i) ASSERT_OR_FAIL(q.try_enqueue(Foo())); ASSERT_OR_FAIL(q.size_approx() == 10); ASSERT_OR_FAIL(Foo::destroy_count() == 0); ASSERT_OR_FAIL(q.try_dequeue(item)); ASSERT_OR_FAIL(q.size_approx() == 9); ASSERT_OR_FAIL(Foo::destroy_count() == 1); } ASSERT_OR_FAIL(Foo::destroy_count() == 2); ASSERT_OR_FAIL(Foo::destroyed_in_order()); BlockingReaderWriterCircularBuffer q2(std::move(q)); ASSERT_OR_FAIL(q.size_approx() == 0); ASSERT_OR_FAIL(q2.size_approx() == 9); BlockingReaderWriterCircularBuffer q3(2); q3 = std::move(q2); ASSERT_OR_FAIL(q.size_approx() == 0); ASSERT_OR_FAIL(q2.size_approx() == 0); ASSERT_OR_FAIL(q3.size_approx() == 9); q = std::move(q2); ASSERT_OR_FAIL(q.size_approx() == 0); ASSERT_OR_FAIL(q2.size_approx() == 0); ASSERT_OR_FAIL(q3.size_approx() == 9); ASSERT_OR_FAIL(Foo::destroy_count() == 2); } ASSERT_OR_FAIL(Foo::destroy_count() == 11); ASSERT_OR_FAIL(Foo::destroyed_in_order()); weak_atomic result; result = 1; { // Threaded BlockingReaderWriterCircularBuffer q(8); SimpleThread reader([&]() { int item; for (int i = 0; i != 1000000; ++i) { q.wait_dequeue(item); if (item != i) result = 0; } }); SimpleThread writer([&]() { for (int i = 0; i != 1000000; ++i) q.wait_enqueue(i); }); writer.join(); reader.join(); ASSERT_OR_FAIL(q.size_approx() == 0); ASSERT_OR_FAIL(result.load()); } return true; } }; void printTests(ReaderWriterQueueTests const& tests) { std::printf(" Supported tests are:\n"); std::vector names; tests.getAllTestNames(names); for (auto it = names.cbegin(); it != names.cend(); ++it) { std::printf(" %s\n", it->c_str()); } } // Basic test harness int main(int argc, char** argv) { bool disablePrompt = false; std::vector selectedTests; // Disable buffering (so that when run in, e.g., Sublime Text, the output appears as it is written) std::setvbuf(stdout, nullptr, _IONBF, 0); // Isolate the executable name std::string progName = argv[0]; auto slash = progName.find_last_of("/\\"); if (slash != std::string::npos) { progName = progName.substr(slash + 1); } ReaderWriterQueueTests tests; // Parse command line options if (argc == 1) { std::printf("Running all unit tests for moodycamel::ReaderWriterQueue.\n(Run %s --help for other options.)\n\n", progName.c_str()); } else { bool printHelp = false; bool printedTests = false; bool error = false; for (int i = 1; i < argc; ++i) { if (std::strcmp(argv[i], "--help") == 0) { printHelp = true; } else if (std::strcmp(argv[i], "--disable-prompt") == 0) { disablePrompt = true; } else if (std::strcmp(argv[i], "--run") == 0) { if (i + 1 == argc || argv[i + 1][0] == '-') { std::printf("Expected test name argument for --run option.\n"); if (!printedTests) { printTests(tests); printedTests = true; } error = true; continue; } if (!tests.validateTestName(argv[++i])) { std::printf("Unrecognized test '%s'.\n", argv[i]); if (!printedTests) { printTests(tests); printedTests = true; } error = true; continue; } selectedTests.push_back(argv[i]); } else { std::printf("Unrecognized option '%s'.\n", argv[i]); error = true; } } if (error || printHelp) { if (error) { std::printf("\n"); } std::printf("%s\n Description: Runs unit tests for moodycamel::ReaderWriterQueue\n", progName.c_str()); std::printf(" --help Prints this help blurb\n"); std::printf(" --run test Runs only the specified test(s)\n"); std::printf(" --disable-prompt Disables prompt before exit when the tests finish\n"); return error ? -1 : 0; } } int exitCode = 0; bool result; if (selectedTests.size() > 0) { result = tests.run(selectedTests); } else { result = tests.run(); } if (result) { std::printf("All %stests passed.\n", (selectedTests.size() > 0 ? "selected " : "")); } else { std::printf("Test(s) failed!\n"); exitCode = 2; } if (!disablePrompt) { std::printf("Press ENTER to exit.\n"); getchar(); } return exitCode; }