Loading test/threadpool_bounded_queue_test.cpp +26 −5 Original line number Diff line number Diff line Loading @@ -86,20 +86,41 @@ bool testBoundedQueueRejectsPastLimit() { } bool testCapacityFreesUpAfterDraining() { std::mutex mtx; std::condition_variable cv; bool release = false; netplus::ThreadPool pool(2, /*max_queue_size=*/2); std::atomic<int> completed{0}; // Saturate: 2 workers pick up 2 tasks immediately, 2 more fill the queue. for (int i = 0; i < 4; ++i) { if (!pool.submit([&completed] { std::this_thread::sleep_for(std::chrono::milliseconds(20)); // Pin both workers on a gate first (same trick as the rejection test above) so the 2 // submits that follow are guaranteed to land *in the queue*, not race the workers for // immediate execution -- without this, a submit loop can easily outrun two idle workers' // wake-up scheduling and see rejections that have nothing to do with the queue bound itself. for (int i = 0; i < 2; ++i) { if (!pool.submit([&] { std::unique_lock<std::mutex> lk(mtx); cv.wait(lk, [&] { return release; }); ++completed; })) { std::cerr << "FAIL: initial saturating submit " << i << " was rejected unexpectedly\n"; std::cerr << "FAIL: gate submit " << i << " was rejected unexpectedly\n"; return false; } } std::this_thread::sleep_for(std::chrono::milliseconds(50)); for (int i = 0; i < 2; ++i) { if (!pool.submit([&completed] { ++completed; })) { std::cerr << "FAIL: queued submit " << i << " (within capacity) was rejected\n"; return false; } } { std::lock_guard<std::mutex> lk(mtx); release = true; } cv.notify_all(); for (int i = 0; i < 200 && completed.load() < 4; ++i) std::this_thread::sleep_for(std::chrono::milliseconds(10)); if (completed.load() != 4) { Loading Loading
test/threadpool_bounded_queue_test.cpp +26 −5 Original line number Diff line number Diff line Loading @@ -86,20 +86,41 @@ bool testBoundedQueueRejectsPastLimit() { } bool testCapacityFreesUpAfterDraining() { std::mutex mtx; std::condition_variable cv; bool release = false; netplus::ThreadPool pool(2, /*max_queue_size=*/2); std::atomic<int> completed{0}; // Saturate: 2 workers pick up 2 tasks immediately, 2 more fill the queue. for (int i = 0; i < 4; ++i) { if (!pool.submit([&completed] { std::this_thread::sleep_for(std::chrono::milliseconds(20)); // Pin both workers on a gate first (same trick as the rejection test above) so the 2 // submits that follow are guaranteed to land *in the queue*, not race the workers for // immediate execution -- without this, a submit loop can easily outrun two idle workers' // wake-up scheduling and see rejections that have nothing to do with the queue bound itself. for (int i = 0; i < 2; ++i) { if (!pool.submit([&] { std::unique_lock<std::mutex> lk(mtx); cv.wait(lk, [&] { return release; }); ++completed; })) { std::cerr << "FAIL: initial saturating submit " << i << " was rejected unexpectedly\n"; std::cerr << "FAIL: gate submit " << i << " was rejected unexpectedly\n"; return false; } } std::this_thread::sleep_for(std::chrono::milliseconds(50)); for (int i = 0; i < 2; ++i) { if (!pool.submit([&completed] { ++completed; })) { std::cerr << "FAIL: queued submit " << i << " (within capacity) was rejected\n"; return false; } } { std::lock_guard<std::mutex> lk(mtx); release = true; } cv.notify_all(); for (int i = 0; i < 200 && completed.load() < 4; ++i) std::this_thread::sleep_for(std::chrono::milliseconds(10)); if (completed.load() != 4) { Loading