blob: 2d00f93e52dbb456dd38519f388639d1dc21f7dc [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#ifndef ZIRCON_KERNEL_INCLUDE_KERNEL_AUTO_PREEMPT_DISABLER_H_
#define ZIRCON_KERNEL_INCLUDE_KERNEL_AUTO_PREEMPT_DISABLER_H_
#include <kernel/thread.h>
// AutoPreemptDisabler is a RAII helper that automatically manages disabling and
// re-enabling preemption. When the object goes out of scope, it automatically
// re-enables preemption if it had been previously disabled by the instance.
//
// Example usage:
//
// // Immediately disable preemption, then obtain the list_ lock and append an
// // element to the list.
// {
// AutoPreemptDisabler preempt_disabler;
// Guard<Mutex> guard{&lock_};
// list_.push_back(ktl::move(element_uptr));
// }
//
// // Reserve the option to disable preemption, but do not do so right now.
// {
// AutoPreemptDisabler preempt_disabler{AutoPreemptDisabler::Defer};
// Guard<Mutex> guard{&lock_};
//
// // Do some work.
//
// if (predicate()) {
// preempt_disabler.Disable();
// // Do some more work with preemption disabled.
// }
// } // lock_ is released first, then (if predicate() was true), preemption is re-enabled.
//
class AutoPreemptDisabler {
public:
// Tag type to construct the AutoPreemptDisabler without preemption initially
// disabled.
enum DeferType { Defer };
AutoPreemptDisabler() { Thread::Current::preemption_state().PreemptDisable(); }
explicit AutoPreemptDisabler(DeferType) : disabled_{false} {}
~AutoPreemptDisabler() {
if (disabled_) {
Thread::Current::preemption_state().PreemptReenable();
}
}
AutoPreemptDisabler(const AutoPreemptDisabler&) = delete;
AutoPreemptDisabler& operator=(const AutoPreemptDisabler&) = delete;
AutoPreemptDisabler(AutoPreemptDisabler&&) = delete;
AutoPreemptDisabler& operator=(AutoPreemptDisabler&&) = delete;
// Disables preemption if it was not disabled by this instance already.
void Disable() {
if (!disabled_) {
Thread::Current::preemption_state().PreemptDisable();
disabled_ = true;
}
}
private:
bool disabled_{true};
};
#endif // ZIRCON_KERNEL_INCLUDE_KERNEL_AUTO_PREEMPT_DISABLER_H_