xref: /aosp_15_r20/external/pigweed/pw_sync_freertos/interrupt_spin_lock.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 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/interrupt_spin_lock.h"
16 
17 #include "pw_assert/check.h"
18 #include "pw_interrupt/context.h"
19 #include "task.h"
20 
21 namespace pw::sync {
22 
23 #if (INCLUDE_xTaskGetSchedulerState != 1) && (configUSE_TIMERS != 1)
24 #error "xTaskGetSchedulerState is required for pw::sync::InterruptSpinLock"
25 #endif
26 
lock()27 void InterruptSpinLock::lock() {
28   if (interrupt::InInterruptContext()) {
29     native_type_.saved_interrupt_mask = taskENTER_CRITICAL_FROM_ISR();
30   } else {  // Task context
31     // Suspending the scheduler ensures that kernel API calls that occur
32     // within the critical section will not preempt the current task
33     // (if called from a thread context).  Otherwise, kernel APIs called
34     // from within the critical section may preempt the running task if
35     // the port implements portYIELD synchronously.
36     // Note: calls to vTaskSuspendAll(), like taskENTER_CRITICAL() can
37     // be nested.
38     // Note: vTaskSuspendAll()/xTaskResumeAll() are not safe to call before the
39     // scheduler has been started.
40     if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
41       vTaskSuspendAll();
42     }
43     taskENTER_CRITICAL();
44   }
45   // We can't deadlock here so crash instead.
46   PW_DCHECK(!native_type_.locked,
47             "Recursive InterruptSpinLock::lock() detected");
48   native_type_.locked = true;
49 }
50 
unlock()51 void InterruptSpinLock::unlock() {
52   native_type_.locked = false;
53   if (interrupt::InInterruptContext()) {
54     taskEXIT_CRITICAL_FROM_ISR(native_type_.saved_interrupt_mask);
55   } else {  // Task context
56     taskEXIT_CRITICAL();
57     if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
58       xTaskResumeAll();
59     }
60   }
61 }
62 
63 }  // namespace pw::sync
64