xref: /aosp_15_r20/external/pigweed/pw_sync_freertos/timed_thread_notification_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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/timed_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_thread/thread_core.h"
26 #include "pw_unit_test/framework.h"
27 #include "task.h"
28 
29 namespace pw::sync::freertos {
30 namespace {
31 
32 using pw::chrono::SystemClock;
33 
34 }  // namespace
35 
36 // These tests are targeted specifically to verify interactions between suspend
37 // and being blocked on direct task notifications and how they impact usage of
38 // the FreeRTOS optimized TimedThreadNotification backend.
39 #if INCLUDE_vTaskSuspend == 1
40 
41 class TimedNotificationAcquirer : public thread::ThreadCore {
42  public:
WaitUntilRunning()43   void WaitUntilRunning() { started_notification_.acquire(); }
Release()44   void Release() { unblock_notification_.release(); }
WaitUntilFinished()45   void WaitUntilFinished() { finished_notification_.acquire(); }
notified_time() const46   std::optional<SystemClock::time_point> notified_time() const {
47     return notified_time_;
48   }
task_handle() const49   TaskHandle_t task_handle() const { return task_handle_; }
50 
51  private:
Run()52   void Run() final {
53     task_handle_ = xTaskGetCurrentTaskHandle();
54     started_notification_.release();
55     if (unblock_notification_.try_acquire_until(
56             SystemClock::TimePointAfterAtLeast(std::chrono::hours(42)))) {
57       notified_time_ = SystemClock::now();
58     }
59     finished_notification_.release();
60   }
61 
62   TaskHandle_t task_handle_;
63   TimedThreadNotification started_notification_;
64   TimedThreadNotification unblock_notification_;
65   ThreadNotification finished_notification_;
66   std::optional<SystemClock::time_point> notified_time_;
67 };
68 
TEST(TimedThreadNotification,AcquireWithoutSuspend)69 TEST(TimedThreadNotification, AcquireWithoutSuspend) {
70   TimedNotificationAcquirer notification_acquirer;
71   // TODO: b/290860904 - Replace TestOptionsThread0 with TestThreadContext.
72   pw::Thread thread =
73       pw::Thread(thread::test::TestOptionsThread0(), notification_acquirer);
74 
75   notification_acquirer.WaitUntilRunning();
76   // At this point the thread is blocked and waiting on the notification.
77   const SystemClock::time_point release_time = SystemClock::now();
78   notification_acquirer.Release();
79   notification_acquirer.WaitUntilFinished();
80   ASSERT_TRUE(notification_acquirer.notified_time().has_value());
81   EXPECT_GE(notification_acquirer.notified_time().value(), release_time);
82 
83   // Clean up the test thread context.
84 #if PW_THREAD_JOINING_ENABLED
85   thread.join();
86 #else
87   thread.detach();
88   thread::test::WaitUntilDetachedThreadsCleanedUp();
89 #endif  // PW_THREAD_JOINING_ENABLED
90 }
91 
TEST(TimedThreadNotification,AcquireWithSuspend)92 TEST(TimedThreadNotification, AcquireWithSuspend) {
93   TimedNotificationAcquirer notification_acquirer;
94   pw::Thread thread =
95       pw::Thread(thread::test::TestOptionsThread0(), notification_acquirer);
96 
97   notification_acquirer.WaitUntilRunning();
98 
99   // Suspend and resume the task before notifying it, which should cause the
100   // internal xTaskNotifyWait to stop blocking and return pdFALSE upon resume.
101   vTaskSuspend(notification_acquirer.task_handle());
102   vTaskResume(notification_acquirer.task_handle());
103 
104   // Sleep for at least one tick to ensure the time moved forward to let us
105   // observe the unblock time is in fact after resumed it.
106   this_thread::sleep_for(SystemClock::duration(1));
107 
108   // At this point the thread is blocked and waiting on the notification.
109   const SystemClock::time_point release_time = SystemClock::now();
110   notification_acquirer.Release();
111   notification_acquirer.WaitUntilFinished();
112   ASSERT_TRUE(notification_acquirer.notified_time().has_value());
113   EXPECT_GE(notification_acquirer.notified_time().value(), release_time);
114 
115   // Clean up the test thread context.
116 #if PW_THREAD_JOINING_ENABLED
117   thread.join();
118 #else
119   thread.detach();
120   thread::test::WaitUntilDetachedThreadsCleanedUp();
121 #endif  // PW_THREAD_JOINING_ENABLED
122 }
123 
124 #endif  // INCLUDE_vTaskSuspend == 1
125 
126 }  // namespace pw::sync::freertos
127