1 // Copyright 2022 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_sync/thread_notification.h"
16
17 #include <chrono>
18 #include <optional>
19
20 #include "FreeRTOS.h"
21 #include "pw_chrono/system_clock.h"
22 #include "pw_thread/non_portable_test_thread_options.h"
23 #include "pw_thread/sleep.h"
24 #include "pw_thread/thread.h"
25 #include "pw_unit_test/framework.h"
26 #include "task.h"
27
28 namespace pw::sync::freertos {
29 namespace {
30
31 using pw::chrono::SystemClock;
32
33 } // namespace
34
35 // These tests are targeted specifically to verify interactions between suspend
36 // and being blocked on direct task notifications and how they impact usage of
37 // the FreeRTOS optimized ThreadNotification backend.
38 #if INCLUDE_vTaskSuspend == 1
39
40 class NotificationAcquirer {
41 public:
WaitUntilRunning()42 void WaitUntilRunning() { started_notification_.acquire(); }
Release()43 void Release() { unblock_notification_.release(); }
WaitUntilFinished()44 void WaitUntilFinished() { finished_notification_.acquire(); }
notified_time() const45 std::optional<SystemClock::time_point> notified_time() const {
46 return notified_time_;
47 }
task_handle() const48 TaskHandle_t task_handle() const { return task_handle_; }
49
Run()50 void Run() {
51 task_handle_ = xTaskGetCurrentTaskHandle();
52 started_notification_.release();
53 unblock_notification_.acquire();
54 notified_time_ = SystemClock::now();
55 finished_notification_.release();
56 }
57
58 TaskHandle_t task_handle_;
59 ThreadNotification started_notification_;
60 ThreadNotification unblock_notification_;
61 ThreadNotification finished_notification_;
62 std::optional<SystemClock::time_point> notified_time_;
63 };
64
TEST(ThreadNotification,AcquireWithoutSuspend)65 TEST(ThreadNotification, AcquireWithoutSuspend) {
66 NotificationAcquirer notification_acquirer;
67 // TODO: b/290860904 - Replace TestOptionsThread0 with TestThreadContext.
68 pw::Thread thread =
69 pw::Thread(thread::test::TestOptionsThread0(),
70 [¬ification_acquirer] { notification_acquirer.Run(); });
71
72 notification_acquirer.WaitUntilRunning();
73 // At this point the thread is blocked and waiting on the notification.
74 const SystemClock::time_point release_time = SystemClock::now();
75 notification_acquirer.Release();
76 notification_acquirer.WaitUntilFinished();
77 ASSERT_TRUE(notification_acquirer.notified_time().has_value());
78 EXPECT_GE(notification_acquirer.notified_time().value(), release_time);
79
80 // Clean up the test thread context.
81 #if PW_THREAD_JOINING_ENABLED
82 thread.join();
83 #else
84 thread.detach();
85 thread::test::WaitUntilDetachedThreadsCleanedUp();
86 #endif // PW_THREAD_JOINING_ENABLED
87 }
88
TEST(ThreadNotification,AcquireWithSuspend)89 TEST(ThreadNotification, AcquireWithSuspend) {
90 NotificationAcquirer notification_acquirer;
91 pw::Thread thread =
92 pw::Thread(thread::test::TestOptionsThread0(),
93 [¬ification_acquirer] { notification_acquirer.Run(); });
94
95 notification_acquirer.WaitUntilRunning();
96
97 // Suspend and resume the task before notifying it, which should cause the
98 // internal xTaskNotifyWait to stop blocking and return pdFALSE upon resume.
99 vTaskSuspend(notification_acquirer.task_handle());
100 vTaskResume(notification_acquirer.task_handle());
101
102 // Sleep for at least one tick to ensure the time moved forward to let us
103 // observe the unblock time is in fact after resumed it.
104 this_thread::sleep_for(SystemClock::duration(1));
105
106 // At this point the thread is blocked and waiting on the notification.
107 const SystemClock::time_point release_time = SystemClock::now();
108 notification_acquirer.Release();
109 notification_acquirer.WaitUntilFinished();
110 ASSERT_TRUE(notification_acquirer.notified_time().has_value());
111 EXPECT_GE(notification_acquirer.notified_time().value(), release_time);
112
113 // Clean up the test thread context.
114 #if PW_THREAD_JOINING_ENABLED
115 thread.join();
116 #else
117 thread.detach();
118 thread::test::WaitUntilDetachedThreadsCleanedUp();
119 #endif // PW_THREAD_JOINING_ENABLED
120 }
121
122 #endif // INCLUDE_vTaskSuspend == 1
123
124 } // namespace pw::sync::freertos
125