void QueueTest::putMessages() { EventBase eventBase; QueueConsumer consumer; QueueConsumer consumer2; consumer.fn = [&](int msg) { // Stop consuming after we receive a message with value 0, and start // consumer2 if (msg == 0) { consumer.stopConsuming(); consumer2.startConsuming(&eventBase, &queue); } }; consumer2.fn = [&](int msg) { // Stop consuming after we receive a message with value 0 if (msg == 0) { consumer2.stopConsuming(); } }; consumer.startConsuming(&eventBase, &queue); list<int> msgList = { 1, 2, 3, 4 }; vector<int> msgVector = { 5, 0, 9, 8, 7, 6, 7, 7, 8, 8, 2, 9, 6, 6, 10, 2, 0 }; // Call putMessages() several times to add messages to the queue queue.putMessages(msgList.begin(), msgList.end()); queue.putMessages(msgVector.begin() + 2, msgVector.begin() + 4); // Test sending 17 messages, the pipe-based queue calls write in 16 byte // chunks queue.putMessages(msgVector.begin(), msgVector.end()); // Loop until the consumer has stopped eventBase.loop(); vector<int> expectedMessages = { 1, 2, 3, 4, 9, 8, 7, 5, 0 }; vector<int> expectedMessages2 = { 9, 8, 7, 6, 7, 7, 8, 8, 2, 9, 6, 10, 2, 0 }; EXPECT_EQ(expectedMessages.size(), consumer.messages.size()); for (unsigned int idx = 0; idx < expectedMessages.size(); ++idx) { EXPECT_EQ(expectedMessages[idx], consumer.messages.at(idx)); } EXPECT_EQ(expectedMessages2.size(), consumer2.messages.size()); for (unsigned int idx = 0; idx < expectedMessages2.size(); ++idx) { EXPECT_EQ(expectedMessages2[idx], consumer2.messages.at(idx)); } }
TEST(NotificationQueueTest, ConsumeUntilDrainedStress) { for (size_t i = 0; i < 1 << 8; ++i) { // Basic tests: make sure we // - drain all the messages // - ignore any maxReadAtOnce // - can't add messages during draining EventBase eventBase; IntQueue queue; QueueConsumer consumer; consumer.fn = [&](int j) { EXPECT_THROW(queue.tryPutMessage(j), std::runtime_error); EXPECT_FALSE(queue.tryPutMessageNoThrow(j)); EXPECT_THROW(queue.putMessage(j), std::runtime_error); std::vector<int> ints{1, 2, 3}; EXPECT_THROW( queue.putMessages(ints.begin(), ints.end()), std::runtime_error); }; consumer.setMaxReadAtOnce(10); // We should ignore this consumer.startConsuming(&eventBase, &queue); for (int j = 0; j < 20; j++) { queue.putMessage(j); } EXPECT_TRUE(consumer.consumeUntilDrained()); EXPECT_EQ(20, consumer.messages.size()); // Make sure there can only be one drainer at once folly::Baton<> callbackBaton, threadStartBaton; consumer.fn = [&](int /* i */) { callbackBaton.wait(); }; QueueConsumer competingConsumer; competingConsumer.startConsuming(&eventBase, &queue); queue.putMessage(1); atomic<bool> raceA {false}; atomic<bool> raceB {false}; size_t numConsA = 0; size_t numConsB = 0; auto thread = std::thread([&]{ threadStartBaton.post(); raceB = consumer.consumeUntilDrained(&numConsB) && numConsB; }); threadStartBaton.wait(); raceA = competingConsumer.consumeUntilDrained(&numConsA) && numConsA; callbackBaton.post(); thread.join(); EXPECT_FALSE(raceA && raceB); EXPECT_TRUE(raceA || raceB); EXPECT_TRUE(raceA ^ raceB); } }