SpinNoPreempt
Relevant source files
This document describes the SpinNoPreempt
spinlock type, which provides kernel-space synchronization by disabling preemption during critical sections. This spinlock offers a balanced approach between performance and safety, suitable for IRQ-disabled contexts where preemption control is necessary but interrupt masking is already handled externally.
For information about the raw spinlock without protection mechanisms, see SpinRaw. For the full-protection spinlock that also disables IRQs, see SpinNoIrq. For comprehensive usage guidelines across all spinlock types, see Usage Guidelines and Safety.
Type Definition and Core Components
SpinNoPreempt<T>
is implemented as a type alias that combines the generic BaseSpinLock
with the NoPreempt
guard type from the kernel_guard
crate. This composition provides preemption-disabled synchronization while maintaining type safety and RAII semantics.
Type Composition Diagram
flowchart TD SpinNoPreempt["SpinNoPreempt<T>"] BaseSpinLock["BaseSpinLock<NoPreempt, T>"] NoPreempt["NoPreempt"] SpinNoPreemptGuard["SpinNoPreemptGuard<'a, T>"] BaseSpinLockGuard["BaseSpinLockGuard<'a, NoPreempt, T>"] BaseSpinLock --> NoPreempt BaseSpinLock --> SpinNoPreemptGuard BaseSpinLockGuard --> NoPreempt SpinNoPreempt --> BaseSpinLock SpinNoPreemptGuard --> BaseSpinLockGuard
Sources: src/lib.rs(L15 - L18)
The key components are:
Component | Definition | Purpose |
---|---|---|
SpinNoPreempt | BaseSpinLock<NoPreempt, T> | Main spinlock type that holds data of typeT |
SpinNoPreemptGuard<'a, T> | BaseSpinLockGuard<'a, NoPreempt, T> | RAII guard providing mutable access to protected data |
NoPreempt | Guard type fromkernel_guardcrate | Implements preemption disable/enable behavior |
Sources: src/lib.rs(L15 - L18) src/lib.rs(L6)
Safety Requirements and Usage Context
SpinNoPreempt
has specific safety requirements that must be understood before use. The spinlock is designed for contexts where IRQ handling is already managed externally, but preemption control is needed for the critical section.
Safety Context Requirements
flowchart TD subgraph subGraph2["SpinNoPreempt Behavior"] AcquireLock["lock() called"] DisablePreemption["NoPreempt::acquire()"] CriticalExecution["Protected code execution"] EnablePreemption["NoPreempt::release()"] ReleaseLock["Guard dropped"] end subgraph subGraph1["Unsafe Usage Contexts"] IRQEnabled["IRQ-Enabled Context"] InterruptHandler["Interrupt Handler"] UserSpace["User Space"] end subgraph subGraph0["Safe Usage Contexts"] IRQDisabled["IRQ-Disabled Context"] KernelCode["Kernel Code with IRQ Control"] CriticalSection["Existing Critical Section"] end AcquireLock --> DisablePreemption CriticalExecution --> EnablePreemption CriticalSection --> AcquireLock DisablePreemption --> CriticalExecution EnablePreemption --> ReleaseLock IRQDisabled --> AcquireLock IRQEnabled --> AcquireLock InterruptHandler --> AcquireLock KernelCode --> AcquireLock
Sources: src/lib.rs(L10 - L14)
Key Safety Rules
- IRQ Context Requirement: Must be used in local IRQ-disabled context
- Interrupt Handler Prohibition: Never use in interrupt handlers
- Preemption Assumption: Assumes external IRQ management is already in place
The documentation explicitly states: "It must be used in the local IRQ-disabled context, or never be used in interrupt handlers."
Sources: src/lib.rs(L13 - L14)
Implementation Architecture
SpinNoPreempt
leverages the modular architecture of the kspin crate, where the BaseSpinLock
provides the core spinning logic and the NoPreempt
guard type defines the protection behavior.
Lock Acquisition Flow
Sources: src/lib.rs(L15) src/base.rs (referenced via BaseSpinLock usage)
Comparison with Other Spinlock Types
SpinNoPreempt
occupies the middle ground in the protection spectrum provided by the kspin crate, offering more protection than SpinRaw
but less overhead than SpinNoIrq
.
Protection Level Comparison
Spinlock Type | Preemption Control | IRQ Control | Usage Context | Performance |
---|---|---|---|---|
SpinRaw | None | None | IRQ-disabled + preemption-disabled | Fastest |
SpinNoPreempt | Disabled during lock | Must be externally disabled | IRQ-disabled contexts | Balanced |
SpinNoIrq | Disabled during lock | Disabled during lock | Any context | Safest but slower |
Sources: src/lib.rs(L15) src/lib.rs(L24) src/lib.rs(L33)
Guard Type Mapping
flowchart TD subgraph subGraph2["Protection Behavior"] NoneProtection["No protection"] PreemptProtection["Preemption disabled"] FullProtection["Preemption + IRQ disabled"] end subgraph subGraph1["Guard Types (kernel_guard)"] NoOp["NoOp"] NoPreempt["NoPreempt"] NoPreemptIrqSave["NoPreemptIrqSave"] end subgraph subGraph0["Spinlock Types"] SpinRaw["SpinRaw<T>"] SpinNoPreempt["SpinNoPreempt<T>"] SpinNoIrq["SpinNoIrq<T>"] end NoOp --> NoneProtection NoPreempt --> PreemptProtection NoPreemptIrqSave --> FullProtection SpinNoIrq --> NoPreemptIrqSave SpinNoPreempt --> NoPreempt SpinRaw --> NoOp
Sources: src/lib.rs(L6) src/lib.rs(L15) src/lib.rs(L24) src/lib.rs(L33)
Usage Patterns and Examples
The typical usage pattern for SpinNoPreempt
follows the RAII principle, where the guard automatically manages preemption state during its lifetime.
Basic Usage Pattern
From the repository examples, the standard pattern is:
let data = SpinNoPreempt::new(());
let mut guard = data.lock();
/* critical section, preemption are disabled. */
drop(guard);
Sources: README.md(L24 - L27)
Method Interface
SpinNoPreempt<T>
inherits all methods from BaseSpinLock<NoPreempt, T>
, providing the standard spinlock interface:
Method | Return Type | Purpose |
---|---|---|
new(data: T) | Self | Create new spinlock with initial data |
lock() | SpinNoPreemptGuard<'_, T> | Acquire lock, blocking until available |
try_lock() | Option<SpinNoPreemptGuard<'_, T>> | Attempt to acquire lock without blocking |
The guard provides:
- Automatic preemption re-enabling on drop
- Mutable access to protected data via
Deref
andDerefMut
- Lock release when guard goes out of scope
Sources: src/lib.rs(L15 - L18) src/base.rs (inherited interface)