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