├── .gitignore ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── atomicops.h ├── benchmarks ├── bench.cpp ├── ext │ ├── 1024cores │ │ └── spscqueue.h │ └── folly │ │ └── ProducerConsumerQueue.h ├── makefile ├── msvc10 │ ├── winbench-intel.vcxproj │ ├── winbench-intel.vcxproj.filters │ ├── winbench.sln │ ├── winbench.vcxproj │ └── winbench.vcxproj.filters ├── msvc12 │ ├── winbench-intel.vcxproj │ ├── winbench-intel.vcxproj.filters │ ├── winbench.sln │ ├── winbench.vcxproj │ └── winbench.vcxproj.filters ├── msvc14 │ ├── winbench-intel.vcxproj │ ├── winbench-intel.vcxproj.filters │ ├── winbench.sln │ ├── winbench.vcxproj │ └── winbench.vcxproj.filters ├── systemtime.cpp └── systemtime.h ├── readerwritercircularbuffer.h ├── readerwriterqueue.h ├── readerwriterqueueConfig.cmake.in └── tests ├── common ├── simplethread.cpp └── simplethread.h ├── stabtest ├── makefile ├── msvc10 │ ├── stabtest.sln │ ├── stabtest.vcxproj │ └── stabtest.vcxproj.filters ├── msvc12 │ ├── stabtest.sln │ ├── stabtest.vcxproj │ └── stabtest.vcxproj.filters └── stabtest.cpp └── unittests ├── makefile ├── minitest.h ├── msvc10 ├── unittests.sln ├── unittests.vcxproj └── unittests.vcxproj.filters ├── msvc12 ├── unittests.sln ├── unittests.vcxproj └── unittests.vcxproj.filters └── unittests.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.ipch 2 | *.suo 3 | *.user 4 | *.sdf 5 | *.opensdf 6 | *.exe 7 | *.VC.db 8 | .vs/ 9 | tests/stabtest/msvc*/Debug/ 10 | tests/stabtest/msvc*/Release/ 11 | tests/stabtest/msvc*/obj/ 12 | tests/stabtest/msvc*/log.txt 13 | tests/stabtest/log.txt 14 | tests/unittests/msvc*/Debug/ 15 | tests/unittests/msvc*/Release/ 16 | tests/unittests/msvc*/obj/ 17 | tests/CDSChecker/model-checker/ 18 | benchmarks/msvc*/Debug/ 19 | benchmarks/msvc*/Release/ 20 | benchmarks/msvc*/obj/ 21 | test/ 22 | # Linux binaries 23 | benchmarks/benchmarks 24 | tests/stabtest/stabtest 25 | tests/unittests/unittests 26 | 27 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9) 2 | project(readerwriterqueue VERSION 1.0.7) 3 | 4 | include(GNUInstallDirs) 5 | include(CMakePackageConfigHelpers) 6 | 7 | add_library(${PROJECT_NAME} INTERFACE) 8 | 9 | target_include_directories(readerwriterqueue INTERFACE 10 | $ 11 | $ 12 | ) 13 | 14 | install(FILES atomicops.h readerwriterqueue.h readerwritercircularbuffer.h LICENSE.md 15 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}) 16 | 17 | install(TARGETS ${PROJECT_NAME} 18 | EXPORT ${PROJECT_NAME}Targets 19 | ) 20 | 21 | write_basic_package_version_file( 22 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 23 | VERSION 24 | ${PROJECT_VERSION} 25 | COMPATIBILITY AnyNewerVersion 26 | ARCH_INDEPENDENT 27 | ) 28 | 29 | configure_package_config_file(${PROJECT_NAME}Config.cmake.in 30 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 31 | INSTALL_DESTINATION 32 | ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}/ 33 | ) 34 | 35 | install(EXPORT 36 | ${PROJECT_NAME}Targets 37 | FILE 38 | ${PROJECT_NAME}Targets.cmake 39 | NAMESPACE 40 | "${PROJECT_NAME}::" 41 | DESTINATION 42 | ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 43 | COMPONENT 44 | Devel 45 | ) 46 | 47 | install( 48 | FILES 49 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 50 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 51 | DESTINATION 52 | ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 53 | COMPONENT 54 | Devel 55 | ) 56 | 57 | set(CPACK_PACKAGE_NAME ${PROJECT_NAME}) 58 | set(CPACK_PACKAGE_VENDOR "Cameron Desrochers ") 59 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A single-producer, single-consumer lock-free queue for C++.") 60 | set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") 61 | set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") 62 | set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") 63 | set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") 64 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER ${CPACK_PACKAGE_VENDOR}) 65 | set(CPACK_GENERATOR "RPM;DEB") 66 | 67 | include(CPack) 68 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This license applies to all the code in this repository except that written by third 2 | parties, namely the files in benchmarks/ext, which have their own licenses, and Jeff 3 | Preshing's semaphore implementation (used in the blocking queues) which has a zlib 4 | license (embedded in atomicops.h). 5 | 6 | Simplified BSD License: 7 | 8 | Copyright (c) 2013-2021, Cameron Desrochers 9 | All rights reserved. 10 | 11 | Redistribution and use in source and binary forms, with or without modification, 12 | are permitted provided that the following conditions are met: 13 | 14 | - Redistributions of source code must retain the above copyright notice, this list of 15 | conditions and the following disclaimer. 16 | - Redistributions in binary form must reproduce the above copyright notice, this list of 17 | conditions and the following disclaimer in the documentation and/or other materials 18 | provided with the distribution. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 21 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 22 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 23 | THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 25 | OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 27 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # A single-producer, single-consumer lock-free queue for C++ 3 | 4 | This mini-repository has my very own implementation of a lock-free queue (that I designed from scratch) for C++. 5 | 6 | It only supports a two-thread use case (one consuming, and one producing). The threads can't switch roles, though 7 | you could use this queue completely from a single thread if you wish (but that would sort of defeat the purpose!). 8 | 9 | Note: If you need a general-purpose multi-producer, multi-consumer lock free queue, I have [one of those too][mpmc]. 10 | 11 | This repository also includes a [circular-buffer SPSC queue][circular] which supports blocking on enqueue as well as dequeue. 12 | 13 | 14 | ## Features 15 | 16 | - [Blazing fast][benchmarks] 17 | - Compatible with C++11 (supports moving objects instead of making copies) 18 | - Fully generic (templated container of any type) -- just like `std::queue`, you never need to allocate memory for elements yourself 19 | (which saves you the hassle of writing a lock-free memory manager to hold the elements you're queueing) 20 | - Allocates memory up front, in contiguous blocks 21 | - Provides a `try_enqueue` method which is guaranteed never to allocate memory (the queue starts with an initial capacity) 22 | - Also provides an `enqueue` method which can dynamically grow the size of the queue as needed 23 | - Also provides `try_emplace`/`emplace` convenience methods 24 | - Has a blocking version with `wait_dequeue` 25 | - Completely "wait-free" (no compare-and-swap loop). Enqueue and dequeue are always O(1) (not counting memory allocation) 26 | - 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) 27 | 28 | 29 | ## Use 30 | 31 | Simply drop the readerwriterqueue.h (or readerwritercircularbuffer.h) and atomicops.h files into your source code and include them :-) 32 | A modern compiler is required (MSVC2010+, GCC 4.7+, ICC 13+, or any C++11 compliant compiler should work). 33 | 34 | 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 35 | from working correctly. 36 | 37 | Example: 38 | 39 | ```cpp 40 | using namespace moodycamel; 41 | 42 | ReaderWriterQueue q(100); // Reserve space for at least 100 elements up front 43 | 44 | q.enqueue(17); // Will allocate memory if the queue is full 45 | bool succeeded = q.try_enqueue(18); // Will only succeed if the queue has an empty slot (never allocates) 46 | assert(succeeded); 47 | 48 | int number; 49 | succeeded = q.try_dequeue(number); // Returns false if the queue was empty 50 | 51 | assert(succeeded && number == 17); 52 | 53 | // You can also peek at the front item of the queue (consumer only) 54 | int* front = q.peek(); 55 | assert(*front == 18); 56 | succeeded = q.try_dequeue(number); 57 | assert(succeeded && number == 18); 58 | front = q.peek(); 59 | assert(front == nullptr); // Returns nullptr if the queue was empty 60 | ``` 61 | 62 | The blocking version has the exact same API, with the addition of `wait_dequeue` and 63 | `wait_dequeue_timed` methods: 64 | 65 | ```cpp 66 | BlockingReaderWriterQueue q; 67 | 68 | std::thread reader([&]() { 69 | int item; 70 | #if 1 71 | for (int i = 0; i != 100; ++i) { 72 | // Fully-blocking: 73 | q.wait_dequeue(item); 74 | } 75 | #else 76 | for (int i = 0; i != 100; ) { 77 | // Blocking with timeout 78 | if (q.wait_dequeue_timed(item, std::chrono::milliseconds(5))) 79 | ++i; 80 | } 81 | #endif 82 | }); 83 | std::thread writer([&]() { 84 | for (int i = 0; i != 100; ++i) { 85 | q.enqueue(i); 86 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 87 | } 88 | }); 89 | writer.join(); 90 | reader.join(); 91 | 92 | assert(q.size_approx() == 0); 93 | ``` 94 | 95 | Note that `wait_dequeue` will block indefinitely while the queue is empty; this 96 | means care must be taken to only call `wait_dequeue` if you're sure another element 97 | will come along eventually, or if the queue has a static lifetime. This is because 98 | destroying the queue while a thread is waiting on it will invoke undefined behaviour. 99 | 100 | The blocking circular buffer has a fixed number of slots, but is otherwise quite similar to 101 | use: 102 | 103 | ```cpp 104 | BlockingReaderWriterCircularBuffer q(1024); // pass initial capacity 105 | 106 | q.try_enqueue(1); 107 | int number; 108 | q.try_dequeue(number); 109 | assert(number == 1); 110 | 111 | q.wait_enqueue(123); 112 | q.wait_dequeue(number); 113 | assert(number == 123); 114 | 115 | q.wait_dequeue_timed(number, std::chrono::milliseconds(10)); 116 | ``` 117 | 118 | 119 | ## CMake 120 | ### Using targets in your project 121 | Using this project as a part of an existing CMake project is easy. 122 | 123 | In your CMakeLists.txt: 124 | ``` 125 | include(FetchContent) 126 | 127 | FetchContent_Declare( 128 | readerwriterqueue 129 | GIT_REPOSITORY https://github.com/cameron314/readerwriterqueue 130 | GIT_TAG master 131 | ) 132 | 133 | FetchContent_MakeAvailable(readerwriterqueue) 134 | 135 | add_library(my_target main.cpp) 136 | target_link_libraries(my_target PUBLIC readerwriterqueue) 137 | ``` 138 | 139 | In main.cpp: 140 | ```cpp 141 | #include 142 | 143 | int main() 144 | { 145 | moodycamel::ReaderWriterQueue q(100); 146 | } 147 | ``` 148 | 149 | ### Installing into system directories 150 | As an alternative to including the source files in your project directly, 151 | you can use CMake to install the library in your system's include directory: 152 | 153 | ``` 154 | mkdir build 155 | cd build 156 | cmake .. 157 | make install 158 | ``` 159 | 160 | Then, you can include it from your source code: 161 | ``` 162 | #include 163 | ``` 164 | 165 | ## Disclaimers 166 | 167 | The queue should only be used on platforms where aligned integer and pointer access is atomic; fortunately, that 168 | 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) :-) 169 | 170 | 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 171 | anything that's not x86-based. 172 | 173 | ## More info 174 | 175 | See the [LICENSE.md][license] file for the license (simplified BSD). 176 | 177 | My [blog post][blog] introduces the context that led to this code, and may be of interest if you're curious 178 | about lock-free programming. 179 | 180 | 181 | [blog]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++ 182 | [license]: LICENSE.md 183 | [benchmarks]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++#benchmarks 184 | [gcc46bug]: http://stackoverflow.com/questions/16429669/stdatomic-thread-fence-has-undefined-reference 185 | [mpmc]: https://github.com/cameron314/concurrentqueue 186 | [circular]: readerwritercircularbuffer.h 187 | -------------------------------------------------------------------------------- /benchmarks/bench.cpp: -------------------------------------------------------------------------------- 1 | // ©2013-2015 Cameron Desrochers. 2 | // Distributed under the simplified BSD license (see the LICENSE file that 3 | // should have come with this file). 4 | 5 | // Benchmarks for moodycamel::ReaderWriterQueue. 6 | 7 | #if defined(_MSC_VER) && _MSC_VER < 1700 8 | #define NO_FOLLY_SUPPORT 9 | #endif 10 | 11 | #if defined(_MSC_VER) && _MSC_VER < 1700 12 | #define NO_CIRCULAR_BUFFER_SUPPORT 13 | #endif 14 | 15 | #if !defined(__amd64__) && !defined(_M_X64) && !defined(__x86_64__) && !defined(_M_IX86) && !defined(__i386__) 16 | #define NO_SPSC_SUPPORT // SPSC implementation is for x86 only 17 | #endif 18 | 19 | #include "ext/1024cores/spscqueue.h" // Dmitry's (on Intel site) 20 | #ifndef NO_FOLLY_SUPPORT 21 | #include "ext/folly/ProducerConsumerQueue.h" // Facebook's folly (GitHub) 22 | #endif 23 | #include "../readerwriterqueue.h" // Mine 24 | #ifndef NO_CIRCULAR_BUFFER_SUPPORT 25 | #include "../readerwritercircularbuffer.h" // Mine 26 | template 27 | class BlockingReaderWriterCircularBufferAdapter : public moodycamel::BlockingReaderWriterCircularBuffer { 28 | public: 29 | BlockingReaderWriterCircularBufferAdapter(std::size_t capacity) : moodycamel::BlockingReaderWriterCircularBuffer(capacity) { } 30 | void enqueue(T const& x) { this->wait_enqueue(x); } 31 | }; 32 | #endif 33 | #include "systemtime.h" 34 | #include "../tests/common/simplethread.h" 35 | 36 | #include 37 | #include 38 | #include // For std::accumulate 39 | #include 40 | #include 41 | #include 42 | 43 | #ifndef UNUSED 44 | #define UNUSED(x) ((void)x); 45 | #endif 46 | 47 | using namespace moodycamel; 48 | #ifndef NO_FOLLY_SUPPORT 49 | using namespace folly; 50 | #endif 51 | 52 | 53 | typedef std::minstd_rand RNG_t; 54 | 55 | 56 | enum BenchmarkType { 57 | bench_raw_add, 58 | bench_raw_remove, 59 | bench_empty_remove, 60 | bench_single_threaded, 61 | bench_mostly_add, 62 | bench_mostly_remove, 63 | bench_heavy_concurrent, 64 | bench_random_concurrent, 65 | 66 | BENCHMARK_COUNT 67 | }; 68 | 69 | 70 | // Returns the number of seconds elapsed (high-precision), and the number of enqueue/dequeue 71 | // operations performed (in the out_Ops parameter) 72 | template 73 | double runBenchmark(BenchmarkType benchmark, unsigned int randomSeed, double& out_Ops); 74 | 75 | const int BENCHMARK_NAME_MAX = 17; // Not including null terminator 76 | const char* benchmarkName(BenchmarkType benchmark); 77 | 78 | 79 | int main(int argc, char** argv) 80 | { 81 | #ifdef NDEBUG 82 | const int TEST_COUNT = 25; 83 | #else 84 | const int TEST_COUNT = 2; 85 | #endif 86 | assert(TEST_COUNT >= 2); 87 | 88 | const double FASTEST_PERCENT_CONSIDERED = 20; // Consider only the fastest runs in the top 20% 89 | 90 | double rwqResults[BENCHMARK_COUNT][TEST_COUNT]; 91 | double brwcbResults[BENCHMARK_COUNT][TEST_COUNT]; 92 | double spscResults[BENCHMARK_COUNT][TEST_COUNT]; 93 | double follyResults[BENCHMARK_COUNT][TEST_COUNT]; 94 | 95 | // Also calculate a rough heuristic of "ops/s" (across all runs, not just fastest) 96 | double rwqOps[BENCHMARK_COUNT][TEST_COUNT]; 97 | double brwcbOps[BENCHMARK_COUNT][TEST_COUNT]; 98 | double spscOps[BENCHMARK_COUNT][TEST_COUNT]; 99 | double follyOps[BENCHMARK_COUNT][TEST_COUNT]; 100 | 101 | // Make sure the randomness of each benchmark run is identical 102 | unsigned int randSeeds[BENCHMARK_COUNT]; 103 | for (unsigned int i = 0; i != BENCHMARK_COUNT; ++i) { 104 | randSeeds[i] = ((unsigned int)time(NULL)) * i; 105 | } 106 | 107 | // Run benchmarks 108 | for (int benchmark = 0; benchmark < BENCHMARK_COUNT; ++benchmark) { 109 | for (int i = 0; i < TEST_COUNT; ++i) { 110 | rwqResults[benchmark][i] = runBenchmark>((BenchmarkType)benchmark, randSeeds[benchmark], rwqOps[benchmark][i]); 111 | } 112 | #ifndef NO_CIRCULAR_BUFFER_SUPPORT 113 | for (int i = 0; i < TEST_COUNT; ++i) { 114 | brwcbResults[benchmark][i] = runBenchmark>((BenchmarkType)benchmark, randSeeds[benchmark], brwcbOps[benchmark][i]); 115 | } 116 | #else 117 | for (int i = 0; i < TEST_COUNT; ++i) { 118 | brwcbResults[benchmark][i] = 0; 119 | brwcbOps[benchmark][i] = 0; 120 | } 121 | #endif 122 | #ifndef NO_SPSC_SUPPORT 123 | for (int i = 0; i < TEST_COUNT; ++i) { 124 | spscResults[benchmark][i] = runBenchmark>((BenchmarkType)benchmark, randSeeds[benchmark], spscOps[benchmark][i]); 125 | } 126 | #else 127 | for (int i = 0; i < TEST_COUNT; ++i) { 128 | spscResults[benchmark][i] = 0; 129 | spscOps[benchmark][i] = 0; 130 | } 131 | #endif 132 | #ifndef NO_FOLLY_SUPPORT 133 | for (int i = 0; i < TEST_COUNT; ++i) { 134 | follyResults[benchmark][i] = runBenchmark>((BenchmarkType)benchmark, randSeeds[benchmark], follyOps[benchmark][i]); 135 | } 136 | #else 137 | for (int i = 0; i < TEST_COUNT; ++i) { 138 | follyResults[benchmark][i] = 0; 139 | follyOps[benchmark][i] = 0; 140 | } 141 | #endif 142 | } 143 | 144 | // Sort results 145 | for (int benchmark = 0; benchmark < BENCHMARK_COUNT; ++benchmark) { 146 | std::sort(&rwqResults[benchmark][0], &rwqResults[benchmark][0] + TEST_COUNT); 147 | std::sort(&brwcbResults[benchmark][0], &brwcbResults[benchmark][0] + TEST_COUNT); 148 | std::sort(&spscResults[benchmark][0], &spscResults[benchmark][0] + TEST_COUNT); 149 | std::sort(&follyResults[benchmark][0], &follyResults[benchmark][0] + TEST_COUNT); 150 | } 151 | 152 | // Display results 153 | int max = std::max(2, (int)(TEST_COUNT * FASTEST_PERCENT_CONSIDERED / 100)); 154 | assert(max > 0); 155 | #ifdef NO_CIRCULAR_BUFFER_SUPPORT 156 | std::cout << "Note: BRWCB queue not supported on this platform, discount its timings" << std::endl; 157 | #endif 158 | #ifdef NO_SPSC_SUPPORT 159 | std::cout << "Note: SPSC queue not supported on this platform, discount its timings" << std::endl; 160 | #endif 161 | #ifdef NO_FOLLY_SUPPORT 162 | std::cout << "Note: Folly queue not supported by this compiler, discount its timings" << std::endl; 163 | #endif 164 | std::cout << std::setw(BENCHMARK_NAME_MAX) << " " << " |---------------- Min -----------------|----------------- Max -----------------|----------------- Avg -----------------|\n"; 165 | 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"; 166 | std::cout.fill('-'); 167 | std::cout << std::setw(BENCHMARK_NAME_MAX) << "---------" << "-+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+-------+-------\n"; 168 | std::cout.fill(' '); 169 | double rwqOpsPerSec = 0, brwcbOpsPerSec = 0, spscOpsPerSec = 0, follyOpsPerSec = 0; 170 | int opTimedBenchmarks = 0; 171 | for (int benchmark = 0; benchmark < BENCHMARK_COUNT; ++benchmark) { 172 | double rwqMin = rwqResults[benchmark][0], rwqMax = rwqResults[benchmark][max - 1]; 173 | double brwcbMin = brwcbResults[benchmark][0], brwcbMax = brwcbResults[benchmark][max - 1]; 174 | double spscMin = spscResults[benchmark][0], spscMax = spscResults[benchmark][max - 1]; 175 | double follyMin = follyResults[benchmark][0], follyMax = follyResults[benchmark][max - 1]; 176 | double rwqAvg = std::accumulate(&rwqResults[benchmark][0], &rwqResults[benchmark][0] + max, 0.0) / max; 177 | double brwcbAvg = std::accumulate(&brwcbResults[benchmark][0], &brwcbResults[benchmark][0] + max, 0.0) / max; 178 | double spscAvg = std::accumulate(&spscResults[benchmark][0], &spscResults[benchmark][0] + max, 0.0) / max; 179 | double follyAvg = std::accumulate(&follyResults[benchmark][0], &follyResults[benchmark][0] + max, 0.0) / max; 180 | double spscMult = rwqAvg < 0.00001 ? 0 : spscAvg / rwqAvg; 181 | double follyMult = follyAvg < 0.00001 ? 0 : follyAvg / rwqAvg; 182 | 183 | if (rwqResults[benchmark][0] != -1) { 184 | double rwqTotalAvg = std::accumulate(&rwqResults[benchmark][0], &rwqResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT; 185 | double brwcbTotalAvg = std::accumulate(&brwcbResults[benchmark][0], &brwcbResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT; 186 | double spscTotalAvg = std::accumulate(&spscResults[benchmark][0], &spscResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT; 187 | double follyTotalAvg = std::accumulate(&follyResults[benchmark][0], &follyResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT; 188 | rwqOpsPerSec += rwqTotalAvg == 0 ? 0 : std::accumulate(&rwqOps[benchmark][0], &rwqOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / rwqTotalAvg; 189 | brwcbOpsPerSec += brwcbTotalAvg == 0 ? 0 : std::accumulate(&brwcbOps[benchmark][0], &brwcbOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / brwcbTotalAvg; 190 | spscOpsPerSec += spscTotalAvg == 0 ? 0 : std::accumulate(&spscOps[benchmark][0], &spscOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / spscTotalAvg; 191 | follyOpsPerSec += follyTotalAvg == 0 ? 0 : std::accumulate(&follyOps[benchmark][0], &follyOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / follyTotalAvg; 192 | ++opTimedBenchmarks; 193 | } 194 | 195 | std::cout 196 | << std::left << std::setw(BENCHMARK_NAME_MAX) << benchmarkName((BenchmarkType)benchmark) << " | " 197 | << std::fixed << std::setprecision(4) << rwqMin << "s | " 198 | << std::fixed << std::setprecision(4) << brwcbMin << "s | " 199 | << std::fixed << std::setprecision(4) << spscMin << "s | " 200 | << std::fixed << std::setprecision(4) << follyMin << "s | " 201 | << std::fixed << std::setprecision(4) << rwqMax << "s | " 202 | << std::fixed << std::setprecision(4) << brwcbMax << "s | " 203 | << std::fixed << std::setprecision(4) << spscMax << "s | " 204 | << std::fixed << std::setprecision(4) << follyMax << "s | " 205 | << std::fixed << std::setprecision(4) << rwqAvg << "s | " 206 | << std::fixed << std::setprecision(4) << brwcbAvg << "s | " 207 | << std::fixed << std::setprecision(4) << spscAvg << "s | " 208 | << std::fixed << std::setprecision(4) << follyAvg << "s | " 209 | << std::fixed << std::setprecision(2) << spscMult << "x | " 210 | << std::fixed << std::setprecision(2) << follyMult << "x" 211 | << "\n" 212 | ; 213 | } 214 | 215 | rwqOpsPerSec /= opTimedBenchmarks; 216 | brwcbOpsPerSec /= opTimedBenchmarks; 217 | spscOpsPerSec /= opTimedBenchmarks; 218 | follyOpsPerSec /= opTimedBenchmarks; 219 | 220 | std::cout 221 | << "\nAverage ops/s:\n" 222 | << " ReaderWriterQueue: " << std::fixed << std::setprecision(2) << rwqOpsPerSec / 1000000 << " million\n" 223 | << " BlockingReaderWriterCircularBuffer: " << std::fixed << std::setprecision(2) << brwcbOpsPerSec / 1000000 << " million\n" 224 | << " SPSC queue: " << std::fixed << std::setprecision(2) << spscOpsPerSec / 1000000 << " million\n" 225 | << " Folly queue: " << std::fixed << std::setprecision(2) << follyOpsPerSec / 1000000 << " million\n" 226 | ; 227 | std::cout << std::endl; 228 | 229 | return 0; 230 | } 231 | 232 | 233 | template 234 | double runBenchmark(BenchmarkType benchmark, unsigned int randomSeed, double& out_Ops) 235 | { 236 | typedef unsigned long long counter_t; 237 | 238 | SystemTime start; 239 | double result = 0; 240 | volatile int forceNoOptimizeDummy; 241 | 242 | switch (benchmark) { 243 | case bench_raw_add: { 244 | const counter_t MAX = 100 * 1000; 245 | out_Ops = MAX; 246 | TQueue q(MAX); 247 | int num = 0; 248 | start = getSystemTime(); 249 | for (counter_t i = 0; i != MAX; ++i) { 250 | q.enqueue(num); 251 | ++num; 252 | } 253 | result = getTimeDelta(start); 254 | 255 | int temp = -1; 256 | q.try_dequeue(temp); 257 | forceNoOptimizeDummy = temp; 258 | } break; 259 | case bench_raw_remove: { 260 | const counter_t MAX = 100 * 1000; 261 | out_Ops = MAX; 262 | TQueue q(MAX); 263 | int num = 0; 264 | for (counter_t i = 0; i != MAX; ++i) { 265 | q.enqueue(num); 266 | ++num; 267 | } 268 | 269 | int element = -1; 270 | int total = 0; 271 | num = 0; 272 | start = getSystemTime(); 273 | for (counter_t i = 0; i != MAX; ++i) { 274 | bool success = q.try_dequeue(element); 275 | assert(success && num++ == element); 276 | UNUSED(success); 277 | total += element; 278 | } 279 | result = getTimeDelta(start); 280 | assert(!q.try_dequeue(element)); 281 | forceNoOptimizeDummy = total; 282 | } break; 283 | case bench_empty_remove: { 284 | const counter_t MAX = 2000 * 1000; 285 | out_Ops = MAX; 286 | TQueue q(MAX); 287 | int total = 0; 288 | start = getSystemTime(); 289 | SimpleThread consumer([&]() { 290 | int element; 291 | for (counter_t i = 0; i != MAX; ++i) { 292 | if (q.try_dequeue(element)) { 293 | total += element; 294 | } 295 | } 296 | }); 297 | SimpleThread producer([&]() { 298 | int num = 0; 299 | for (counter_t i = 0; i != MAX / 2; ++i) { 300 | if ((i & 32767) == 0) { // Just to make sure the loops aren't optimized out entirely 301 | q.enqueue(num); 302 | ++num; 303 | } 304 | } 305 | }); 306 | producer.join(); 307 | consumer.join(); 308 | result = getTimeDelta(start); 309 | forceNoOptimizeDummy = total; 310 | } break; 311 | case bench_single_threaded: { 312 | const counter_t MAX = 200 * 1000; 313 | out_Ops = MAX; 314 | RNG_t rng(randomSeed); 315 | std::uniform_int_distribution rand(0, 1); 316 | TQueue q(MAX); 317 | int num = 0; 318 | int element = -1; 319 | start = getSystemTime(); 320 | for (counter_t i = 0; i != MAX; ++i) { 321 | if (rand(rng) == 1) { 322 | q.enqueue(num); 323 | ++num; 324 | } 325 | else { 326 | q.try_dequeue(element); 327 | } 328 | } 329 | result = getTimeDelta(start); 330 | forceNoOptimizeDummy = (int)(q.try_dequeue(element)); 331 | } break; 332 | case bench_mostly_add: { 333 | const counter_t MAX = 1200 * 1000; 334 | out_Ops = MAX; 335 | int readOps = 0; 336 | RNG_t rng(randomSeed); 337 | std::uniform_int_distribution rand(0, 3); 338 | TQueue q(MAX); 339 | int element = -1; 340 | start = getSystemTime(); 341 | SimpleThread consumer([&]() { 342 | for (counter_t i = 0; i != MAX / 10; ++i) { 343 | if (rand(rng) == 0) { 344 | q.try_dequeue(element); 345 | ++readOps; 346 | } 347 | } 348 | }); 349 | SimpleThread producer([&]() { 350 | int num = 0; 351 | for (counter_t i = 0; i != MAX; ++i) { 352 | q.enqueue(num); 353 | ++num; 354 | } 355 | }); 356 | producer.join(); 357 | consumer.join(); 358 | result = getTimeDelta(start); 359 | forceNoOptimizeDummy = (int)(q.try_dequeue(element)); 360 | out_Ops += readOps; 361 | } break; 362 | case bench_mostly_remove: { 363 | const counter_t MAX = 1200 * 1000; 364 | out_Ops = MAX; 365 | int writeOps = 0; 366 | RNG_t rng(randomSeed); 367 | std::uniform_int_distribution rand(0, 3); 368 | TQueue q(MAX); 369 | int element = -1; 370 | start = getSystemTime(); 371 | SimpleThread consumer([&]() { 372 | for (counter_t i = 0; i != MAX; ++i) { 373 | q.try_dequeue(element); 374 | } 375 | }); 376 | SimpleThread producer([&]() { 377 | int num = 0; 378 | for (counter_t i = 0; i != MAX / 10; ++i) { 379 | if (rand(rng) == 0) { 380 | q.enqueue(num); 381 | ++num; 382 | } 383 | } 384 | writeOps = num; 385 | }); 386 | producer.join(); 387 | consumer.join(); 388 | result = getTimeDelta(start); 389 | forceNoOptimizeDummy = (int)(q.try_dequeue(element)); 390 | out_Ops += writeOps; 391 | } break; 392 | case bench_heavy_concurrent: { 393 | const counter_t MAX = 1000 * 1000; 394 | out_Ops = MAX * 2; 395 | TQueue q(MAX); 396 | int element = -1; 397 | start = getSystemTime(); 398 | SimpleThread consumer([&]() { 399 | for (counter_t i = 0; i != MAX; ++i) { 400 | q.try_dequeue(element); 401 | } 402 | }); 403 | SimpleThread producer([&]() { 404 | int num = 0; 405 | for (counter_t i = 0; i != MAX; ++i) { 406 | q.enqueue(num); 407 | ++num; 408 | } 409 | }); 410 | producer.join(); 411 | consumer.join(); 412 | result = getTimeDelta(start); 413 | forceNoOptimizeDummy = (int)(q.try_dequeue(element)); 414 | } break; 415 | case bench_random_concurrent: { 416 | const counter_t MAX = 800 * 1000; 417 | int readOps = 0, writeOps = 0; 418 | TQueue q(MAX); 419 | int element = -1; 420 | start = getSystemTime(); 421 | SimpleThread consumer([&]() { 422 | RNG_t rng(randomSeed); 423 | std::uniform_int_distribution rand(0, 15); 424 | for (counter_t i = 0; i != MAX; ++i) { 425 | if (rand(rng) == 0) { 426 | q.try_dequeue(element); 427 | ++readOps; 428 | } 429 | } 430 | }); 431 | SimpleThread producer([&]() { 432 | RNG_t rng(randomSeed * 3 - 1); 433 | std::uniform_int_distribution rand(0, 15); 434 | int num = 0; 435 | for (counter_t i = 0; i != MAX; ++i) { 436 | if (rand(rng) == 0) { 437 | q.enqueue(num); 438 | ++num; 439 | } 440 | } 441 | writeOps = num; 442 | }); 443 | producer.join(); 444 | consumer.join(); 445 | result = getTimeDelta(start); 446 | forceNoOptimizeDummy = (int)(q.try_dequeue(element)); 447 | out_Ops = readOps + writeOps; 448 | } break; 449 | default: 450 | assert(false); 451 | out_Ops = 0; 452 | return 0; 453 | } 454 | 455 | UNUSED(forceNoOptimizeDummy); 456 | return result / 1000.0; 457 | } 458 | 459 | const char* benchmarkName(BenchmarkType benchmark) 460 | { 461 | switch (benchmark) { 462 | case bench_raw_add: return "Raw add"; 463 | case bench_raw_remove: return "Raw remove"; 464 | case bench_empty_remove: return "Raw empty remove"; 465 | case bench_single_threaded: return "Single-threaded"; 466 | case bench_mostly_add: return "Mostly add"; 467 | case bench_mostly_remove: return "Mostly remove"; 468 | case bench_heavy_concurrent: return "Heavy concurrent"; 469 | case bench_random_concurrent: return "Random concurrent"; 470 | default: return ""; 471 | } 472 | } 473 | -------------------------------------------------------------------------------- /benchmarks/ext/1024cores/spscqueue.h: -------------------------------------------------------------------------------- 1 | #include "../../../atomicops.h" 2 | #include // For std::size_t 3 | 4 | // From http://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue 5 | // (and http://software.intel.com/en-us/articles/single-producer-single-consumer-queue) 6 | 7 | // load with 'consume' (data-dependent) memory ordering 8 | template 9 | T load_consume(T const* addr) 10 | { 11 | // hardware fence is implicit on x86 12 | T v = *const_cast(addr); 13 | moodycamel::compiler_fence(moodycamel::memory_order_seq_cst); 14 | return v; 15 | } 16 | 17 | // store with 'release' memory ordering 18 | template 19 | void store_release(T* addr, T v) 20 | { 21 | // hardware fence is implicit on x86 22 | moodycamel::compiler_fence(moodycamel::memory_order_seq_cst); 23 | *const_cast(addr) = v; 24 | } 25 | 26 | // cache line size on modern x86 processors (in bytes) 27 | size_t const cache_line_size = 64; 28 | // single-producer/single-consumer queue 29 | template 30 | class spsc_queue 31 | { 32 | public: 33 | spsc_queue() 34 | { 35 | node* n = new node; 36 | n->next_ = 0; 37 | tail_ = head_ = first_= tail_copy_ = n; 38 | } 39 | 40 | explicit spsc_queue(size_t prealloc) 41 | { 42 | node* n = new node; 43 | n->next_ = 0; 44 | tail_ = head_ = first_ = tail_copy_ = n; 45 | 46 | // [CD] Not (at all) the most efficient way to pre-allocate memory, but it works 47 | T dummy = T(); 48 | for (size_t i = 0; i != prealloc; ++i) { 49 | enqueue(dummy); 50 | } 51 | for (size_t i = 0; i != prealloc; ++i) { 52 | try_dequeue(dummy); 53 | } 54 | } 55 | 56 | ~spsc_queue() 57 | { 58 | node* n = first_; 59 | do 60 | { 61 | node* next = n->next_; 62 | delete n; 63 | n = next; 64 | } 65 | while (n); 66 | } 67 | 68 | void enqueue(T v) 69 | { 70 | node* n = alloc_node(); 71 | n->next_ = 0; 72 | n->value_ = v; 73 | store_release(&head_->next_, n); 74 | head_ = n; 75 | } 76 | 77 | // returns 'false' if queue is empty 78 | bool try_dequeue(T& v) 79 | { 80 | if (load_consume(&tail_->next_)) 81 | { 82 | v = tail_->next_->value_; 83 | store_release(&tail_, tail_->next_); 84 | return true; 85 | } 86 | else 87 | { 88 | return false; 89 | } 90 | } 91 | 92 | private: 93 | // internal node structure 94 | struct node 95 | { 96 | node* next_; 97 | T value_; 98 | }; 99 | 100 | // consumer part 101 | // accessed mainly by consumer, infrequently be producer 102 | node* tail_; // tail of the queue 103 | 104 | // delimiter between consumer part and producer part, 105 | // so that they situated on different cache lines 106 | char cache_line_pad_ [cache_line_size]; 107 | 108 | // producer part 109 | // accessed only by producer 110 | node* head_; // head of the queue 111 | node* first_; // last unused node (tail of node cache) 112 | node* tail_copy_; // helper (points somewhere between first_ and tail_) 113 | 114 | node* alloc_node() 115 | { 116 | // first tries to allocate node from internal node cache, 117 | // if attempt fails, allocates node via ::operator new() 118 | 119 | if (first_ != tail_copy_) 120 | { 121 | node* n = first_; 122 | first_ = first_->next_; 123 | return n; 124 | } 125 | tail_copy_ = load_consume(&tail_); 126 | if (first_ != tail_copy_) 127 | { 128 | node* n = first_; 129 | first_ = first_->next_; 130 | return n; 131 | } 132 | node* n = new node; 133 | return n; 134 | } 135 | 136 | spsc_queue(spsc_queue const&); 137 | spsc_queue& operator = (spsc_queue const&); 138 | 139 | }; 140 | -------------------------------------------------------------------------------- /benchmarks/ext/folly/ProducerConsumerQueue.h: -------------------------------------------------------------------------------- 1 | // Adapted from https://github.com/facebook/folly/blob/master/folly/ProducerConsumerQueue.h 2 | /* 3 | * Copyright 2013 Facebook, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | // @author Bo Hu (bhu@fb.com) 19 | // @author Jordan DeLong (delong.j@fb.com) 20 | 21 | #ifndef PRODUCER_CONSUMER_QUEUE_H_ 22 | #define PRODUCER_CONSUMER_QUEUE_H_ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | //#include 32 | 33 | namespace folly { 34 | 35 | /* 36 | * ProducerConsumerQueue is a one producer and one consumer queue 37 | * without locks. 38 | */ 39 | template 40 | struct ProducerConsumerQueue { 41 | typedef T value_type; 42 | 43 | // size must be >= 1. 44 | explicit ProducerConsumerQueue(uint32_t size) 45 | : size_(size + 1) // +1 because one slot is always empty 46 | , records_(static_cast(std::malloc(sizeof(T) * (size + 1)))) 47 | , readIndex_(0) 48 | , writeIndex_(0) 49 | { 50 | assert(size >= 1); 51 | if (!records_) { 52 | throw std::bad_alloc(); 53 | } 54 | } 55 | 56 | ~ProducerConsumerQueue() { 57 | // We need to destruct anything that may still exist in our queue. 58 | // (No real synchronization needed at destructor time: only one 59 | // thread can be doing this.) 60 | if (!std::is_trivially_destructible::value) { 61 | int read = readIndex_; 62 | int end = writeIndex_; 63 | while (read != end) { 64 | records_[read].~T(); 65 | if (++read == size_) { 66 | read = 0; 67 | } 68 | } 69 | } 70 | 71 | std::free(records_); 72 | } 73 | 74 | template 75 | bool enqueue(Args&&... recordArgs) { 76 | auto const currentWrite = writeIndex_.load(std::memory_order_relaxed); 77 | auto nextRecord = currentWrite + 1; 78 | if (nextRecord == size_) { 79 | nextRecord = 0; 80 | } 81 | if (nextRecord != readIndex_.load(std::memory_order_acquire)) { 82 | new (&records_[currentWrite]) T(std::forward(recordArgs)...); 83 | writeIndex_.store(nextRecord, std::memory_order_release); 84 | return true; 85 | } 86 | 87 | // queue is full 88 | return false; 89 | } 90 | 91 | // move (or copy) the value at the front of the queue to given variable 92 | bool try_dequeue(T& record) { 93 | auto const currentRead = readIndex_.load(std::memory_order_relaxed); 94 | if (currentRead == writeIndex_.load(std::memory_order_acquire)) { 95 | // queue is empty 96 | return false; 97 | } 98 | 99 | auto nextRecord = currentRead + 1; 100 | if (nextRecord == size_) { 101 | nextRecord = 0; 102 | } 103 | record = std::move(records_[currentRead]); 104 | records_[currentRead].~T(); 105 | readIndex_.store(nextRecord, std::memory_order_release); 106 | return true; 107 | } 108 | 109 | // pointer to the value at the front of the queue (for use in-place) or 110 | // nullptr if empty. 111 | T* frontPtr() { 112 | auto const currentRead = readIndex_.load(std::memory_order_relaxed); 113 | if (currentRead == writeIndex_.load(std::memory_order_acquire)) { 114 | // queue is empty 115 | return nullptr; 116 | } 117 | return &records_[currentRead]; 118 | } 119 | 120 | // queue must not be empty 121 | void popFront() { 122 | auto const currentRead = readIndex_.load(std::memory_order_relaxed); 123 | assert(currentRead != writeIndex_.load(std::memory_order_acquire)); 124 | 125 | auto nextRecord = currentRead + 1; 126 | if (nextRecord == size_) { 127 | nextRecord = 0; 128 | } 129 | records_[currentRead].~T(); 130 | readIndex_.store(nextRecord, std::memory_order_release); 131 | } 132 | 133 | bool isEmpty() const { 134 | return readIndex_.load(std::memory_order_consume) == 135 | writeIndex_.load(std::memory_order_consume); 136 | } 137 | 138 | bool isFull() const { 139 | auto nextRecord = writeIndex_.load(std::memory_order_consume) + 1; 140 | if (nextRecord == size_) { 141 | nextRecord = 0; 142 | } 143 | if (nextRecord != readIndex_.load(std::memory_order_consume)) { 144 | return false; 145 | } 146 | // queue is full 147 | return true; 148 | } 149 | 150 | // * If called by consumer, then true size may be more (because producer may 151 | // be adding items concurrently). 152 | // * If called by producer, then true size may be less (because consumer may 153 | // be removing items concurrently). 154 | // * It is undefined to call this from any other thread. 155 | size_t sizeGuess() const { 156 | int ret = writeIndex_.load(std::memory_order_consume) - 157 | readIndex_.load(std::memory_order_consume); 158 | if (ret < 0) { 159 | ret += size_; 160 | } 161 | return ret; 162 | } 163 | 164 | private: 165 | const uint32_t size_; 166 | T* const records_; 167 | 168 | std::atomic readIndex_; 169 | std::atomic writeIndex_; 170 | }; 171 | 172 | } 173 | 174 | #endif 175 | -------------------------------------------------------------------------------- /benchmarks/makefile: -------------------------------------------------------------------------------- 1 | # ©2014 Cameron Desrochers 2 | 3 | ifeq ($(OS),Windows_NT) 4 | EXT=.exe 5 | PLATFORM_OPTS=-static 6 | else 7 | EXT= 8 | UNAME_S := $(shell uname -s) 9 | ifeq ($(UNAME_S),Darwin) 10 | PLATFORM_OPTS= 11 | else 12 | PLATFORM_OPTS=-Wl,--no-as-needed -lrt 13 | endif 14 | endif 15 | 16 | default: benchmarks$(EXT) 17 | 18 | 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 19 | g++ -std=c++11 -Wpedantic -Wall -DNDEBUG -O3 -g bench.cpp ../tests/common/simplethread.cpp systemtime.cpp -o benchmarks$(EXT) -pthread $(PLATFORM_OPTS) 20 | 21 | run: benchmarks$(EXT) 22 | ./benchmarks$(EXT) 23 | -------------------------------------------------------------------------------- /benchmarks/msvc10/winbench-intel.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9} 23 | Win32Proj 24 | winbenchintel 25 | 26 | 27 | 28 | Application 29 | true 30 | Unicode 31 | Intel C++ Compiler XE 13.0 32 | 33 | 34 | Application 35 | true 36 | Unicode 37 | Intel C++ Compiler XE 13.0 38 | 39 | 40 | Application 41 | false 42 | true 43 | Unicode 44 | Intel C++ Compiler XE 13.0 45 | 46 | 47 | Application 48 | false 49 | true 50 | Unicode 51 | Intel C++ Compiler XE 13.0 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | $(SolutionDir)$(Configuration)\$(Platform)\ 72 | obj\$(Configuration)\$(Platform)\ 73 | 74 | 75 | true 76 | $(SolutionDir)$(Configuration)\$(Platform)\ 77 | obj\$(Configuration)\$(Platform)\ 78 | 79 | 80 | false 81 | $(SolutionDir)$(Configuration)\$(Platform)\ 82 | obj\$(Configuration)\$(Platform)\ 83 | 84 | 85 | false 86 | $(SolutionDir)$(Configuration)\$(Platform)\ 87 | obj\$(Configuration)\$(Platform)\ 88 | 89 | 90 | 91 | 92 | 93 | Level3 94 | Disabled 95 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | 97 | 98 | Console 99 | true 100 | 101 | 102 | 103 | 104 | 105 | 106 | Level3 107 | Disabled 108 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | 119 | 120 | MaxSpeed 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | 125 | 126 | Console 127 | true 128 | true 129 | true 130 | 131 | 132 | 133 | 134 | Level3 135 | 136 | 137 | MaxSpeed 138 | true 139 | true 140 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /benchmarks/msvc10/winbench-intel.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | 35 | 36 | Source Files 37 | 38 | 39 | Source Files 40 | 41 | 42 | Source Files 43 | 44 | 45 | -------------------------------------------------------------------------------- /benchmarks/msvc10/winbench.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench", "winbench.vcxproj", "{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}" 5 | EndProject 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench-intel", "winbench-intel.vcxproj", "{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug|x64 = Debug|x64 12 | Release|Win32 = Release|Win32 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.ActiveCfg = Debug|Win32 17 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.Build.0 = Debug|Win32 18 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.ActiveCfg = Debug|Win32 19 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.Build.0 = Debug|Win32 20 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.ActiveCfg = Release|Win32 21 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.Build.0 = Release|Win32 22 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.ActiveCfg = Release|x64 23 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.Build.0 = Release|x64 24 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.ActiveCfg = Debug|Win32 25 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.Build.0 = Debug|Win32 26 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|x64.ActiveCfg = Debug|Win32 27 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.ActiveCfg = Release|Win32 28 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.Build.0 = Release|Win32 29 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|x64.ActiveCfg = Release|x64 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /benchmarks/msvc10/winbench.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2} 23 | Win32Proj 24 | winbench 25 | 26 | 27 | 28 | Application 29 | true 30 | Unicode 31 | 32 | 33 | Application 34 | true 35 | Unicode 36 | 37 | 38 | Application 39 | false 40 | true 41 | Unicode 42 | 43 | 44 | Application 45 | false 46 | true 47 | Unicode 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | true 67 | $(SolutionDir)$(Configuration)\$(Platform)\ 68 | obj\$(Configuration)\$(Platform)\ 69 | 70 | 71 | true 72 | $(SolutionDir)$(Configuration)\$(Platform)\ 73 | obj\$(Configuration)\$(Platform)\ 74 | 75 | 76 | false 77 | $(SolutionDir)$(Configuration)\$(Platform)\ 78 | obj\$(Configuration)\$(Platform)\ 79 | 80 | 81 | false 82 | $(SolutionDir)$(Configuration)\$(Platform)\ 83 | obj\$(Configuration)\$(Platform)\ 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | 93 | 94 | Console 95 | true 96 | 97 | 98 | 99 | 100 | 101 | 102 | Level3 103 | Disabled 104 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 105 | 106 | 107 | Console 108 | true 109 | 110 | 111 | 112 | 113 | Level3 114 | 115 | 116 | MaxSpeed 117 | true 118 | true 119 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 120 | 121 | 122 | Console 123 | true 124 | true 125 | true 126 | 127 | 128 | 129 | 130 | Level3 131 | 132 | 133 | MaxSpeed 134 | true 135 | true 136 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 137 | 138 | 139 | Console 140 | true 141 | true 142 | true 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /benchmarks/msvc10/winbench.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | -------------------------------------------------------------------------------- /benchmarks/msvc12/winbench-intel.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9} 23 | Win32Proj 24 | winbenchintel 25 | 26 | 27 | 28 | Application 29 | true 30 | Unicode 31 | Intel C++ Compiler XE 13.0 32 | 33 | 34 | Application 35 | true 36 | Unicode 37 | Intel C++ Compiler XE 13.0 38 | 39 | 40 | Application 41 | false 42 | true 43 | Unicode 44 | Intel C++ Compiler XE 13.0 45 | 46 | 47 | Application 48 | false 49 | true 50 | Unicode 51 | Intel C++ Compiler XE 13.0 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | $(SolutionDir)$(Configuration)\$(Platform)\ 72 | obj\$(Configuration)\$(Platform)\ 73 | 74 | 75 | true 76 | $(SolutionDir)$(Configuration)\$(Platform)\ 77 | obj\$(Configuration)\$(Platform)\ 78 | 79 | 80 | false 81 | $(SolutionDir)$(Configuration)\$(Platform)\ 82 | obj\$(Configuration)\$(Platform)\ 83 | 84 | 85 | false 86 | $(SolutionDir)$(Configuration)\$(Platform)\ 87 | obj\$(Configuration)\$(Platform)\ 88 | 89 | 90 | 91 | 92 | 93 | Level3 94 | Disabled 95 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | 97 | 98 | Console 99 | true 100 | 101 | 102 | 103 | 104 | 105 | 106 | Level3 107 | Disabled 108 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | 119 | 120 | MaxSpeed 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | 125 | 126 | Console 127 | true 128 | true 129 | true 130 | 131 | 132 | 133 | 134 | Level3 135 | 136 | 137 | MaxSpeed 138 | true 139 | true 140 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /benchmarks/msvc12/winbench-intel.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | 35 | 36 | Source Files 37 | 38 | 39 | Source Files 40 | 41 | 42 | Source Files 43 | 44 | 45 | -------------------------------------------------------------------------------- /benchmarks/msvc12/winbench.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30501.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench", "winbench.vcxproj", "{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench-intel", "winbench-intel.vcxproj", "{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Win32 = Debug|Win32 13 | Debug|x64 = Debug|x64 14 | Release|Win32 = Release|Win32 15 | Release|x64 = Release|x64 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.ActiveCfg = Debug|Win32 19 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.Build.0 = Debug|Win32 20 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.ActiveCfg = Debug|Win32 21 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.Build.0 = Debug|Win32 22 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.ActiveCfg = Release|Win32 23 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.Build.0 = Release|Win32 24 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.ActiveCfg = Release|x64 25 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.Build.0 = Release|x64 26 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.ActiveCfg = Debug|Win32 27 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.Build.0 = Debug|Win32 28 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|x64.ActiveCfg = Debug|Win32 29 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.ActiveCfg = Release|Win32 30 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.Build.0 = Release|Win32 31 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|x64.ActiveCfg = Release|x64 32 | EndGlobalSection 33 | GlobalSection(SolutionProperties) = preSolution 34 | HideSolutionNode = FALSE 35 | EndGlobalSection 36 | EndGlobal 37 | -------------------------------------------------------------------------------- /benchmarks/msvc12/winbench.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2} 23 | Win32Proj 24 | winbench 25 | 26 | 27 | 28 | Application 29 | true 30 | Unicode 31 | v120 32 | 33 | 34 | Application 35 | true 36 | Unicode 37 | v120 38 | 39 | 40 | Application 41 | false 42 | true 43 | Unicode 44 | v120 45 | 46 | 47 | Application 48 | false 49 | true 50 | Unicode 51 | v120 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | $(SolutionDir)$(Configuration)\$(Platform)\ 72 | obj\$(Configuration)\$(Platform)\ 73 | 74 | 75 | true 76 | $(SolutionDir)$(Configuration)\$(Platform)\ 77 | obj\$(Configuration)\$(Platform)\ 78 | 79 | 80 | false 81 | $(SolutionDir)$(Configuration)\$(Platform)\ 82 | obj\$(Configuration)\$(Platform)\ 83 | 84 | 85 | false 86 | $(SolutionDir)$(Configuration)\$(Platform)\ 87 | obj\$(Configuration)\$(Platform)\ 88 | 89 | 90 | 91 | 92 | 93 | Level3 94 | Disabled 95 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | 97 | 98 | Console 99 | true 100 | 101 | 102 | 103 | 104 | 105 | 106 | Level3 107 | Disabled 108 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | 119 | 120 | MaxSpeed 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | 125 | 126 | Console 127 | true 128 | true 129 | true 130 | 131 | 132 | 133 | 134 | Level3 135 | 136 | 137 | MaxSpeed 138 | true 139 | true 140 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /benchmarks/msvc12/winbench.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | -------------------------------------------------------------------------------- /benchmarks/msvc14/winbench-intel.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9} 23 | Win32Proj 24 | winbenchintel 25 | 26 | 27 | 28 | Application 29 | true 30 | Unicode 31 | Intel C++ Compiler XE 13.0 32 | 33 | 34 | Application 35 | true 36 | Unicode 37 | Intel C++ Compiler XE 13.0 38 | 39 | 40 | Application 41 | false 42 | true 43 | Unicode 44 | Intel C++ Compiler XE 13.0 45 | 46 | 47 | Application 48 | false 49 | true 50 | Unicode 51 | Intel C++ Compiler XE 13.0 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | $(SolutionDir)$(Configuration)\$(Platform)\ 72 | obj\$(Configuration)\$(Platform)\ 73 | 74 | 75 | true 76 | $(SolutionDir)$(Configuration)\$(Platform)\ 77 | obj\$(Configuration)\$(Platform)\ 78 | 79 | 80 | false 81 | $(SolutionDir)$(Configuration)\$(Platform)\ 82 | obj\$(Configuration)\$(Platform)\ 83 | 84 | 85 | false 86 | $(SolutionDir)$(Configuration)\$(Platform)\ 87 | obj\$(Configuration)\$(Platform)\ 88 | 89 | 90 | 91 | 92 | 93 | Level3 94 | Disabled 95 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | 97 | 98 | Console 99 | true 100 | 101 | 102 | 103 | 104 | 105 | 106 | Level3 107 | Disabled 108 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | 119 | 120 | MaxSpeed 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | 125 | 126 | Console 127 | true 128 | true 129 | true 130 | 131 | 132 | 133 | 134 | Level3 135 | 136 | 137 | MaxSpeed 138 | true 139 | true 140 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /benchmarks/msvc14/winbench-intel.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | 35 | 36 | Source Files 37 | 38 | 39 | Source Files 40 | 41 | 42 | Source Files 43 | 44 | 45 | -------------------------------------------------------------------------------- /benchmarks/msvc14/winbench.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench", "winbench.vcxproj", "{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench-intel", "winbench-intel.vcxproj", "{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Win32 = Debug|Win32 13 | Debug|x64 = Debug|x64 14 | Release|Win32 = Release|Win32 15 | Release|x64 = Release|x64 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.ActiveCfg = Debug|Win32 19 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.Build.0 = Debug|Win32 20 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.ActiveCfg = Debug|Win32 21 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.Build.0 = Debug|Win32 22 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.ActiveCfg = Release|Win32 23 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.Build.0 = Release|Win32 24 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.ActiveCfg = Release|x64 25 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.Build.0 = Release|x64 26 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.ActiveCfg = Debug|Win32 27 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.Build.0 = Debug|Win32 28 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|x64.ActiveCfg = Debug|Win32 29 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.ActiveCfg = Release|Win32 30 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.Build.0 = Release|Win32 31 | {6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|x64.ActiveCfg = Release|x64 32 | EndGlobalSection 33 | GlobalSection(SolutionProperties) = preSolution 34 | HideSolutionNode = FALSE 35 | EndGlobalSection 36 | EndGlobal 37 | -------------------------------------------------------------------------------- /benchmarks/msvc14/winbench.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2} 23 | Win32Proj 24 | winbench 25 | 26 | 27 | 28 | Application 29 | true 30 | Unicode 31 | v140 32 | 33 | 34 | Application 35 | true 36 | Unicode 37 | v140 38 | 39 | 40 | Application 41 | false 42 | true 43 | Unicode 44 | v140 45 | 46 | 47 | Application 48 | false 49 | true 50 | Unicode 51 | v140 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | $(SolutionDir)$(Configuration)\$(Platform)\ 72 | obj\$(Configuration)\$(Platform)\ 73 | 74 | 75 | true 76 | $(SolutionDir)$(Configuration)\$(Platform)\ 77 | obj\$(Configuration)\$(Platform)\ 78 | 79 | 80 | false 81 | $(SolutionDir)$(Configuration)\$(Platform)\ 82 | obj\$(Configuration)\$(Platform)\ 83 | 84 | 85 | false 86 | $(SolutionDir)$(Configuration)\$(Platform)\ 87 | obj\$(Configuration)\$(Platform)\ 88 | 89 | 90 | 91 | 92 | 93 | Level3 94 | Disabled 95 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | 97 | 98 | Console 99 | true 100 | 101 | 102 | 103 | 104 | 105 | 106 | Level3 107 | Disabled 108 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | 119 | 120 | MaxSpeed 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | 125 | 126 | Console 127 | true 128 | true 129 | true 130 | 131 | 132 | 133 | 134 | Level3 135 | 136 | 137 | MaxSpeed 138 | true 139 | true 140 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /benchmarks/msvc14/winbench.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | -------------------------------------------------------------------------------- /benchmarks/systemtime.cpp: -------------------------------------------------------------------------------- 1 | // ©2013-2014 Cameron Desrochers 2 | 3 | #include "systemtime.h" 4 | #include 5 | 6 | #if defined(_MSC_VER) && _MSC_VER < 1700 7 | #include 8 | #define CompilerMemBar() _ReadWriteBarrier() 9 | #else 10 | #include 11 | #define CompilerMemBar() std::atomic_signal_fence(std::memory_order_seq_cst) 12 | #endif 13 | 14 | #if defined(ST_WINDOWS) 15 | 16 | #include 17 | 18 | namespace moodycamel 19 | { 20 | 21 | void sleep(int milliseconds) 22 | { 23 | ::Sleep(milliseconds); 24 | } 25 | 26 | SystemTime getSystemTime() 27 | { 28 | LARGE_INTEGER t; 29 | CompilerMemBar(); 30 | if (!QueryPerformanceCounter(&t)) { 31 | return static_cast(-1); 32 | } 33 | CompilerMemBar(); 34 | 35 | return static_cast(t.QuadPart); 36 | } 37 | 38 | double getTimeDelta(SystemTime start) 39 | { 40 | LARGE_INTEGER t; 41 | CompilerMemBar(); 42 | if (start == static_cast(-1) || !QueryPerformanceCounter(&t)) { 43 | return -1; 44 | } 45 | CompilerMemBar(); 46 | 47 | auto now = static_cast(t.QuadPart); 48 | 49 | LARGE_INTEGER f; 50 | if (!QueryPerformanceFrequency(&f)) { 51 | return -1; 52 | } 53 | 54 | return static_cast(static_cast<__int64>(now - start)) / f.QuadPart * 1000; 55 | } 56 | 57 | } // end namespace moodycamel 58 | 59 | #elif defined(ST_APPLE) 60 | 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | namespace moodycamel 67 | { 68 | 69 | void sleep(int milliseconds) 70 | { 71 | ::usleep(milliseconds * 1000); 72 | } 73 | 74 | SystemTime getSystemTime() 75 | { 76 | CompilerMemBar(); 77 | std::uint64_t result = mach_absolute_time(); 78 | CompilerMemBar(); 79 | 80 | return result; 81 | } 82 | 83 | double getTimeDelta(SystemTime start) 84 | { 85 | CompilerMemBar(); 86 | std::uint64_t end = mach_absolute_time(); 87 | CompilerMemBar(); 88 | 89 | mach_timebase_info_data_t tb = { 0 }; 90 | mach_timebase_info(&tb); 91 | double toNano = static_cast(tb.numer) / tb.denom; 92 | 93 | return static_cast(end - start) * toNano * 0.000001; 94 | } 95 | 96 | } // end namespace moodycamel 97 | 98 | #elif defined(ST_NIX) 99 | 100 | #include 101 | 102 | namespace moodycamel 103 | { 104 | 105 | void sleep(int milliseconds) 106 | { 107 | ::usleep(milliseconds * 1000); 108 | } 109 | 110 | SystemTime getSystemTime() 111 | { 112 | timespec t; 113 | CompilerMemBar(); 114 | if (clock_gettime(CLOCK_MONOTONIC_RAW, &t) != 0) { 115 | t.tv_sec = (time_t)-1; 116 | t.tv_nsec = -1; 117 | } 118 | CompilerMemBar(); 119 | 120 | return t; 121 | } 122 | 123 | double getTimeDelta(SystemTime start) 124 | { 125 | timespec t; 126 | CompilerMemBar(); 127 | if ((start.tv_sec == (time_t)-1 && start.tv_nsec == -1) || clock_gettime(CLOCK_MONOTONIC_RAW, &t) != 0) { 128 | return -1; 129 | } 130 | CompilerMemBar(); 131 | 132 | return static_cast(static_cast(t.tv_sec) - static_cast(start.tv_sec)) * 1000 + double(t.tv_nsec - start.tv_nsec) / 1000000; 133 | } 134 | 135 | } // end namespace moodycamel 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /benchmarks/systemtime.h: -------------------------------------------------------------------------------- 1 | // ©2013-2014 Cameron Desrochers 2 | 3 | #pragma once 4 | 5 | #if defined(_WIN32) 6 | #define ST_WINDOWS 7 | #elif defined(__APPLE__) && defined(__MACH__) 8 | #define ST_APPLE 9 | #elif defined(__linux__) || defined(__FreeBSD__) || defined(BSD) 10 | #define ST_NIX 11 | #else 12 | #error "Unknown platform" 13 | #endif 14 | 15 | #if defined(ST_WINDOWS) 16 | namespace moodycamel { typedef unsigned long long SystemTime; } 17 | #elif defined(ST_APPLE) 18 | #include 19 | namespace moodycamel { typedef std::uint64_t SystemTime; } 20 | #elif defined(ST_NIX) 21 | #include 22 | namespace moodycamel { typedef timespec SystemTime; } 23 | #endif 24 | 25 | namespace moodycamel 26 | { 27 | void sleep(int milliseconds); 28 | 29 | SystemTime getSystemTime(); 30 | 31 | // Returns the delta time, in milliseconds 32 | double getTimeDelta(SystemTime start); 33 | } 34 | -------------------------------------------------------------------------------- /readerwritercircularbuffer.h: -------------------------------------------------------------------------------- 1 | // ©2020 Cameron Desrochers. 2 | // Distributed under the simplified BSD license (see the license file that 3 | // should have come with this header). 4 | 5 | // Provides a C++11 implementation of a single-producer, single-consumer wait-free concurrent 6 | // circular buffer (fixed-size queue). 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // Note that this implementation is fully modern C++11 (not compatible with old MSVC versions) 18 | // but we still include atomicops.h for its LightweightSemaphore implementation. 19 | #include "atomicops.h" 20 | 21 | #ifndef MOODYCAMEL_CACHE_LINE_SIZE 22 | #define MOODYCAMEL_CACHE_LINE_SIZE 64 23 | #endif 24 | 25 | namespace moodycamel { 26 | 27 | template 28 | class BlockingReaderWriterCircularBuffer 29 | { 30 | public: 31 | typedef T value_type; 32 | 33 | public: 34 | explicit BlockingReaderWriterCircularBuffer(std::size_t capacity) 35 | : maxcap(capacity), mask(), rawData(), data(), 36 | slots_(new spsc_sema::LightweightSemaphore(static_cast(capacity))), 37 | items(new spsc_sema::LightweightSemaphore(0)), 38 | nextSlot(0), nextItem(0) 39 | { 40 | // Round capacity up to power of two to compute modulo mask. 41 | // Adapted from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 42 | --capacity; 43 | capacity |= capacity >> 1; 44 | capacity |= capacity >> 2; 45 | capacity |= capacity >> 4; 46 | for (std::size_t i = 1; i < sizeof(std::size_t); i <<= 1) 47 | capacity |= capacity >> (i << 3); 48 | mask = capacity++; 49 | rawData = static_cast(std::malloc(capacity * sizeof(T) + std::alignment_of::value - 1)); 50 | data = align_for(rawData); 51 | } 52 | 53 | BlockingReaderWriterCircularBuffer(BlockingReaderWriterCircularBuffer&& other) 54 | : maxcap(0), mask(0), rawData(nullptr), data(nullptr), 55 | slots_(new spsc_sema::LightweightSemaphore(0)), 56 | items(new spsc_sema::LightweightSemaphore(0)), 57 | nextSlot(), nextItem() 58 | { 59 | swap(other); 60 | } 61 | 62 | BlockingReaderWriterCircularBuffer(BlockingReaderWriterCircularBuffer const&) = delete; 63 | 64 | // Note: The queue should not be accessed concurrently while it's 65 | // being deleted. It's up to the user to synchronize this. 66 | ~BlockingReaderWriterCircularBuffer() 67 | { 68 | for (std::size_t i = 0, n = items->availableApprox(); i != n; ++i) 69 | reinterpret_cast(data)[(nextItem + i) & mask].~T(); 70 | std::free(rawData); 71 | } 72 | 73 | BlockingReaderWriterCircularBuffer& operator=(BlockingReaderWriterCircularBuffer&& other) noexcept 74 | { 75 | swap(other); 76 | return *this; 77 | } 78 | 79 | BlockingReaderWriterCircularBuffer& operator=(BlockingReaderWriterCircularBuffer const&) = delete; 80 | 81 | // Swaps the contents of this buffer with the contents of another. 82 | // Not thread-safe. 83 | void swap(BlockingReaderWriterCircularBuffer& other) noexcept 84 | { 85 | std::swap(maxcap, other.maxcap); 86 | std::swap(mask, other.mask); 87 | std::swap(rawData, other.rawData); 88 | std::swap(data, other.data); 89 | std::swap(slots_, other.slots_); 90 | std::swap(items, other.items); 91 | std::swap(nextSlot, other.nextSlot); 92 | std::swap(nextItem, other.nextItem); 93 | } 94 | 95 | // Enqueues a single item (by copying it). 96 | // Fails if not enough room to enqueue. 97 | // Thread-safe when called by producer thread. 98 | // No exception guarantee (state will be corrupted) if constructor of T throws. 99 | bool try_enqueue(T const& item) 100 | { 101 | if (!slots_->tryWait()) 102 | return false; 103 | inner_enqueue(item); 104 | return true; 105 | } 106 | 107 | // Enqueues a single item (by moving it, if possible). 108 | // Fails if not enough room to enqueue. 109 | // Thread-safe when called by producer thread. 110 | // No exception guarantee (state will be corrupted) if constructor of T throws. 111 | bool try_enqueue(T&& item) 112 | { 113 | if (!slots_->tryWait()) 114 | return false; 115 | inner_enqueue(std::move(item)); 116 | return true; 117 | } 118 | 119 | // Blocks the current thread until there's enough space to enqueue the given item, 120 | // then enqueues it (via copy). 121 | // Thread-safe when called by producer thread. 122 | // No exception guarantee (state will be corrupted) if constructor of T throws. 123 | void wait_enqueue(T const& item) 124 | { 125 | while (!slots_->wait()); 126 | inner_enqueue(item); 127 | } 128 | 129 | // Blocks the current thread until there's enough space to enqueue the given item, 130 | // then enqueues it (via move, if possible). 131 | // Thread-safe when called by producer thread. 132 | // No exception guarantee (state will be corrupted) if constructor of T throws. 133 | void wait_enqueue(T&& item) 134 | { 135 | while (!slots_->wait()); 136 | inner_enqueue(std::move(item)); 137 | } 138 | 139 | // Blocks the current thread until there's enough space to enqueue the given item, 140 | // or the timeout expires. Returns false without enqueueing the item if the timeout 141 | // expires, otherwise enqueues the item (via copy) and returns true. 142 | // Thread-safe when called by producer thread. 143 | // No exception guarantee (state will be corrupted) if constructor of T throws. 144 | bool wait_enqueue_timed(T const& item, std::int64_t timeout_usecs) 145 | { 146 | if (!slots_->wait(timeout_usecs)) 147 | return false; 148 | inner_enqueue(item); 149 | return true; 150 | } 151 | 152 | // Blocks the current thread until there's enough space to enqueue the given item, 153 | // or the timeout expires. Returns false without enqueueing the item if the timeout 154 | // expires, otherwise enqueues the item (via move, if possible) and returns true. 155 | // Thread-safe when called by producer thread. 156 | // No exception guarantee (state will be corrupted) if constructor of T throws. 157 | bool wait_enqueue_timed(T&& item, std::int64_t timeout_usecs) 158 | { 159 | if (!slots_->wait(timeout_usecs)) 160 | return false; 161 | inner_enqueue(std::move(item)); 162 | return true; 163 | } 164 | 165 | // Blocks the current thread until there's enough space to enqueue the given item, 166 | // or the timeout expires. Returns false without enqueueing the item if the timeout 167 | // expires, otherwise enqueues the item (via copy) and returns true. 168 | // Thread-safe when called by producer thread. 169 | // No exception guarantee (state will be corrupted) if constructor of T throws. 170 | template 171 | inline bool wait_enqueue_timed(T const& item, std::chrono::duration const& timeout) 172 | { 173 | return wait_enqueue_timed(item, std::chrono::duration_cast(timeout).count()); 174 | } 175 | 176 | // Blocks the current thread until there's enough space to enqueue the given item, 177 | // or the timeout expires. Returns false without enqueueing the item if the timeout 178 | // expires, otherwise enqueues the item (via move, if possible) and returns true. 179 | // Thread-safe when called by producer thread. 180 | // No exception guarantee (state will be corrupted) if constructor of T throws. 181 | template 182 | inline bool wait_enqueue_timed(T&& item, std::chrono::duration const& timeout) 183 | { 184 | return wait_enqueue_timed(std::move(item), std::chrono::duration_cast(timeout).count()); 185 | } 186 | 187 | // Attempts to dequeue a single item. 188 | // Returns false if the buffer is empty. 189 | // Thread-safe when called by consumer thread. 190 | // No exception guarantee (state will be corrupted) if assignment operator of U throws. 191 | template 192 | bool try_dequeue(U& item) 193 | { 194 | if (!items->tryWait()) 195 | return false; 196 | inner_dequeue(item); 197 | return true; 198 | } 199 | 200 | // Blocks the current thread until there's something to dequeue, then dequeues it. 201 | // Thread-safe when called by consumer thread. 202 | // No exception guarantee (state will be corrupted) if assignment operator of U throws. 203 | template 204 | void wait_dequeue(U& item) 205 | { 206 | while (!items->wait()); 207 | inner_dequeue(item); 208 | } 209 | 210 | // Blocks the current thread until either there's something to dequeue 211 | // or the timeout expires. Returns false without setting `item` if the 212 | // timeout expires, otherwise assigns to `item` and returns true. 213 | // Thread-safe when called by consumer thread. 214 | // No exception guarantee (state will be corrupted) if assignment operator of U throws. 215 | template 216 | bool wait_dequeue_timed(U& item, std::int64_t timeout_usecs) 217 | { 218 | if (!items->wait(timeout_usecs)) 219 | return false; 220 | inner_dequeue(item); 221 | return true; 222 | } 223 | 224 | // Blocks the current thread until either there's something to dequeue 225 | // or the timeout expires. Returns false without setting `item` if the 226 | // timeout expires, otherwise assigns to `item` and returns true. 227 | // Thread-safe when called by consumer thread. 228 | // No exception guarantee (state will be corrupted) if assignment operator of U throws. 229 | template 230 | inline bool wait_dequeue_timed(U& item, std::chrono::duration const& timeout) 231 | { 232 | return wait_dequeue_timed(item, std::chrono::duration_cast(timeout).count()); 233 | } 234 | 235 | // Returns a pointer to the next element in the queue (the one that would 236 | // be removed next by a call to `try_dequeue` or `try_pop`). If the queue 237 | // appears empty at the time the method is called, returns nullptr instead. 238 | // Thread-safe when called by consumer thread. 239 | inline T* peek() 240 | { 241 | if (!items->availableApprox()) 242 | return nullptr; 243 | return inner_peek(); 244 | } 245 | 246 | // Pops the next element from the queue, if there is one. 247 | // Thread-safe when called by consumer thread. 248 | inline bool try_pop() 249 | { 250 | if (!items->tryWait()) 251 | return false; 252 | inner_pop(); 253 | return true; 254 | } 255 | 256 | // Returns a (possibly outdated) snapshot of the total number of elements currently in the buffer. 257 | // Thread-safe. 258 | inline std::size_t size_approx() const 259 | { 260 | return items->availableApprox(); 261 | } 262 | 263 | // Returns the maximum number of elements that this circular buffer can hold at once. 264 | // Thread-safe. 265 | inline std::size_t max_capacity() const 266 | { 267 | return maxcap; 268 | } 269 | 270 | private: 271 | template 272 | void inner_enqueue(U&& item) 273 | { 274 | std::size_t i = nextSlot++; 275 | new (reinterpret_cast(data) + (i & mask)) T(std::forward(item)); 276 | items->signal(); 277 | } 278 | 279 | template 280 | void inner_dequeue(U& item) 281 | { 282 | std::size_t i = nextItem++; 283 | T& element = reinterpret_cast(data)[i & mask]; 284 | item = std::move(element); 285 | element.~T(); 286 | slots_->signal(); 287 | } 288 | 289 | T* inner_peek() 290 | { 291 | return reinterpret_cast(data) + (nextItem & mask); 292 | } 293 | 294 | void inner_pop() 295 | { 296 | std::size_t i = nextItem++; 297 | reinterpret_cast(data)[i & mask].~T(); 298 | slots_->signal(); 299 | } 300 | 301 | template 302 | static inline char* align_for(char* ptr) 303 | { 304 | const std::size_t alignment = std::alignment_of::value; 305 | return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; 306 | } 307 | 308 | private: 309 | std::size_t maxcap; // actual (non-power-of-two) capacity 310 | std::size_t mask; // circular buffer capacity mask (for cheap modulo) 311 | char* rawData; // raw circular buffer memory 312 | char* data; // circular buffer memory aligned to element alignment 313 | std::unique_ptr slots_; // number of slots currently free (named with underscore to accommodate Qt's 'slots' macro) 314 | std::unique_ptr items; // number of elements currently enqueued 315 | char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(char*) * 2 - sizeof(std::size_t) * 2 - sizeof(std::unique_ptr) * 2]; 316 | std::size_t nextSlot; // index of next free slot to enqueue into 317 | char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(std::size_t)]; 318 | std::size_t nextItem; // index of next element to dequeue from 319 | }; 320 | 321 | } 322 | -------------------------------------------------------------------------------- /readerwriterqueueConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake) 4 | -------------------------------------------------------------------------------- /tests/common/simplethread.cpp: -------------------------------------------------------------------------------- 1 | #include "simplethread.h" 2 | 3 | #if defined(_WIN32) 4 | #define WIN32_LEAN_AND_MEAN 5 | #include 6 | 7 | struct SimpleThread::ThreadRef 8 | { 9 | HANDLE handle; 10 | 11 | static DWORD WINAPI ThreadProc(LPVOID param) 12 | { 13 | auto threadRef = static_cast(param); 14 | threadRef->callbackFunc(threadRef->callbackObj); 15 | return 0; 16 | } 17 | 18 | ThreadRef(void* callbackObj, CallbackFunc callbackFunc) 19 | : callbackObj(callbackObj), callbackFunc(callbackFunc) 20 | { 21 | } 22 | 23 | void* callbackObj; 24 | CallbackFunc callbackFunc; 25 | }; 26 | 27 | void SimpleThread::startThread(void* callbackObj, CallbackFunc callbackFunc) 28 | { 29 | thread = new ThreadRef(callbackObj, callbackFunc); 30 | thread->handle = CreateThread(NULL, StackSize, &ThreadRef::ThreadProc, thread, 0, NULL); 31 | } 32 | 33 | void SimpleThread::join() 34 | { 35 | if (thread != nullptr && thread->handle != NULL) { 36 | WaitForSingleObject(thread->handle, INFINITE); 37 | CloseHandle(thread->handle); 38 | thread->handle = NULL; 39 | } 40 | } 41 | #else 42 | #include 43 | 44 | struct SimpleThread::ThreadRef 45 | { 46 | std::thread thread; 47 | 48 | static void threadProc(ThreadRef* threadRef) 49 | { 50 | threadRef->callbackFunc(threadRef->callbackObj); 51 | } 52 | 53 | ThreadRef(void* callbackObj, CallbackFunc callbackFunc) 54 | : callbackObj(callbackObj), callbackFunc(callbackFunc) 55 | { 56 | } 57 | 58 | void* callbackObj; 59 | CallbackFunc callbackFunc; 60 | }; 61 | 62 | void SimpleThread::startThread(void* callbackObj, CallbackFunc callbackFunc) 63 | { 64 | thread = new ThreadRef(callbackObj, callbackFunc); 65 | thread->thread = std::thread(&ThreadRef::threadProc, thread); 66 | } 67 | 68 | void SimpleThread::join() 69 | { 70 | if (thread != nullptr && thread->thread.joinable()) { 71 | thread->thread.join(); 72 | } 73 | } 74 | #endif 75 | 76 | SimpleThread::~SimpleThread() 77 | { 78 | if (thread != nullptr) { 79 | join(); 80 | delete thread; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/common/simplethread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Like C++11's std::thread, but with a reduced API, and works on Windows with MSVC2010+. 4 | // Wraps std::thread on other OSes. Perhaps the most significant departure between 5 | // std::thread and this mini-library is that join() is called implicitly in the destructor, 6 | // if the thread is joinable. The thread callback functions should not throw exceptions. 7 | 8 | #include 9 | #include 10 | 11 | 12 | namespace details 13 | { 14 | template 15 | struct ArgWrapper 16 | { 17 | typename std::remove_reference::type arg1; 18 | typename std::remove_reference::type arg2; 19 | typename std::remove_reference::type arg3; 20 | template 21 | ArgWrapper(T&& a1, U&& a2, V&& a3) : arg1(std::forward(a1)), arg2(std::forward(a2)), arg3(std::forward(a3)) { } 22 | template 23 | void callCallback(TCallback&& callback) const { std::forward(callback)(std::move(arg1), std::move(arg2), std::move(arg3)); } 24 | }; 25 | 26 | template 27 | struct ArgWrapper 28 | { 29 | typename std::remove_reference::type arg1; 30 | typename std::remove_reference::type arg2; 31 | template 32 | ArgWrapper(T&& a1, U&& a2) : arg1(std::forward(a1)), arg2(std::forward(a2)) { } 33 | template 34 | void callCallback(TCallback&& callback) const { std::forward(callback)(std::move(arg1), std::move(arg2)); } 35 | }; 36 | 37 | template 38 | struct ArgWrapper 39 | { 40 | typename std::remove_reference::type arg1; 41 | template 42 | ArgWrapper(T&& a1) : arg1(std::forward(a1)) { } 43 | template 44 | void callCallback(TCallback&& callback) const { std::forward(callback)(std::move(arg1)); } 45 | }; 46 | 47 | template<> struct ArgWrapper 48 | { 49 | template void callCallback(TCallback&& callback) const { std::forward(callback)(); } 50 | }; 51 | } 52 | 53 | 54 | class SimpleThread 55 | { 56 | private: 57 | struct ThreadRef; 58 | 59 | template 60 | struct CallbackWrapper 61 | { 62 | template 63 | CallbackWrapper(TCallback&& callback, U&& args) 64 | : callback(std::forward(callback)), args(std::forward(args)) 65 | { 66 | } 67 | 68 | static void callAndDelete(void* wrapper) 69 | { 70 | auto typedWrapper = static_cast(wrapper); 71 | typedWrapper->args.callCallback(std::move(typedWrapper->callback)); 72 | delete typedWrapper; 73 | } 74 | 75 | typename std::decay::type callback; 76 | TArgs args; 77 | }; 78 | 79 | typedef void (*CallbackFunc)(void*); 80 | 81 | void startThread(void* callbackObj, CallbackFunc callbackFunc); 82 | 83 | 84 | public: 85 | static const int StackSize = 4 * 1024; // bytes 86 | 87 | SimpleThread() : thread(nullptr) { } 88 | 89 | SimpleThread(SimpleThread&& other) 90 | : thread(other.thread) 91 | { 92 | other.thread = nullptr; 93 | } 94 | 95 | SimpleThread& operator=(SimpleThread&& other) 96 | { 97 | thread = other.thread; 98 | other.thread = nullptr; 99 | return *this; 100 | } 101 | 102 | // Disable copying and copy-assignment 103 | private: 104 | SimpleThread(SimpleThread const&); 105 | SimpleThread& operator=(SimpleThread const&); 106 | public: 107 | 108 | template 109 | explicit SimpleThread(TCallback&& callback) 110 | { 111 | auto wrapper = new CallbackWrapper>( 112 | std::forward(callback), 113 | details::ArgWrapper<>() 114 | ); 115 | startThread(wrapper, &CallbackWrapper>::callAndDelete); 116 | } 117 | 118 | template 119 | explicit SimpleThread(TCallback&& callback, TArg1&& arg1) 120 | { 121 | auto wrapper = new CallbackWrapper>( 122 | std::forward(callback), 123 | details::ArgWrapper(std::forward(arg1)) 124 | ); 125 | startThread(wrapper, &CallbackWrapper>::callAndDelete); 126 | } 127 | 128 | template 129 | explicit SimpleThread(TCallback&& callback, TArg1&& arg1, TArg2&& arg2) 130 | { 131 | auto wrapper = new CallbackWrapper>( 132 | std::forward(callback), 133 | details::ArgWrapper(std::forward(arg1), std::forward(arg2)) 134 | ); 135 | startThread(wrapper, &CallbackWrapper>::callAndDelete); 136 | } 137 | 138 | template 139 | explicit SimpleThread(TCallback&& callback, TArg1&& arg1, TArg2&& arg2, TArg3&& arg3) 140 | { 141 | auto wrapper = new CallbackWrapper>( 142 | std::forward(callback), 143 | details::ArgWrapper(std::forward(arg1), std::forward(arg2), std::forward(arg3)) 144 | ); 145 | startThread(wrapper, &CallbackWrapper>::callAndDelete); 146 | } 147 | 148 | ~SimpleThread(); 149 | 150 | void join(); 151 | 152 | private: 153 | ThreadRef* thread; 154 | }; 155 | -------------------------------------------------------------------------------- /tests/stabtest/makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(OS),Windows_NT) 2 | EXT=.exe 3 | PLATFORM_OPTS=-static 4 | PLATFORM_LD_OPTS=-Wl,--no-as-needed 5 | else 6 | UNAME_S := $(shell uname -s) 7 | ifeq ($(UNAME_S),Darwin) 8 | EXT= 9 | PLATFORM_OPTS= 10 | PLATFORM_LD_OPTS= 11 | else 12 | EXT= 13 | PLATFORM_OPTS= 14 | PLATFORM_LD_OPTS=-lrt -Wl,--no-as-needed 15 | endif 16 | endif 17 | 18 | default: stabtest$(EXT) 19 | 20 | stabtest$(EXT): stabtest.cpp ../../readerwriterqueue.h ../../atomicops.h ../common/simplethread.h ../common/simplethread.cpp makefile 21 | g++ $(PLATFORM_OPTS) -std=c++11 -Wsign-conversion -Wpedantic -Wall -DNDEBUG -O3 stabtest.cpp ../common/simplethread.cpp -o stabtest$(EXT) -pthread $(PLATFORM_LD_OPTS) 22 | 23 | run: stabtest$(EXT) 24 | ./stabtest$(EXT) 25 | -------------------------------------------------------------------------------- /tests/stabtest/msvc10/stabtest.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stabtest", "stabtest.vcxproj", "{16E74A53-972D-4762-BC18-8946FB1EF452}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Debug|x64 = Debug|x64 10 | Release|Win32 = Release|Win32 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.Build.0 = Debug|Win32 16 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.ActiveCfg = Debug|x64 17 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.Build.0 = Debug|x64 18 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.ActiveCfg = Release|Win32 19 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.Build.0 = Release|Win32 20 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.ActiveCfg = Release|x64 21 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.Build.0 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /tests/stabtest/msvc10/stabtest.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {16E74A53-972D-4762-BC18-8946FB1EF452} 23 | Win32Proj 24 | stabtest 25 | 26 | 27 | 28 | Application 29 | true 30 | Unicode 31 | 32 | 33 | Application 34 | true 35 | Unicode 36 | 37 | 38 | Application 39 | false 40 | true 41 | Unicode 42 | 43 | 44 | Application 45 | false 46 | true 47 | Unicode 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | true 67 | $(SolutionDir)$(Configuration)\$(Platform) 68 | obj\$(Configuration)\$(Platform) 69 | 70 | 71 | true 72 | $(SolutionDir)$(Configuration)\$(Platform) 73 | obj\$(Configuration)\$(Platform) 74 | 75 | 76 | false 77 | $(SolutionDir)$(Configuration)\$(Platform) 78 | obj\$(Configuration)\$(Platform) 79 | 80 | 81 | false 82 | $(SolutionDir)$(Configuration)\$(Platform) 83 | obj\$(Configuration)\$(Platform) 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | 93 | 94 | Console 95 | true 96 | 97 | 98 | 99 | 100 | 101 | 102 | Level3 103 | Disabled 104 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 105 | 106 | 107 | Console 108 | true 109 | 110 | 111 | 112 | 113 | Level3 114 | 115 | 116 | MaxSpeed 117 | true 118 | true 119 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 120 | 121 | 122 | Console 123 | true 124 | true 125 | true 126 | 127 | 128 | 129 | 130 | Level3 131 | 132 | 133 | MaxSpeed 134 | true 135 | true 136 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 137 | 138 | 139 | Console 140 | true 141 | true 142 | true 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /tests/stabtest/msvc10/stabtest.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | 26 | 27 | Header Files 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | -------------------------------------------------------------------------------- /tests/stabtest/msvc12/stabtest.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30501.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stabtest", "stabtest.vcxproj", "{16E74A53-972D-4762-BC18-8946FB1EF452}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug|x64 = Debug|x64 12 | Release|Win32 = Release|Win32 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.ActiveCfg = Debug|Win32 17 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.Build.0 = Debug|Win32 18 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.ActiveCfg = Debug|x64 19 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.Build.0 = Debug|x64 20 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.ActiveCfg = Release|Win32 21 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.Build.0 = Release|Win32 22 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.ActiveCfg = Release|x64 23 | {16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /tests/stabtest/msvc12/stabtest.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {16E74A53-972D-4762-BC18-8946FB1EF452} 23 | Win32Proj 24 | stabtest 25 | 26 | 27 | 28 | Application 29 | true 30 | Unicode 31 | v120 32 | 33 | 34 | Application 35 | true 36 | Unicode 37 | v120 38 | 39 | 40 | Application 41 | false 42 | true 43 | Unicode 44 | v120 45 | 46 | 47 | Application 48 | false 49 | true 50 | Unicode 51 | v120 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | $(SolutionDir)$(Configuration)\$(Platform) 72 | obj\$(Configuration)\$(Platform) 73 | 74 | 75 | true 76 | $(SolutionDir)$(Configuration)\$(Platform) 77 | obj\$(Configuration)\$(Platform) 78 | 79 | 80 | false 81 | $(SolutionDir)$(Configuration)\$(Platform) 82 | obj\$(Configuration)\$(Platform) 83 | 84 | 85 | false 86 | $(SolutionDir)$(Configuration)\$(Platform) 87 | obj\$(Configuration)\$(Platform) 88 | 89 | 90 | 91 | 92 | 93 | Level3 94 | Disabled 95 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | 97 | 98 | Console 99 | true 100 | 101 | 102 | 103 | 104 | 105 | 106 | Level3 107 | Disabled 108 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | 119 | 120 | MaxSpeed 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | 125 | 126 | Console 127 | true 128 | true 129 | true 130 | 131 | 132 | 133 | 134 | Level3 135 | 136 | 137 | MaxSpeed 138 | true 139 | true 140 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /tests/stabtest/msvc12/stabtest.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | 26 | 27 | Header Files 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | -------------------------------------------------------------------------------- /tests/stabtest/stabtest.cpp: -------------------------------------------------------------------------------- 1 | #include "../../readerwriterqueue.h" 2 | #include "../common/simplethread.h" 3 | 4 | using namespace moodycamel; 5 | 6 | #include 7 | #include 8 | #include 9 | #include // rand() 10 | //#include // usleep() 11 | 12 | void unpredictableDelay(int extra = 0) 13 | { 14 | /* if ((rand() & 4095) == 0) { 15 | usleep(2000 + extra); // in microseconds 16 | }*/ 17 | } 18 | 19 | int main(int argc, char** argv) 20 | { 21 | // Disable buffering (so that when run in, e.g., Sublime Text, the output appears as it is written) 22 | std::setvbuf(stdout, nullptr, _IONBF, 0); 23 | 24 | std::printf("Running stability test for moodycamel::ReaderWriterQueue.\n"); 25 | std::printf("Logging to 'log.txt'. Press CTRL+C to quit.\n\n"); 26 | 27 | 28 | std::ofstream log("log.txt"); 29 | 30 | try { 31 | for (unsigned int i = 0; true; ++i) { 32 | log << "Test #" << i << std::endl; 33 | std::printf("Test #%d\n", i); 34 | 35 | ReaderWriterQueue q((rand() % 32) + 1); 36 | 37 | SimpleThread writer([&]() { 38 | for (unsigned long long j = 0; j < 1024ULL * 1024ULL * 32ULL; ++j) { 39 | unpredictableDelay(500); 40 | q.enqueue(j); 41 | } 42 | }); 43 | 44 | SimpleThread reader([&]() { 45 | bool canLog = true; 46 | unsigned long long element; 47 | for (unsigned long long j = 0; j < 1024ULL * 1024ULL * 32ULL;) { 48 | if (canLog && (j & (1024 * 1024 * 16 - 1)) == 0) { 49 | log << " ... iteration " << j << std::endl; 50 | std::printf(" ... iteration %llu\n", j); 51 | canLog = false; 52 | } 53 | unpredictableDelay(); 54 | if (q.try_dequeue(element)) { 55 | if (element != j) { 56 | log << " ERROR DETECTED: Expected to read " << j << " but found " << element << std::endl; 57 | std::printf(" ERROR DETECTED: Expected to read %llu but found %llu", j, element); 58 | } 59 | ++j; 60 | canLog = true; 61 | } 62 | } 63 | if (q.try_dequeue(element)) { 64 | log << " ERROR DETECTED: Expected queue to be empty" << std::endl; 65 | std::printf(" ERROR DETECTED: Expected queue to be empty\n"); 66 | } 67 | }); 68 | 69 | writer.join(); 70 | reader.join(); 71 | } 72 | } 73 | catch (std::exception const& ex) { 74 | log << " ERROR DETECTED: Exception thrown: " << ex.what() << std::endl; 75 | std::printf(" ERROR DETECTED: Exception thrown: %s\n", ex.what()); 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | -------------------------------------------------------------------------------- /tests/unittests/makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | ifeq ($(OS),Windows_NT) 4 | EXT=.exe 5 | PLATFORM_OPTS=-static 6 | PLATFORM_LD_OPTS=-Wl,--no-as-needed 7 | else 8 | UNAME_S := $(shell uname -s) 9 | ifeq ($(UNAME_S),Darwin) 10 | EXT= 11 | PLATFORM_OPTS= 12 | PLATFORM_LD_OPTS= 13 | else 14 | EXT= 15 | PLATFORM_OPTS= 16 | PLATFORM_LD_OPTS=-lrt -Wl,--no-as-needed 17 | endif 18 | endif 19 | 20 | 21 | default: unittests$(EXT) 22 | 23 | unittests$(EXT): unittests.cpp ../../readerwriterqueue.h ../../readerwritercircularbuffer.h ../../atomicops.h ../common/simplethread.h ../common/simplethread.cpp minitest.h makefile 24 | 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) 25 | 26 | run: unittests$(EXT) 27 | ./unittests$(EXT) 28 | -------------------------------------------------------------------------------- /tests/unittests/minitest.h: -------------------------------------------------------------------------------- 1 | // ©2013-2014 Cameron Desrochers. 2 | // Distributed under the simplified BSD license (see the LICENSE file that 3 | // should have come with this header). 4 | 5 | // Provides an extremely basic unit testing framework. 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef __GNUG__ 17 | #include 18 | #include 19 | #endif 20 | 21 | 22 | 23 | #define REGISTER_TEST(testName) registerTest(#testName, &subclass_t::testName) 24 | 25 | #define ASSERT_OR_FAIL(expr) { if (!(expr)) { notifyTestFailed(__LINE__, #expr); return false; } } 26 | #define SUCCEED() { return true; } 27 | 28 | 29 | 30 | // Uses CRTP 31 | template 32 | class TestClass 33 | { 34 | public: 35 | static void notifyTestFailed(int line, const char* expr) 36 | { 37 | std::printf(" FAILED!\n ******* Assertion failed (line %d): %s\n\n", line, expr); 38 | } 39 | 40 | bool validateTestName(std::string const& which) const 41 | { 42 | return testMap.find(which) != testMap.end(); 43 | } 44 | 45 | void getAllTestNames(std::vector& names) const 46 | { 47 | for (auto it = testMap.cbegin(); it != testMap.cend(); ++it) { 48 | names.push_back(it->first); 49 | } 50 | } 51 | 52 | bool run(unsigned int iterations = 1) 53 | { 54 | bool success = true; 55 | for (auto it = testVec.cbegin(); it != testVec.cend(); ++it) { 56 | if (!execTest(*it, iterations)) { 57 | success = false; 58 | } 59 | } 60 | return success; 61 | } 62 | 63 | bool run(std::vector const& which, unsigned int iterations = 1) 64 | { 65 | bool success = true; 66 | for (auto it = which.begin(); it != which.end(); ++it) { 67 | if (!execTest(*testMap.find(*it), iterations)) { 68 | success = false; 69 | } 70 | } 71 | return success; 72 | } 73 | 74 | protected: 75 | typedef TSubclass subclass_t; 76 | 77 | void registerTest(const char* name, bool (subclass_t::* method)()) 78 | { 79 | testVec.push_back(std::make_pair(std::string(name), method)); 80 | testMap[std::string(name)] = method; 81 | } 82 | 83 | bool execTest(std::pair const& testRef, unsigned int iterations) 84 | { 85 | std::printf("%s::%s... \n", demangle_type_name(typeid(subclass_t).name()).c_str(), testRef.first.c_str()); 86 | 87 | bool result = true; 88 | for (unsigned int i = 0; i != iterations; ++i) { 89 | if (!(static_cast(this)->*testRef.second)()) { 90 | result = false; 91 | break; 92 | } 93 | } 94 | 95 | if (result) { 96 | std::printf(" passed\n\n"); 97 | } 98 | else { 99 | std::printf(" FAILED!\n\n"); 100 | } 101 | return result; 102 | } 103 | 104 | private: 105 | static std::string demangle_type_name(const char* name) 106 | { 107 | #ifdef __GNUG__ 108 | // Adapted from http://stackoverflow.com/a/4541470/21475 109 | int status = -4; 110 | char* res = abi::__cxa_demangle(name, nullptr, nullptr, &status); 111 | 112 | const char* const demangled_name = (status == 0) ? res : name; 113 | std::string ret(demangled_name); 114 | 115 | std::free(res); 116 | return ret; 117 | #else 118 | return name; 119 | #endif 120 | } 121 | 122 | protected: 123 | std::vector > testVec; 124 | std::map testMap; 125 | }; 126 | -------------------------------------------------------------------------------- /tests/unittests/msvc10/unittests.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittests", "unittests.vcxproj", "{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Debug|x64 = Debug|x64 10 | Release|Win32 = Release|Win32 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|Win32.Build.0 = Debug|Win32 16 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|x64.ActiveCfg = Debug|x64 17 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|x64.Build.0 = Debug|x64 18 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|Win32.ActiveCfg = Release|Win32 19 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|Win32.Build.0 = Release|Win32 20 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|x64.ActiveCfg = Release|x64 21 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|x64.Build.0 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /tests/unittests/msvc10/unittests.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B} 23 | Win32Proj 24 | unittests 25 | 26 | 27 | 28 | Application 29 | true 30 | Unicode 31 | 32 | 33 | Application 34 | true 35 | Unicode 36 | 37 | 38 | Application 39 | false 40 | true 41 | Unicode 42 | 43 | 44 | Application 45 | false 46 | true 47 | Unicode 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | true 67 | obj\$(Configuration)\$(Platform)\ 68 | $(SolutionDir)$(Configuration)\$(Platform)\ 69 | 70 | 71 | true 72 | obj\$(Configuration)\$(Platform)\ 73 | $(SolutionDir)$(Configuration)\$(Platform)\ 74 | 75 | 76 | false 77 | obj\$(Configuration)\$(Platform)\ 78 | $(SolutionDir)$(Configuration)\$(Platform)\ 79 | 80 | 81 | false 82 | obj\$(Configuration)\$(Platform)\ 83 | $(SolutionDir)$(Configuration)\$(Platform)\ 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | 93 | 94 | Console 95 | true 96 | 97 | 98 | 99 | 100 | 101 | 102 | Level3 103 | Disabled 104 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 105 | 106 | 107 | Console 108 | true 109 | 110 | 111 | 112 | 113 | Level3 114 | 115 | 116 | MaxSpeed 117 | true 118 | true 119 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 120 | 121 | 122 | Console 123 | true 124 | true 125 | true 126 | 127 | 128 | 129 | 130 | Level3 131 | 132 | 133 | MaxSpeed 134 | true 135 | true 136 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 137 | 138 | 139 | Console 140 | true 141 | true 142 | true 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /tests/unittests/msvc10/unittests.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | 32 | 33 | Source Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | -------------------------------------------------------------------------------- /tests/unittests/msvc12/unittests.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30501.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittests", "unittests.vcxproj", "{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug|x64 = Debug|x64 12 | Release|Win32 = Release|Win32 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|Win32.ActiveCfg = Debug|Win32 17 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|Win32.Build.0 = Debug|Win32 18 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|x64.ActiveCfg = Debug|x64 19 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|x64.Build.0 = Debug|x64 20 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|Win32.ActiveCfg = Release|Win32 21 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|Win32.Build.0 = Release|Win32 22 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|x64.ActiveCfg = Release|x64 23 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /tests/unittests/msvc12/unittests.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {C209657D-56BF-4A61-8FD2-DBAEB1E51B3B} 23 | Win32Proj 24 | unittests 25 | 26 | 27 | 28 | Application 29 | true 30 | Unicode 31 | v120 32 | 33 | 34 | Application 35 | true 36 | Unicode 37 | v120 38 | 39 | 40 | Application 41 | false 42 | true 43 | Unicode 44 | v120 45 | 46 | 47 | Application 48 | false 49 | true 50 | Unicode 51 | v120 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | obj\$(Configuration)\$(Platform)\ 72 | $(SolutionDir)$(Configuration)\$(Platform)\ 73 | 74 | 75 | true 76 | obj\$(Configuration)\$(Platform)\ 77 | $(SolutionDir)$(Configuration)\$(Platform)\ 78 | 79 | 80 | false 81 | obj\$(Configuration)\$(Platform)\ 82 | $(SolutionDir)$(Configuration)\$(Platform)\ 83 | 84 | 85 | false 86 | obj\$(Configuration)\$(Platform)\ 87 | $(SolutionDir)$(Configuration)\$(Platform)\ 88 | 89 | 90 | 91 | 92 | 93 | Level3 94 | Disabled 95 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | 97 | 98 | Console 99 | true 100 | 101 | 102 | 103 | 104 | 105 | 106 | Level3 107 | Disabled 108 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | 119 | 120 | MaxSpeed 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | 125 | 126 | Console 127 | true 128 | true 129 | true 130 | 131 | 132 | 133 | 134 | Level3 135 | 136 | 137 | MaxSpeed 138 | true 139 | true 140 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /tests/unittests/msvc12/unittests.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | 32 | 33 | Source Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | -------------------------------------------------------------------------------- /tests/unittests/unittests.cpp: -------------------------------------------------------------------------------- 1 | // ©2013-2015 Cameron Desrochers 2 | // Unit tests for moodycamel::ReaderWriterQueue 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "minitest.h" 11 | #include "../common/simplethread.h" 12 | #include "../../readerwriterqueue.h" 13 | #include "../../readerwritercircularbuffer.h" 14 | 15 | using namespace moodycamel; 16 | 17 | 18 | // *NOT* thread-safe 19 | struct Foo 20 | { 21 | Foo() : copied(false) { id = _id()++; } 22 | Foo(Foo const& other) : id(other.id), copied(true) { } 23 | Foo(Foo&& other) : id(other.id), copied(other.copied) { other.copied = true; } 24 | Foo& operator=(Foo&& other) 25 | { 26 | verify(); 27 | id = other.id, copied = other.copied; 28 | other.copied = true; 29 | return *this; 30 | } 31 | ~Foo() { verify(); } 32 | 33 | private: 34 | void verify() 35 | { 36 | if (copied) return; 37 | if (id != _last_destroyed_id() + 1) { 38 | _destroyed_in_order() = false; 39 | } 40 | _last_destroyed_id() = id; 41 | ++_destroy_count(); 42 | } 43 | 44 | public: 45 | static void reset() { _destroy_count() = 0; _id() = 0; _destroyed_in_order() = true; _last_destroyed_id() = -1; } 46 | static int destroy_count() { return _destroy_count(); } 47 | static bool destroyed_in_order() { return _destroyed_in_order(); } 48 | 49 | private: 50 | static int& _destroy_count() { static int c = 0; return c; } 51 | static int& _id() { static int i = 0; return i; } 52 | static bool& _destroyed_in_order() { static bool d = true; return d; } 53 | static int& _last_destroyed_id() { static int i = -1; return i; } 54 | 55 | int id; 56 | bool copied; 57 | }; 58 | 59 | 60 | #if MOODYCAMEL_HAS_EMPLACE 61 | class UniquePtrWrapper 62 | { 63 | public: 64 | UniquePtrWrapper() = default; 65 | UniquePtrWrapper(std::unique_ptr p) : m_p(std::move(p)) {} 66 | int get_value() const { return *m_p; } 67 | std::unique_ptr& get_ptr() { return m_p; } 68 | private: 69 | std::unique_ptr m_p; 70 | }; 71 | #endif 72 | 73 | /// Extracted from private static method of ReaderWriterQueue 74 | static size_t ceilToPow2(size_t x) 75 | { 76 | // From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 77 | --x; 78 | x |= x >> 1; 79 | x |= x >> 2; 80 | x |= x >> 4; 81 | for (size_t i = 1; i < sizeof(size_t); i <<= 1) { 82 | x |= x >> (i << 3); 83 | } 84 | ++x; 85 | return x; 86 | } 87 | 88 | 89 | class ReaderWriterQueueTests : public TestClass 90 | { 91 | public: 92 | ReaderWriterQueueTests() 93 | { 94 | REGISTER_TEST(create_empty_queue); 95 | REGISTER_TEST(enqueue_one); 96 | REGISTER_TEST(enqueue_many); 97 | REGISTER_TEST(nonempty_destroy); 98 | REGISTER_TEST(try_enqueue); 99 | REGISTER_TEST(try_dequeue); 100 | REGISTER_TEST(peek); 101 | REGISTER_TEST(pop); 102 | REGISTER_TEST(size_approx); 103 | REGISTER_TEST(max_capacity); 104 | REGISTER_TEST(threaded); 105 | REGISTER_TEST(blocking); 106 | REGISTER_TEST(vector); 107 | #if MOODYCAMEL_HAS_EMPLACE 108 | REGISTER_TEST(emplace); 109 | REGISTER_TEST(try_enqueue_fail_workaround); 110 | REGISTER_TEST(try_emplace_fail); 111 | #endif 112 | REGISTER_TEST(blocking_circular_buffer); 113 | } 114 | 115 | bool create_empty_queue() 116 | { 117 | { 118 | ReaderWriterQueue q; 119 | } 120 | 121 | { 122 | ReaderWriterQueue q(1234); 123 | } 124 | 125 | return true; 126 | } 127 | 128 | bool enqueue_one() 129 | { 130 | int item; 131 | 132 | { 133 | item = 0; 134 | ReaderWriterQueue q(1); 135 | q.enqueue(12345); 136 | ASSERT_OR_FAIL(q.try_dequeue(item)); 137 | ASSERT_OR_FAIL(item == 12345); 138 | } 139 | 140 | { 141 | item = 0; 142 | ReaderWriterQueue q(1); 143 | ASSERT_OR_FAIL(q.try_enqueue(12345)); 144 | ASSERT_OR_FAIL(q.try_dequeue(item)); 145 | ASSERT_OR_FAIL(item == 12345); 146 | } 147 | 148 | return true; 149 | } 150 | 151 | bool enqueue_many() 152 | { 153 | int item = -1; 154 | 155 | { 156 | ReaderWriterQueue q(100); 157 | for (int i = 0; i != 100; ++i) { 158 | q.enqueue(i); 159 | } 160 | 161 | for (int i = 0; i != 100; ++i) { 162 | ASSERT_OR_FAIL(q.try_dequeue(item)); 163 | ASSERT_OR_FAIL(item == i); 164 | } 165 | } 166 | 167 | { 168 | ReaderWriterQueue q(100); 169 | for (int i = 0; i != 1200; ++i) { 170 | q.enqueue(i); 171 | } 172 | 173 | for (int i = 0; i != 1200; ++i) { 174 | ASSERT_OR_FAIL(q.try_dequeue(item)); 175 | ASSERT_OR_FAIL(item == i); 176 | } 177 | } 178 | 179 | return true; 180 | } 181 | 182 | bool nonempty_destroy() 183 | { 184 | // Some elements at beginning 185 | Foo::reset(); 186 | { 187 | ReaderWriterQueue q(31); 188 | for (int i = 0; i != 10; ++i) { 189 | q.enqueue(Foo()); 190 | } 191 | ASSERT_OR_FAIL(Foo::destroy_count() == 0); 192 | } 193 | ASSERT_OR_FAIL(Foo::destroy_count() == 10); 194 | ASSERT_OR_FAIL(Foo::destroyed_in_order()); 195 | 196 | // Entire block 197 | Foo::reset(); 198 | { 199 | ReaderWriterQueue q(31); 200 | for (int i = 0; i != 31; ++i) { 201 | q.enqueue(Foo()); 202 | } 203 | ASSERT_OR_FAIL(Foo::destroy_count() == 0); 204 | } 205 | ASSERT_OR_FAIL(Foo::destroy_count() == 31); 206 | ASSERT_OR_FAIL(Foo::destroyed_in_order()); 207 | 208 | // Multiple blocks 209 | Foo::reset(); 210 | { 211 | ReaderWriterQueue q(31); 212 | for (int i = 0; i != 94; ++i) { 213 | q.enqueue(Foo()); 214 | } 215 | ASSERT_OR_FAIL(Foo::destroy_count() == 0); 216 | } 217 | ASSERT_OR_FAIL(Foo::destroy_count() == 94); 218 | ASSERT_OR_FAIL(Foo::destroyed_in_order()); 219 | 220 | // Some elements in another block 221 | Foo::reset(); 222 | { 223 | ReaderWriterQueue q(31); 224 | Foo item; 225 | for (int i = 0; i != 42; ++i) { 226 | q.enqueue(Foo()); 227 | } 228 | ASSERT_OR_FAIL(Foo::destroy_count() == 0); 229 | for (int i = 0; i != 31; ++i) { 230 | ASSERT_OR_FAIL(q.try_dequeue(item)); 231 | } 232 | ASSERT_OR_FAIL(Foo::destroy_count() == 31); 233 | } 234 | ASSERT_OR_FAIL(Foo::destroy_count() == 43); 235 | ASSERT_OR_FAIL(Foo::destroyed_in_order()); 236 | 237 | // Some elements in multiple blocks 238 | Foo::reset(); 239 | { 240 | ReaderWriterQueue q(31); 241 | Foo item; 242 | for (int i = 0; i != 123; ++i) { 243 | q.enqueue(Foo()); 244 | } 245 | for (int i = 0; i != 25; ++i) { 246 | ASSERT_OR_FAIL(q.try_dequeue(item)); 247 | } 248 | for (int i = 0; i != 47; ++i) { 249 | q.enqueue(Foo()); 250 | } 251 | for (int i = 0; i != 140; ++i) { 252 | ASSERT_OR_FAIL(q.try_dequeue(item)); 253 | } 254 | for (int i = 0; i != 230; ++i) { 255 | q.enqueue(Foo()); 256 | } 257 | for (int i = 0; i != 130; ++i) { 258 | ASSERT_OR_FAIL(q.try_dequeue(item)); 259 | } 260 | for (int i = 0; i != 100; ++i) { 261 | q.enqueue(Foo()); 262 | } 263 | } 264 | ASSERT_OR_FAIL(Foo::destroy_count() == 501); 265 | ASSERT_OR_FAIL(Foo::destroyed_in_order()); 266 | 267 | return true; 268 | } 269 | 270 | bool try_enqueue() 271 | { 272 | ReaderWriterQueue q(31); 273 | int item; 274 | int size = 0; 275 | 276 | for (int i = 0; i < 10000; ++i) { 277 | if ((rand() & 1) == 1) { 278 | bool result = q.try_enqueue(i); 279 | if (size == 31) { 280 | ASSERT_OR_FAIL(!result); 281 | } 282 | else { 283 | ASSERT_OR_FAIL(result); 284 | ++size; 285 | } 286 | } 287 | else { 288 | bool result = q.try_dequeue(item); 289 | if (size == 0) { 290 | ASSERT_OR_FAIL(!result); 291 | } 292 | else { 293 | ASSERT_OR_FAIL(result); 294 | --size; 295 | } 296 | } 297 | } 298 | 299 | return true; 300 | } 301 | 302 | bool try_dequeue() 303 | { 304 | int item; 305 | 306 | { 307 | ReaderWriterQueue q(1); 308 | ASSERT_OR_FAIL(!q.try_dequeue(item)); 309 | } 310 | 311 | { 312 | ReaderWriterQueue q(10); 313 | ASSERT_OR_FAIL(!q.try_dequeue(item)); 314 | } 315 | 316 | return true; 317 | } 318 | 319 | bool threaded() 320 | { 321 | weak_atomic result; 322 | result = 1; 323 | 324 | ReaderWriterQueue q(100); 325 | SimpleThread reader([&]() { 326 | int item; 327 | int prevItem = -1; 328 | for (int i = 0; i != 1000000; ++i) { 329 | if (q.try_dequeue(item)) { 330 | if (item <= prevItem) { 331 | result = 0; 332 | } 333 | prevItem = item; 334 | } 335 | } 336 | }); 337 | SimpleThread writer([&]() { 338 | for (int i = 0; i != 1000000; ++i) { 339 | if (((i >> 7) & 1) == 0) { 340 | q.enqueue(i); 341 | } 342 | else { 343 | q.try_enqueue(i); 344 | } 345 | } 346 | }); 347 | 348 | writer.join(); 349 | reader.join(); 350 | 351 | return result.load() == 1 ? true : false; 352 | } 353 | 354 | bool peek() 355 | { 356 | weak_atomic result; 357 | result = 1; 358 | 359 | ReaderWriterQueue q(100); 360 | SimpleThread reader([&]() { 361 | int item; 362 | int prevItem = -1; 363 | int* peeked; 364 | for (int i = 0; i != 100000; ++i) { 365 | peeked = q.peek(); 366 | if (peeked != nullptr) { 367 | if (q.try_dequeue(item)) { 368 | if (item <= prevItem || item != *peeked) { 369 | result = 0; 370 | } 371 | prevItem = item; 372 | } 373 | else { 374 | result = 0; 375 | } 376 | } 377 | } 378 | }); 379 | SimpleThread writer([&]() { 380 | for (int i = 0; i != 100000; ++i) { 381 | if (((i >> 7) & 1) == 0) { 382 | q.enqueue(i); 383 | } 384 | else { 385 | q.try_enqueue(i); 386 | } 387 | } 388 | }); 389 | 390 | writer.join(); 391 | reader.join(); 392 | 393 | return result.load() == 1 ? true : false; 394 | } 395 | 396 | bool pop() 397 | { 398 | weak_atomic result; 399 | result = 1; 400 | 401 | ReaderWriterQueue q(100); 402 | SimpleThread reader([&]() { 403 | int item; 404 | int prevItem = -1; 405 | int* peeked; 406 | for (int i = 0; i != 100000; ++i) { 407 | peeked = q.peek(); 408 | if (peeked != nullptr) { 409 | item = *peeked; 410 | if (q.pop()) { 411 | if (item <= prevItem) { 412 | result = 0; 413 | } 414 | prevItem = item; 415 | } 416 | else { 417 | result = 0; 418 | } 419 | } 420 | } 421 | }); 422 | SimpleThread writer([&]() { 423 | for (int i = 0; i != 100000; ++i) { 424 | if (((i >> 7) & 1) == 0) { 425 | q.enqueue(i); 426 | } 427 | else { 428 | q.try_enqueue(i); 429 | } 430 | } 431 | }); 432 | 433 | writer.join(); 434 | reader.join(); 435 | 436 | return result.load() == 1 ? true : false; 437 | } 438 | 439 | bool size_approx() 440 | { 441 | weak_atomic result; 442 | weak_atomic front; 443 | weak_atomic tail; 444 | 445 | result = 1; 446 | front = 0; 447 | tail = 0; 448 | 449 | ReaderWriterQueue q(10); 450 | SimpleThread reader([&]() { 451 | int item; 452 | for (int i = 0; i != 100000; ++i) { 453 | if (q.try_dequeue(item)) { 454 | fence(memory_order_release); 455 | front = front.load() + 1; 456 | } 457 | int size = static_cast(q.size_approx()); 458 | fence(memory_order_acquire); 459 | int tail_ = tail.load(); 460 | int front_ = front.load(); 461 | if (size > tail_ - front_ || size < 0) { 462 | result = 0; 463 | } 464 | } 465 | }); 466 | SimpleThread writer([&]() { 467 | for (int i = 0; i != 100000; ++i) { 468 | tail = tail.load() + 1; 469 | fence(memory_order_release); 470 | q.enqueue(i); 471 | int tail_ = tail.load(); 472 | int front_ = front.load(); 473 | fence(memory_order_acquire); 474 | int size = static_cast(q.size_approx()); 475 | if (size > tail_ - front_ || size < 0) { 476 | result = 0; 477 | } 478 | } 479 | }); 480 | 481 | writer.join(); 482 | reader.join(); 483 | 484 | return result.load() == 1 ? true : false; 485 | } 486 | 487 | bool max_capacity() 488 | { 489 | { 490 | // this math for queue size estimation is only valid for q_size <= 256 491 | for (size_t q_size = 2; q_size < 256; ++q_size) { 492 | ReaderWriterQueue q(q_size); 493 | ASSERT_OR_FAIL(q.max_capacity() == ceilToPow2(q_size+1)-1); 494 | 495 | const size_t start_cap = q.max_capacity(); 496 | for (size_t i = 0; i < start_cap+1; ++i) // fill 1 past capacity to resize 497 | q.enqueue(i); 498 | ASSERT_OR_FAIL(q.max_capacity() == 3*start_cap+1); 499 | } 500 | } 501 | return true; 502 | } 503 | 504 | bool blocking() 505 | { 506 | { 507 | BlockingReaderWriterQueue q; 508 | int item; 509 | 510 | q.enqueue(123); 511 | ASSERT_OR_FAIL(q.try_dequeue(item)); 512 | ASSERT_OR_FAIL(item == 123); 513 | ASSERT_OR_FAIL(q.size_approx() == 0); 514 | 515 | q.enqueue(234); 516 | ASSERT_OR_FAIL(q.size_approx() == 1); 517 | ASSERT_OR_FAIL(*q.peek() == 234); 518 | ASSERT_OR_FAIL(*q.peek() == 234); 519 | ASSERT_OR_FAIL(q.pop()); 520 | 521 | ASSERT_OR_FAIL(q.try_enqueue(345)); 522 | q.wait_dequeue(item); 523 | ASSERT_OR_FAIL(item == 345); 524 | ASSERT_OR_FAIL(!q.peek()); 525 | ASSERT_OR_FAIL(q.size_approx() == 0); 526 | ASSERT_OR_FAIL(!q.try_dequeue(item)); 527 | } 528 | 529 | weak_atomic result; 530 | result = 1; 531 | 532 | { 533 | BlockingReaderWriterQueue q(100); 534 | SimpleThread reader([&]() { 535 | int item = -1; 536 | int prevItem = -1; 537 | for (int i = 0; i != 1000000; ++i) { 538 | q.wait_dequeue(item); 539 | if (item <= prevItem) { 540 | result = 0; 541 | } 542 | prevItem = item; 543 | } 544 | }); 545 | SimpleThread writer([&]() { 546 | for (int i = 0; i != 1000000; ++i) { 547 | q.enqueue(i); 548 | } 549 | }); 550 | 551 | writer.join(); 552 | reader.join(); 553 | 554 | ASSERT_OR_FAIL(q.size_approx() == 0); 555 | ASSERT_OR_FAIL(result.load()); 556 | } 557 | 558 | { 559 | BlockingReaderWriterQueue q(100); 560 | SimpleThread reader([&]() { 561 | int item = -1; 562 | int prevItem = -1; 563 | for (int i = 0; i != 1000000; ++i) { 564 | if (!q.wait_dequeue_timed(item, 1000)) { 565 | --i; 566 | continue; 567 | } 568 | if (item <= prevItem) { 569 | result = 0; 570 | } 571 | prevItem = item; 572 | } 573 | }); 574 | SimpleThread writer([&]() { 575 | for (int i = 0; i != 1000000; ++i) { 576 | q.enqueue(i); 577 | for (volatile int x = 0; x != 100; ++x); 578 | } 579 | }); 580 | 581 | writer.join(); 582 | reader.join(); 583 | 584 | int item; 585 | ASSERT_OR_FAIL(q.size_approx() == 0); 586 | ASSERT_OR_FAIL(!q.wait_dequeue_timed(item, 0)); 587 | ASSERT_OR_FAIL(!q.wait_dequeue_timed(item, 1)); 588 | ASSERT_OR_FAIL(result.load()); 589 | } 590 | 591 | #if MOODYCAMEL_HAS_EMPLACE 592 | { 593 | BlockingReaderWriterQueue q(100); 594 | std::unique_ptr p { new int(123) }; 595 | q.emplace(std::move(p)); 596 | q.try_emplace(std::move(p)); 597 | UniquePtrWrapper item; 598 | ASSERT_OR_FAIL(q.wait_dequeue_timed(item, 0)); 599 | ASSERT_OR_FAIL(item.get_value() == 123); 600 | ASSERT_OR_FAIL(q.wait_dequeue_timed(item, 0)); 601 | ASSERT_OR_FAIL(item.get_ptr() == nullptr); 602 | ASSERT_OR_FAIL(q.size_approx() == 0); 603 | } 604 | #endif 605 | 606 | return true; 607 | } 608 | 609 | bool vector() 610 | { 611 | { 612 | std::vector> queues; 613 | queues.push_back(ReaderWriterQueue()); 614 | queues.emplace_back(); 615 | 616 | queues[0].enqueue(1); 617 | queues[1].enqueue(2); 618 | std::swap(queues[0], queues[1]); 619 | 620 | int item; 621 | ASSERT_OR_FAIL(queues[0].try_dequeue(item)); 622 | ASSERT_OR_FAIL(item == 2); 623 | 624 | ASSERT_OR_FAIL(queues[1].try_dequeue(item)); 625 | ASSERT_OR_FAIL(item == 1); 626 | } 627 | 628 | { 629 | std::vector> queues; 630 | queues.push_back(BlockingReaderWriterQueue()); 631 | queues.emplace_back(); 632 | 633 | queues[0].enqueue(1); 634 | queues[1].enqueue(2); 635 | std::swap(queues[0], queues[1]); 636 | 637 | int item; 638 | ASSERT_OR_FAIL(queues[0].try_dequeue(item)); 639 | ASSERT_OR_FAIL(item == 2); 640 | 641 | queues[1].wait_dequeue(item); 642 | ASSERT_OR_FAIL(item == 1); 643 | } 644 | return true; 645 | } 646 | 647 | #if MOODYCAMEL_HAS_EMPLACE 648 | bool emplace() 649 | { 650 | ReaderWriterQueue q(100); 651 | std::unique_ptr p { new int(123) }; 652 | q.emplace(std::move(p)); 653 | UniquePtrWrapper item; 654 | ASSERT_OR_FAIL(q.try_dequeue(item)); 655 | ASSERT_OR_FAIL(item.get_value() == 123); 656 | ASSERT_OR_FAIL(q.size_approx() == 0); 657 | 658 | return true; 659 | } 660 | 661 | // This is what you have to do to try_enqueue() a movable type, and demonstrates why try_emplace() is useful 662 | bool try_enqueue_fail_workaround() 663 | { 664 | ReaderWriterQueue q(0); 665 | { 666 | // A failed try_enqueue() will still delete p 667 | std::unique_ptr p { new int(123) }; 668 | q.try_enqueue(std::move(p)); 669 | ASSERT_OR_FAIL(q.size_approx() == 0); 670 | ASSERT_OR_FAIL(p == nullptr); 671 | } 672 | { 673 | // Workaround isn't pretty and potentially expensive - use try_emplace() instead 674 | std::unique_ptr p { new int(123) }; 675 | UniquePtrWrapper w(std::move(p)); 676 | q.try_enqueue(std::move(w)); 677 | p = std::move(w.get_ptr()); 678 | ASSERT_OR_FAIL(q.size_approx() == 0); 679 | ASSERT_OR_FAIL(p != nullptr); 680 | ASSERT_OR_FAIL(*p == 123); 681 | } 682 | 683 | return true; 684 | } 685 | 686 | bool try_emplace_fail() 687 | { 688 | ReaderWriterQueue q(0); 689 | std::unique_ptr p { new int(123) }; 690 | q.try_emplace(std::move(p)); 691 | ASSERT_OR_FAIL(q.size_approx() == 0); 692 | ASSERT_OR_FAIL(p != nullptr); 693 | ASSERT_OR_FAIL(*p == 123); 694 | 695 | return true; 696 | } 697 | #endif 698 | 699 | bool blocking_circular_buffer() 700 | { 701 | { 702 | // Basic enqueue 703 | BlockingReaderWriterCircularBuffer q(65); 704 | for (int iteration = 0; iteration != 128; ++iteration) { // check there's no problem with mismatch between nominal and allocated capacity 705 | ASSERT_OR_FAIL(q.max_capacity() == 65); 706 | ASSERT_OR_FAIL(q.size_approx() == 0); 707 | ASSERT_OR_FAIL(!q.try_pop()); 708 | ASSERT_OR_FAIL(q.try_enqueue(0)); 709 | ASSERT_OR_FAIL(q.max_capacity() == 65); 710 | ASSERT_OR_FAIL(q.size_approx() == 1); 711 | ASSERT_OR_FAIL(*q.peek() == 0); 712 | for (int i = 1; i != 65; ++i) 713 | q.wait_enqueue(i); 714 | ASSERT_OR_FAIL(q.size_approx() == 65); 715 | ASSERT_OR_FAIL(!q.try_enqueue(65)); 716 | 717 | // Basic dequeue 718 | int item; 719 | ASSERT_OR_FAIL(q.try_dequeue(item)); 720 | ASSERT_OR_FAIL(item == 0); 721 | for (int i = 1; i != 65; ++i) { 722 | q.wait_dequeue(item); 723 | ASSERT_OR_FAIL(item == i); 724 | } 725 | ASSERT_OR_FAIL(!q.try_dequeue(item)); 726 | ASSERT_OR_FAIL(!q.wait_dequeue_timed(item, 1)); 727 | ASSERT_OR_FAIL(item == 64); 728 | } 729 | } 730 | 731 | { 732 | // Zero capacity 733 | BlockingReaderWriterCircularBuffer q(0); 734 | ASSERT_OR_FAIL(q.max_capacity() == 0); 735 | ASSERT_OR_FAIL(!q.try_enqueue(1)); 736 | ASSERT_OR_FAIL(!q.wait_enqueue_timed(1, 0)); 737 | } 738 | 739 | // Element lifetimes 740 | Foo::reset(); 741 | { 742 | BlockingReaderWriterCircularBuffer q(31); 743 | { 744 | Foo item; 745 | for (int i = 0; i != 23 + 32; ++i) { 746 | ASSERT_OR_FAIL(q.try_enqueue(Foo())); 747 | ASSERT_OR_FAIL(q.try_dequeue(item)); 748 | } 749 | ASSERT_OR_FAIL(Foo::destroy_count() == 23 + 32); 750 | ASSERT_OR_FAIL(Foo::destroyed_in_order()); 751 | } 752 | Foo::reset(); 753 | 754 | { 755 | Foo item; 756 | for (int i = 0; i != 23 + 32; ++i) { 757 | ASSERT_OR_FAIL(q.try_enqueue(Foo())); 758 | item = std::move(*q.peek()); 759 | ASSERT_OR_FAIL(q.try_pop()); 760 | } 761 | ASSERT_OR_FAIL(!q.peek()); 762 | ASSERT_OR_FAIL(!q.try_pop()); 763 | ASSERT_OR_FAIL(Foo::destroy_count() == 23 + 32); 764 | ASSERT_OR_FAIL(Foo::destroyed_in_order()); 765 | } 766 | Foo::reset(); 767 | 768 | { 769 | Foo item; 770 | for (int i = 0; i != 10; ++i) 771 | ASSERT_OR_FAIL(q.try_enqueue(Foo())); 772 | ASSERT_OR_FAIL(q.size_approx() == 10); 773 | ASSERT_OR_FAIL(Foo::destroy_count() == 0); 774 | ASSERT_OR_FAIL(q.try_dequeue(item)); 775 | ASSERT_OR_FAIL(q.size_approx() == 9); 776 | ASSERT_OR_FAIL(Foo::destroy_count() == 1); 777 | } 778 | ASSERT_OR_FAIL(Foo::destroy_count() == 2); 779 | ASSERT_OR_FAIL(Foo::destroyed_in_order()); 780 | 781 | BlockingReaderWriterCircularBuffer q2(std::move(q)); 782 | ASSERT_OR_FAIL(q.size_approx() == 0); 783 | ASSERT_OR_FAIL(q2.size_approx() == 9); 784 | 785 | BlockingReaderWriterCircularBuffer q3(2); 786 | q3 = std::move(q2); 787 | ASSERT_OR_FAIL(q.size_approx() == 0); 788 | ASSERT_OR_FAIL(q2.size_approx() == 0); 789 | ASSERT_OR_FAIL(q3.size_approx() == 9); 790 | 791 | q = std::move(q2); 792 | ASSERT_OR_FAIL(q.size_approx() == 0); 793 | ASSERT_OR_FAIL(q2.size_approx() == 0); 794 | ASSERT_OR_FAIL(q3.size_approx() == 9); 795 | ASSERT_OR_FAIL(Foo::destroy_count() == 2); 796 | } 797 | ASSERT_OR_FAIL(Foo::destroy_count() == 11); 798 | ASSERT_OR_FAIL(Foo::destroyed_in_order()); 799 | 800 | weak_atomic result; 801 | result = 1; 802 | 803 | { 804 | // Threaded 805 | BlockingReaderWriterCircularBuffer q(8); 806 | SimpleThread reader([&]() { 807 | int item; 808 | for (int i = 0; i != 1000000; ++i) { 809 | int* peeked = q.peek(); 810 | if (peeked) { 811 | item = *peeked; 812 | if (peeked != q.peek() || !q.try_pop()) 813 | result = 0; 814 | } 815 | else { 816 | q.wait_dequeue(item); 817 | } 818 | if (item != i) 819 | result = 0; 820 | } 821 | }); 822 | SimpleThread writer([&]() { 823 | for (int i = 0; i != 1000000; ++i) 824 | q.wait_enqueue(i); 825 | }); 826 | 827 | writer.join(); 828 | reader.join(); 829 | 830 | ASSERT_OR_FAIL(q.size_approx() == 0); 831 | ASSERT_OR_FAIL(result.load()); 832 | } 833 | 834 | return true; 835 | } 836 | }; 837 | 838 | 839 | 840 | void printTests(ReaderWriterQueueTests const& tests) 841 | { 842 | std::printf(" Supported tests are:\n"); 843 | 844 | std::vector names; 845 | tests.getAllTestNames(names); 846 | for (auto it = names.cbegin(); it != names.cend(); ++it) { 847 | std::printf(" %s\n", it->c_str()); 848 | } 849 | } 850 | 851 | 852 | // Basic test harness 853 | int main(int argc, char** argv) 854 | { 855 | bool disablePrompt = false; 856 | std::vector selectedTests; 857 | 858 | // Disable buffering (so that when run in, e.g., Sublime Text, the output appears as it is written) 859 | std::setvbuf(stdout, nullptr, _IONBF, 0); 860 | 861 | // Isolate the executable name 862 | std::string progName = argv[0]; 863 | auto slash = progName.find_last_of("/\\"); 864 | if (slash != std::string::npos) { 865 | progName = progName.substr(slash + 1); 866 | } 867 | 868 | ReaderWriterQueueTests tests; 869 | 870 | // Parse command line options 871 | if (argc == 1) { 872 | std::printf("Running all unit tests for moodycamel::ReaderWriterQueue.\n(Run %s --help for other options.)\n\n", progName.c_str()); 873 | } 874 | else { 875 | bool printHelp = false; 876 | bool printedTests = false; 877 | bool error = false; 878 | for (int i = 1; i < argc; ++i) { 879 | if (std::strcmp(argv[i], "--help") == 0) { 880 | printHelp = true; 881 | } 882 | else if (std::strcmp(argv[i], "--disable-prompt") == 0) { 883 | disablePrompt = true; 884 | } 885 | else if (std::strcmp(argv[i], "--run") == 0) { 886 | if (i + 1 == argc || argv[i + 1][0] == '-') { 887 | std::printf("Expected test name argument for --run option.\n"); 888 | if (!printedTests) { 889 | printTests(tests); 890 | printedTests = true; 891 | } 892 | error = true; 893 | continue; 894 | } 895 | 896 | if (!tests.validateTestName(argv[++i])) { 897 | std::printf("Unrecognized test '%s'.\n", argv[i]); 898 | if (!printedTests) { 899 | printTests(tests); 900 | printedTests = true; 901 | } 902 | error = true; 903 | continue; 904 | } 905 | 906 | selectedTests.push_back(argv[i]); 907 | } 908 | else { 909 | std::printf("Unrecognized option '%s'.\n", argv[i]); 910 | error = true; 911 | } 912 | } 913 | 914 | if (error || printHelp) { 915 | if (error) { 916 | std::printf("\n"); 917 | } 918 | std::printf("%s\n Description: Runs unit tests for moodycamel::ReaderWriterQueue\n", progName.c_str()); 919 | std::printf(" --help Prints this help blurb\n"); 920 | std::printf(" --run test Runs only the specified test(s)\n"); 921 | std::printf(" --disable-prompt Disables prompt before exit when the tests finish\n"); 922 | return error ? -1 : 0; 923 | } 924 | } 925 | 926 | 927 | int exitCode = 0; 928 | 929 | bool result; 930 | if (selectedTests.size() > 0) { 931 | result = tests.run(selectedTests); 932 | } 933 | else { 934 | result = tests.run(); 935 | } 936 | 937 | if (result) { 938 | std::printf("All %stests passed.\n", (selectedTests.size() > 0 ? "selected " : "")); 939 | } 940 | else { 941 | std::printf("Test(s) failed!\n"); 942 | exitCode = 2; 943 | } 944 | 945 | if (!disablePrompt) { 946 | std::printf("Press ENTER to exit.\n"); 947 | getchar(); 948 | } 949 | return exitCode; 950 | } 951 | 952 | --------------------------------------------------------------------------------