| // Copyright 2021 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #include "pw_work_queue/work_queue.h" |
| |
| #include "gtest/gtest.h" |
| #include "pw_function/function.h" |
| #include "pw_log/log.h" |
| #include "pw_sync/thread_notification.h" |
| #include "pw_thread/thread.h" |
| #include "pw_work_queue/test_thread.h" |
| |
| namespace pw::work_queue { |
| namespace { |
| |
| TEST(WorkQueue, PingPongOneRequestType) { |
| struct { |
| int counter = 0; |
| sync::ThreadNotification worker_ping; |
| } context; |
| |
| WorkQueueWithBuffer<10> work_queue; |
| |
| // Start the worker thread. |
| thread::Thread work_thread(test::WorkQueueThreadOptions(), work_queue); |
| |
| // Pick a number bigger than the circular buffer to ensure we loop around. |
| const int kPingPongs = 300; |
| |
| for (int i = 0; i < kPingPongs; ++i) { |
| // Ping: throw work at the queue that will increment our counter. |
| EXPECT_EQ(OkStatus(), work_queue.PushWork([&context] { |
| context.counter++; |
| PW_LOG_INFO("Send pong..."); |
| context.worker_ping.release(); |
| })); |
| |
| // Throw a distraction in the queue. |
| EXPECT_EQ(OkStatus(), work_queue.PushWork([] { |
| PW_LOG_INFO("I'm a random task in the work queue; nothing to see here!"); |
| })); |
| |
| // Pong: wait for the callback to notify us from the worker thread. |
| context.worker_ping.acquire(); |
| } |
| |
| // Wait for the worker thread to terminate. |
| work_queue.RequestStop(); |
| work_thread.join(); |
| |
| EXPECT_EQ(context.counter, kPingPongs); |
| } |
| |
| TEST(WorkQueue, PingPongTwoRequestTypesWithExtraRequests) { |
| struct { |
| int counter = 0; |
| sync::ThreadNotification worker_ping; |
| } context_a, context_b; |
| |
| WorkQueueWithBuffer<10> work_queue; |
| |
| // Start the worker thread. |
| thread::Thread work_thread(test::WorkQueueThreadOptions(), work_queue); |
| |
| // Pick a number bigger than the circular buffer to ensure we loop around. |
| const int kPingPongs = 300; |
| |
| // Run a bunch of work items in the queue. |
| for (int i = 0; i < kPingPongs; ++i) { |
| // Other requests... |
| EXPECT_EQ(OkStatus(), |
| work_queue.PushWork([] { PW_LOG_INFO("Chopping onions"); })); |
| |
| // Ping A: throw work at the queue that will increment our counter. |
| EXPECT_EQ(OkStatus(), work_queue.PushWork([&context_a] { |
| context_a.counter++; |
| context_a.worker_ping.release(); |
| })); |
| |
| // Other requests... |
| EXPECT_EQ(OkStatus(), |
| work_queue.PushWork([] { PW_LOG_INFO("Dicing carrots"); })); |
| EXPECT_EQ(OkStatus(), |
| work_queue.PushWork([] { PW_LOG_INFO("Blanching spinach"); })); |
| |
| // Ping B: throw work at the queue that will increment our counter. |
| EXPECT_EQ(OkStatus(), work_queue.PushWork([&context_b] { |
| context_b.counter++; |
| context_b.worker_ping.release(); |
| })); |
| |
| // Other requests... |
| EXPECT_EQ(OkStatus(), |
| work_queue.PushWork([] { PW_LOG_INFO("Peeling potatoes"); })); |
| |
| // Pong A & B: wait for the callbacks to notify us from the worker thread. |
| context_a.worker_ping.acquire(); |
| context_b.worker_ping.acquire(); |
| } |
| |
| // Wait for the worker thread to terminate. |
| work_queue.RequestStop(); |
| work_thread.join(); |
| |
| EXPECT_EQ(context_a.counter, kPingPongs); |
| EXPECT_EQ(context_b.counter, kPingPongs); |
| } |
| |
| // TODO(ewout): Add unit tests for the metrics once they have been restructured. |
| |
| } // namespace |
| } // namespace pw::work_queue |