### Install ConcurrentQueue with vcpkg Source: https://github.com/cameron314/concurrentqueue/blob/master/README.md Use vcpkg to download and install the moodycamel::ConcurrentQueue. Ensure vcpkg is set up correctly in your environment. ```Shell git clone https://github.com/Microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg integrate install vcpkg install concurrentqueue ``` -------------------------------- ### Complete Relacy Test Suite Example Source: https://github.com/cameron314/concurrentqueue/blob/master/tests/relacy/relacy/test/tutorial.txt A full example demonstrating a Relacy test suite with two threads, including `before`, `thread`, `after`, and `invariant` functions, and `rl::simulate` execution. ```cpp #include // template parameter '2' is number of threads struct race_test : rl::test_suite { std::atomic a; rl::var x; // executed in single thread before main thread function void before() { a($) = 0; x($) = 0; } // main thread function void thread(unsigned thread_index) { if (0 == thread_index) { x($) = 1; a($).store(1, rl::memory_order_relaxed); } else { if (1 == a($).load(rl::memory_order_relaxed)) x($) = 2; } } // executed in single thread after main thread function void after() { } // executed in single thread after every 'visible' action in main threads // disallowed to modify any state void invariant() { } }; int main() { rl::simulate(); } ``` -------------------------------- ### Start Relacy Simulation Source: https://github.com/cameron314/concurrentqueue/blob/master/tests/relacy/relacy/test/tutorial.txt Initiate the Relacy simulation for a given test suite using `rl::simulate()`. ```cpp rl::simulate(); ``` -------------------------------- ### Multi-threaded Enqueue and Dequeue Source: https://github.com/cameron314/concurrentqueue/blob/master/samples.md Illustrates concurrent usage of the queue with multiple producer and consumer threads. This example verifies that all enqueued items are eventually dequeued. ```C++ ConcurrentQueue q; int dequeued[100] = { 0 }; std::thread threads[20]; // Producers for (int i = 0; i != 10; ++i) { threads[i] = std::thread([&](int i) { for (int j = 0; j != 10; ++j) { q.enqueue(i * 10 + j); } }, i); } // Consumers for (int i = 10; i != 20; ++i) { threads[i] = std::thread([&]() { int item; for (int j = 0; j != 20; ++j) { if (q.try_dequeue(item)) { ++dequeued[item]; } } }); } // Wait for all threads for (int i = 0; i != 20; ++i) { threads[i].join(); } // Collect any leftovers (could be some if e.g. consumers finish before producers) int item; while (q.try_dequeue(item)) { ++dequeued[item]; } // Make sure everything went in and came back out! for (int i = 0; i != 100; ++i) { assert(dequeued[i] == 1); } ``` -------------------------------- ### Bulk Enqueue and Dequeue Operations Source: https://github.com/cameron314/concurrentqueue/blob/master/samples.md Shows how to use enqueue_bulk and try_dequeue_bulk for potentially faster data transfer in multi-threaded scenarios. This example achieves the same result as the previous one but uses bulk operations. ```C++ ConcurrentQueue q; int dequeued[100] = { 0 }; std::thread threads[20]; // Producers for (int i = 0; i != 10; ++i) { threads[i] = std::thread([&](int i) { int items[10]; for (int j = 0; j != 10; ++j) { items[j] = i * 10 + j; } q.enqueue_bulk(items, 10); }, i); } // Consumers for (int i = 10; i != 20; ++i) { threads[i] = std::thread([&]() { int items[20]; for (std::size_t count = q.try_dequeue_bulk(items, 20); count != 0; --count) { ++dequeued[items[count - 1]]; } }); } // Wait for all threads for (int i = 0; i != 20; ++i) { threads[i].join(); } // Collect any leftovers (could be some if e.g. consumers finish before producers) int items[10]; std::size_t count; while ((count = q.try_dequeue_bulk(items, 10)) != 0) { for (std::size_t i = 0; i != count; ++i) { ++dequeued[items[i]]; } } // Make sure everything went in and came back out! for (int i = 0; i != 100; ++i) { assert(dequeued[i] == 1); } ``` -------------------------------- ### Data Race Simulation Example Source: https://github.com/cameron314/concurrentqueue/blob/master/tests/relacy/relacy/test/todo.txt Demonstrates a specific data race scenario involving two threads and atomic variables 'x' and 'y'. This example highlights how modification orders can be affected by concurrent writes, potentially leading to unexpected behavior. ```cpp //thread 1 x.store(1, std::memory_order_relaxed); y.store(1, std::memory_order_relaxed); //thread 2 while (y.load(std::memory_order_relaxed) == 0 {} x.store(2, std::memory_order_relaxed); -> modification order of 'x' will never be "2, 1" ``` -------------------------------- ### Blocking Concurrent Queue Example Source: https://github.com/cameron314/concurrentqueue/blob/master/README.md Demonstrates the usage of the blocking concurrent queue with producer and consumer threads. Use `wait_dequeue` for blocking retrieval and `wait_dequeue_timed` for timed retrieval. Ensure proper thread synchronization to avoid issues when destroying the queue while threads are waiting. ```C++ #include "blockingconcurrentqueue.h" moodycamel::BlockingConcurrentQueue q; std::thread producer([&]() { for (int i = 0; i != 100; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(i % 10)); q.enqueue(i); } }); std::thread consumer([&]() { for (int i = 0; i != 100; ++i) { int item; q.wait_dequeue(item); assert(item == i); if (q.wait_dequeue_timed(item, std::chrono::milliseconds(5))) { ++i; assert(item == i); } } }); producer.join(); consumer.join(); assert(q.size_approx() == 0); ``` -------------------------------- ### Non-deterministic Sub-expression Calculation Example Source: https://github.com/cameron314/concurrentqueue/blob/master/tests/relacy/relacy/test/todo.txt Illustrates a scenario where sub-expressions might be evaluated non-deterministically due to concurrent memory accesses. Use this to understand potential race conditions in atomic operations. ```cpp foo(bar.load(std::memory_order_acquire), baz.load(std::memory_order_acquire)); ``` -------------------------------- ### Producer/Consumer Model with Synchronization Source: https://github.com/cameron314/concurrentqueue/blob/master/samples.md Implements a simultaneous producer-consumer model where producers add items and consumers remove them until all items are processed. This example uses atomic variables for synchronization and careful memory ordering to ensure correctness. ```C++ ConcurrentQueue q; const int ProducerCount = 8; const int ConsumerCount = 8; std::thread producers[ProducerCount]; std::thread consumers[ConsumerCount]; std::atomic doneProducers(0); std::atomic doneConsumers(0); for (int i = 0; i != ProducerCount; ++i) { producers[i] = std::thread([&]() { while (produce) { q.enqueue(produceItem()); } doneProducers.fetch_add(1, std::memory_order_release); }); } for (int i = 0; i != ConsumerCount; ++i) { consumers[i] = std::thread([&]() { Item item; bool itemsLeft; do { // It's important to fence (if the producers have finished) *before* dequeueing itemsLeft = doneProducers.load(std::memory_order_acquire) != ProducerCount; while (q.try_dequeue(item)) { itemsLeft = true; consumeItem(item); } } while (itemsLeft || doneConsumers.fetch_add(1, std::memory_order_acq_rel) + 1 == ConsumerCount); // The condition above is a bit tricky, but it's necessary to ensure that the // last consumer sees the memory effects of all the other consumers before it // calls try_dequeue for the last time }); } for (int i = 0; i != ProducerCount; ++i) { producers[i].join(); } for (int i = 0; i != ConsumerCount; ++i) { consumers[i].join(); } ``` -------------------------------- ### Customizing ConcurrentQueue with Traits Source: https://github.com/cameron314/concurrentqueue/blob/master/README.md Inherit from ConcurrentQueueDefaultTraits to override specific values like BLOCK_SIZE for custom queue behavior. This example shows how to use bigger blocks. ```C++ struct MyTraits : public moodycamel::ConcurrentQueueDefaultTraits { static const size_t BLOCK_SIZE = 256; }; moodycamel::ConcurrentQueue q; ``` -------------------------------- ### Pump ConcurrentQueue Until Empty (Multi-Threaded) Source: https://github.com/cameron314/concurrentqueue/blob/master/samples.md Process remaining items in the queue using multiple threads. This example uses an atomic counter to signal completion and requires careful consideration of memory ordering for visibility. ```C++ std::thread threads[8]; std::atomic doneConsumers(0); for (int i = 0; i != 8; ++i) { threads[i] = std::thread([&]() { Item item; do { while (q.try_dequeue(item)) { // Process item... } } while (doneConsumers.fetch_add(1, std::memory_order_acq_rel) + 1 == 8); }); } for (int i = 0; i != 8; ++i) { threads[i].join(); } ``` -------------------------------- ### Run Benchmarks Source: https://github.com/cameron314/concurrentqueue/blob/master/README.md Instructions to build and run the benchmarks for the concurrentqueue project. Requires MinGW and GnuWin32 utilities on Windows, or g++ on Linux. ```Shell cd build make benchmarks bin/benchmarks ``` -------------------------------- ### Enqueue and Dequeue with Tokens Source: https://github.com/cameron314/concurrentqueue/blob/master/README.md Demonstrates creating and using producer and consumer tokens for efficient enqueue and dequeue operations. Tokens are not thread-safe and ideally one of each kind should be used per thread. ```C++ moodycamel::ConcurrentQueue q; moodycamel::ProducerToken ptok(q); q.enqueue(ptok, 17); moodycamel::ConsumerToken ctok(q); int item; q.try_dequeue(ctok, item); assert(item == 17); ``` -------------------------------- ### Basic Enqueue and Dequeue Source: https://github.com/cameron314/concurrentqueue/blob/master/samples.md Demonstrates the fundamental enqueue and try_dequeue operations for a single-threaded scenario. Ensure items are enqueued before attempting to dequeue them. ```C++ ConcurrentQueue q; for (int i = 0; i != 123; ++i) q.enqueue(i); int item; for (int i = 0; i != 123; ++i) { q.try_dequeue(item); assert(item == i); } ``` -------------------------------- ### Basic ConcurrentQueue Usage Source: https://github.com/cameron314/concurrentqueue/blob/master/README.md Demonstrates the basic enqueue and try_dequeue operations of the ConcurrentQueue. Ensure the queue object is fully constructed before use by multiple threads. ```C++ #include "concurrentqueue.h" moodycamel::ConcurrentQueue q; q.enqueue(25); int item; bool found = q.try_dequeue(item); assert(found && item == 25); ``` -------------------------------- ### Bulk Enqueue and Dequeue Operations Source: https://github.com/cameron314/concurrentqueue/blob/master/README.md Shows how to efficiently enqueue and dequeue multiple items using bulk operations. This can significantly reduce overhead compared to single-item operations. ```C++ moodycamel::ConcurrentQueue q; int items[] = { 1, 2, 3, 4, 5 }; q.enqueue_bulk(items, 5); int results[5]; // Could also be any iterator size_t count = q.try_dequeue_bulk(results, 5); for (size_t i = 0; i != count; ++i) { assert(results[i] == items[i]); } ``` -------------------------------- ### Preallocation Formula for Implicit Producers Source: https://github.com/cameron314/concurrentqueue/blob/master/README.md Use this formula to calculate the preallocation size for queues with implicit producers. Ensure `BLOCK_SIZE` and `MAX_NUM_PRODUCERS` are defined. ```C++ (ceil(N / BLOCK_SIZE) - 1 + 2 * MAX_NUM_PRODUCERS) * BLOCK_SIZE ``` -------------------------------- ### Configure Test Parameters for Simulation Source: https://github.com/cameron314/concurrentqueue/blob/master/tests/relacy/relacy/test/advanced.txt Set simulation parameters such as scheduler type, context bound, and execution depth limit. Used to control the simulation's behavior and termination conditions. ```cpp rl::test_params p; p.search_type = rl::fair_context_bound_scheduler_type; p.context_bound = 1; p.execution_depth_limit = 1000; rl::simulate(p); ``` -------------------------------- ### Blocking Producer/Consumer Model Source: https://github.com/cameron314/concurrentqueue/blob/master/samples.md Use this for scenarios where producers and consumers operate simultaneously and blocking is acceptable. Ensure a mechanism exists to signal consumers when to stop to avoid indefinite blocking. ```C++ BlockingConcurrentQueue q; const int ProducerCount = 8; const int ConsumerCount = 8; std::thread producers[ProducerCount]; std::thread consumers[ConsumerCount]; std::atomic promisedElementsRemaining(ProducerCount * 1000); for (int i = 0; i != ProducerCount; ++i) { producers[i] = std::thread([&]() { for (int j = 0; j != 1000; ++j) { q.enqueue(produceItem()); } }); } for (int i = 0; i != ConsumerCount; ++i) { consumers[i] = std::thread([&]() { Item item; while (promisedElementsRemaining.fetch_sub(1, std::memory_order_relaxed) > 0) { q.wait_dequeue(item); consumeItem(item); } }); } for (int i = 0; i != ProducerCount; ++i) { producers[i].join(); } for (int i = 0; i != ConsumerCount; ++i) { consumers[i].join(); } ``` -------------------------------- ### Preallocation Formula for Mixed Producers Source: https://github.com/cameron314/concurrentqueue/blob/master/README.md Use this formula to calculate the preallocation size for queues with mixed producer types. Ensure `BLOCK_SIZE`, `MAX_EXPLICIT_PRODUCERS`, and `MAX_IMPLICIT_PRODUCERS` are defined. ```C++ ((ceil(N / BLOCK_SIZE) - 1) * (MAX_EXPLICIT_PRODUCERS + 1) + 2 * (MAX_IMPLICIT_PRODUCERS + MAX_EXPLICIT_PRODUCERS)) * BLOCK_SIZE ``` -------------------------------- ### Preallocation Formula for Explicit Producers Source: https://github.com/cameron314/concurrentqueue/blob/master/README.md Use this formula to calculate the preallocation size for queues with explicit producers. Ensure `BLOCK_SIZE` and `MAX_NUM_PRODUCERS` are defined. ```C++ (ceil(N / BLOCK_SIZE) + 1) * MAX_NUM_PRODUCERS * BLOCK_SIZE ``` -------------------------------- ### Race Condition Test Suite with Yield Source: https://github.com/cameron314/concurrentqueue/blob/master/tests/relacy/relacy/test/advanced.txt A test suite demonstrating a race condition scenario. It includes a 'before' function for initialization and a 'thread' function where one thread waits for an atomic variable to change, using 'yield' to ensure fairness in spin-loops. ```cpp struct race_seq_ld_ld_test : rl::test_suite { std::atomic a; rl::var x; void before() { a($) = 0; x($) = 0; } void thread(unsigned index) { if (index) { x($).load(); a($).store(1, std::memory_order_relaxed); } else { rl::backoff b; while (0 == a($).load(rl::memory_order_relaxed)) b.yield($); x($).load(); } } }; ``` -------------------------------- ### Separate Stages Producer/Consumer Model Source: https://github.com/cameron314/concurrentqueue/blob/master/samples.md Suitable for scenarios where all production completes before consumption begins. This model avoids blocking by ensuring all items are enqueued before any are dequeued. ```C++ ConcurrentQueue q; // Production stage std::thread threads[8]; for (int i = 0; i != 8; ++i) { threads[i] = std::thread([&]() { while (produce) { q.enqueue(produceItem()); } }); } for (int i = 0; i != 8; ++i) { threads[i].join(); } // Consumption stage std::atomic doneConsumers(0); for (int i = 0; i != 8; ++i) { threads[i] = std::thread([&]() { Item item; do { while (q.try_dequeue(item)) { consumeItem(item); } // Loop again one last time if we're the last producer (with the acquired // memory effects of the other producers): } while (doneConsumers.fetch_add(1, std::memory_order_acq_rel) + 1 == 8); }); } for (int i = 0; i != 8; ++i) { threads[i].join(); } ``` -------------------------------- ### Object Pool with Implicit Methods Source: https://github.com/cameron314/concurrentqueue/blob/master/samples.md Use this when the threads accessing the queue are not known in advance. It leverages implicit methods that do not require explicit token management. ```C++ // A pool of 'Something' objects that can be safely accessed // from any thread class SomethingPool { public: Something getSomething() { Something obj; queue.try_dequeue(obj); // If the dequeue succeeded, obj will be an object from the // thread pool, otherwise it will be the default-constructed // object as declared above return obj; } void recycleSomething(Something&& obj) { queue.enqueue(std::move(obj)); } }; ``` -------------------------------- ### Workaround for Dequeuing Types Without Default Constructor (Deferred Construction) Source: https://github.com/cameron314/concurrentqueue/blob/master/README.md A safer alternative for types where moves are cheap but default construction is not. This wrapper defers construction until assignment, enabling the use of the move constructor. It ensures proper destruction of the moved-from object. ```C++ struct MyObjectMover { inline void operator=(MyObject&& x) { new (data) MyObject(std::move(x)); created = true; } inline MyObject& obj() { assert(created); return *reinterpret_cast(data); } ~MyObjectMover() { if (created) obj().~MyObject(); } private: align(alignof(MyObject)) char data[sizeof(MyObject)]; bool created = false; }; ``` -------------------------------- ### Workaround for Dequeuing Types Without Default Constructor (memcpy) Source: https://github.com/cameron314/concurrentqueue/blob/master/README.md This workaround is for types that are expensive to construct or lack a default constructor. It uses memcpy to copy memory contents, suitable for objects without internal pointers. Ensure proper cleanup of the moved-from object to avoid data corruption. ```C++ struct MyObjectMover { inline void operator=(MyObject&& obj) { std::memcpy(data, &obj, sizeof(MyObject)); // TODO: Cleanup obj so that when it's destructed by the queue // it doesn't corrupt the data of the object we just moved it into } inline MyObject& obj() { return *reinterpret_cast(data); } private: align(alignof(MyObject)) char data[sizeof(MyObject)]; }; ``` -------------------------------- ### Access Atomic and Relacy Variables Source: https://github.com/cameron314/concurrentqueue/blob/master/tests/relacy/relacy/test/tutorial.txt Postfix all accesses to `std::atomic` and `rl::var` variables with '($)' for proper race detection and tracing. ```cpp std::atomic head; rl::var data; head($).store(0); data($) = head($).load(); ``` -------------------------------- ### Use Thread-Private Variables Source: https://github.com/cameron314/concurrentqueue/blob/master/tests/relacy/relacy/test/tutorial.txt Strictly thread-private variables can be declared as usual. They are not checked for races nor included in traces, which can accelerate verification. ```cpp for (int i = 0; i != 10; ++i) ; ``` -------------------------------- ### Thread Pool Task Queue Source: https://github.com/cameron314/concurrentqueue/blob/master/samples.md Enables adding tasks to a queue from any thread and processing them on dedicated thread pool threads using blocking dequeues. ```C++ BlockingConcurrentQueue q; // To create a task from any thread: q.enqueue(...); // On threadpool threads: Task task; while (true) { q.wait_dequeue(task); // Process task... } ``` -------------------------------- ### Multithreaded Game Loop Synchronization Source: https://github.com/cameron314/concurrentqueue/blob/master/samples.md Synchronizes tasks within a game loop, allowing tasks to be processed concurrently and providing mechanisms to wait for their completion before rendering. ```C++ BlockingConcurrentQueue q; std::atomic pendingTasks(0); // On threadpool threads: Task task; while (true) { q.wait_dequeue(task); // Process task... pendingTasks.fetch_add(-1, std::memory_order_release); } // Whenever a new task needs to be processed for the frame: pendingTasks.fetch_add(1, std::memory_order_release); q.enqueue(...); // To wait for all the frame's tasks to complete before rendering: while (pendingTasks.load(std::memory_order_acquire) != 0) continue; // Alternatively you could help out the thread pool while waiting: while (pendingTasks.load(std::memory_order_acquire) != 0) { if (!q.try_dequeue(task)) { continue; } // Process task... pendingTasks.fetch_add(-1, std::memory_order_release); } ``` -------------------------------- ### Pump ConcurrentQueue Until Empty (Single-Threaded) Source: https://github.com/cameron314/concurrentqueue/blob/master/samples.md Process all items remaining in the queue in a single-threaded environment. Ensure memory effects are visible if other threads are enqueuing. ```C++ ConcurrentQueue q; // Single-threaded pumping: Item item; while (q.try_dequeue(item)) { // Process item... } ``` -------------------------------- ### Assert with Relacy Source: https://github.com/cameron314/concurrentqueue/blob/master/tests/relacy/relacy/test/tutorial.txt Use `RL_ASSERT` for assertions within Relacy tests. Access shared variables using the '($)' suffix. ```cpp int x = g($).load(); RL_ASSERT(x > 0); ``` -------------------------------- ### Declare Non-Atomic Variable with Relacy Source: https://github.com/cameron314/concurrentqueue/blob/master/tests/relacy/relacy/test/tutorial.txt Use `rl::var` for usual non-atomic variables. These are checked for races and included in traces. All accesses must be suffixed with '($)'. ```cpp rl::var data; ``` -------------------------------- ### Declare Atomic Variable with Relacy Source: https://github.com/cameron314/concurrentqueue/blob/master/tests/relacy/relacy/test/tutorial.txt Use `std::atomic` for atomic variables. All accesses must be suffixed with '($)'. ```cpp #include std::atomic head; ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.