third_party.pigweed.src/pw_sync_embos/interrupt_spin_lock.cc
Ewout van Bekkum ebe9b8529a pw_sync_embos: Disable task switching in the ISL
Updates the pw::sync::InterruptSpinLock implementation for embOS to
disable task switching if not in an interrupt. This way there's no
risk that the current task gets switched out to another which
recursively attempts to grab the same lock.

This also changes the internal state to use a plain bool instead
of an atomic bool as an atomic is not necessary.

Change-Id: Ic30bdbee2b298398056d0e5dcdae878ac681f95d
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/50462
Reviewed-by: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Pigweed-Auto-Submit: Ewout van Bekkum <ewout@google.com>
2021-06-22 23:03:09 +00:00

62 lines
1.8 KiB
C++

// Copyright 2021 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_sync/interrupt_spin_lock.h"
#include "RTOS.h"
#include "pw_assert/check.h"
namespace pw::sync {
void InterruptSpinLock::lock() {
// Mask interrupts.
OS_IncDI();
// Disable task switching to ensure kernel APIs cannot switch to other tasks
// which could then end up deadlocking recursively on this same lock.
OS_SuspendAllTasks();
// We can't deadlock here so crash instead.
PW_CHECK(!native_type_.locked,
"Recursive InterruptSpinLock::lock() detected");
native_type_.locked = true;
}
bool InterruptSpinLock::try_lock() {
// Mask interrupts.
OS_IncDI();
// Disable task switching to ensure kernel APIs cannot switch to other tasks
// which could then end up deadlocking recursively on this same lock.
OS_SuspendAllTasks();
if (native_type_.locked) {
// Already locked, bail out.
OS_ResumeAllSuspendedTasks(); // Restore task switching.
OS_DecRI(); // Restore interrupts.
return false;
}
native_type_.locked = true;
return true;
}
void InterruptSpinLock::unlock() {
native_type_.locked = false;
OS_ResumeAllSuspendedTasks(); // Restore task switching.
OS_DecRI(); // Restore interrupts.
}
} // namespace pw::sync