blob: 9fc5f08fff9b418af2f790cf2e136c0c8fb498d7 [file] [log] [blame]
Ewout van Bekkum258171a2021-11-01 12:46:03 -07001// Copyright 2021 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 "FreeRTOS.h"
18#include "pw_assert/check.h"
19#include "pw_interrupt/context.h"
20#include "pw_sync_freertos/config.h"
21#include "task.h"
22
23namespace pw::sync {
24namespace {
25
26BaseType_t WaitForNotification(TickType_t xTicksToWait) {
27#ifdef configTASK_NOTIFICATION_ARRAY_ENTRIES
28 return xTaskNotifyWaitIndexed(
29 pw::sync::freertos::config::kThreadNotificationIndex,
30 0, // Clear no bits on entry.
31 0, // Clear no bits on exit.
32 nullptr, // Don't care about the notification value.
33 xTicksToWait);
34#else // !configTASK_NOTIFICATION_ARRAY_ENTRIES
35 return xTaskNotifyWait(0, // Clear no bits on entry.
36 0, // Clear no bits on exit.
37 nullptr, // Don't care about the notification value.
38 xTicksToWait);
39#endif // configTASK_NOTIFICATION_ARRAY_ENTRIES
40}
41
42} // namespace
43
44void ThreadNotification::acquire() {
Ewout van Bekkuma6642d72021-11-05 09:43:14 -070045 // Enforce the pw::sync::ThreadNotification IRQ contract.
Ewout van Bekkum258171a2021-11-01 12:46:03 -070046 PW_DCHECK(!interrupt::InInterruptContext());
Ewout van Bekkuma6642d72021-11-05 09:43:14 -070047
48 // Enforce that only a single thread can block at a time.
Ewout van Bekkum258171a2021-11-01 12:46:03 -070049 PW_DCHECK(native_type_.blocked_thread == nullptr);
50
Ewout van Bekkumdabf9152022-03-18 10:07:01 -070051 // Ensure that no one forgot to clean up nor corrupted the task notification
52 // state in the TCB.
53 PW_DCHECK(xTaskNotifyStateClear(nullptr) == pdFALSE);
54
Ewout van Bekkum258171a2021-11-01 12:46:03 -070055 taskENTER_CRITICAL();
56 if (native_type_.notified) {
57 native_type_.notified = false;
58 taskEXIT_CRITICAL();
59 return;
60 }
61 // Not notified yet, set the task handle for a one-time notification.
62 native_type_.blocked_thread = xTaskGetCurrentTaskHandle();
63 taskEXIT_CRITICAL();
64
65#if INCLUDE_vTaskSuspend == 1 // This means portMAX_DELAY is indefinite.
66 const BaseType_t result = WaitForNotification(portMAX_DELAY);
67 PW_DCHECK_UINT_EQ(result, pdTRUE);
68#else // INCLUDE_vTaskSuspend != 1
69 while (WaitForNotification(portMAX_DELAY) == pdFALSE) {
70 }
71#endif // INCLUDE_vTaskSuspend
72
73 taskENTER_CRITICAL();
74 // The task handle was cleared by the notifier.
75 // Note that this may hide another notification, however this is considered
76 // a form of notification saturation just like as if this happened before
77 // acquire() was invoked.
78 native_type_.notified = false;
79 taskEXIT_CRITICAL();
80}
81
82void ThreadNotification::release() {
83 if (!interrupt::InInterruptContext()) { // Task context
84 taskENTER_CRITICAL();
85 if (native_type_.blocked_thread != nullptr) {
86#ifdef configTASK_NOTIFICATION_ARRAY_ENTRIES
87 xTaskNotifyIndexed(native_type_.blocked_thread,
88 pw::sync::freertos::config::kThreadNotificationIndex,
89 0u,
90 eNoAction);
91#else // !configTASK_NOTIFICATION_ARRAY_ENTRIES
92 xTaskNotify(native_type_.blocked_thread, 0u, eNoAction);
93#endif // configTASK_NOTIFICATION_ARRAY_ENTRIES
94
95 native_type_.blocked_thread = nullptr;
96 }
97 native_type_.notified = true;
98 taskEXIT_CRITICAL();
99 return;
100 }
101
102 // Interrupt context
103 const UBaseType_t saved_interrupt_mask = taskENTER_CRITICAL_FROM_ISR();
104 if (native_type_.blocked_thread != nullptr) {
105 BaseType_t woke_higher_task = pdFALSE;
106
107#ifdef configTASK_NOTIFICATION_ARRAY_ENTRIES
108 xTaskNotifyIndexedFromISR(
109 native_type_.blocked_thread,
110 pw::sync::freertos::config::kThreadNotificationIndex,
111 0u,
112 eNoAction,
113 &woke_higher_task);
114#else // !configTASK_NOTIFICATION_ARRAY_ENTRIES
115 xTaskNotifyFromISR(
116 native_type_.blocked_thread, 0u, eNoAction, &woke_higher_task);
117#endif // configTASK_NOTIFICATION_ARRAY_ENTRIES
118
119 native_type_.blocked_thread = nullptr;
120 portYIELD_FROM_ISR(woke_higher_task);
121 }
122 native_type_.notified = true;
123 taskEXIT_CRITICAL_FROM_ISR(saved_interrupt_mask);
124}
125
126} // namespace pw::sync