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:

ComponentDefinitionPurpose
SpinNoPreemptBaseSpinLock<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
NoPreemptGuard type fromkernel_guardcrateImplements 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

  1. IRQ Context Requirement: Must be used in local IRQ-disabled context
  2. Interrupt Handler Prohibition: Never use in interrupt handlers
  3. 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 TypePreemption ControlIRQ ControlUsage ContextPerformance
SpinRawNoneNoneIRQ-disabled + preemption-disabledFastest
SpinNoPreemptDisabled during lockMust be externally disabledIRQ-disabled contextsBalanced
SpinNoIrqDisabled during lockDisabled during lockAny contextSafest 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:

MethodReturn TypePurpose
new(data: T)SelfCreate 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 and DerefMut
  • Lock release when guard goes out of scope

Sources: src/lib.rs(L15 - L18)  src/base.rs (inherited interface)