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
DerefandDerefMut - Lock release when guard goes out of scope
Sources: src/lib.rs(L15 - L18) src/base.rs (inherited interface)