surelock 0.1.0

Deadlock-free locks for Rust with compile time guarantees, incremental locks, and atomic lock sets.
Documentation
# Surelock `no_std` Guide

Surelock supports `no_std` environments when default features are disabled. The core library requires only `alloc`. This guide covers what changes on `no_std` and what patterns to follow.

## Feature Configuration

```toml
[dependencies]
# Minimal no_std (AtomicU32 for LockId, works on Cortex-M4+, RISC-V with CAS, etc.)
surelock = { version = "0.1", default-features = false }

# no_std with u64 LockId (requires native AtomicU64)
surelock = { version = "0.1", default-features = false, features = ["atomic-u64"] }

# no_std for Cortex-M0 / thumbv6m (no native CAS)
surelock = { version = "0.1", default-features = false, features = ["cortex-m"] }

# no_std for other targets without CAS
surelock = { version = "0.1", default-features = false, features = ["critical-section"] }

# no_std with lock_api backend (parking_lot, spin, etc.)
surelock = { version = "0.1", default-features = false, features = ["lock-api"] }
```

## What's Different on `no_std`

### No Default Mutex Backend

On `std`, `Mutex<u32>` defaults to `StdMutex` (wrapping `std::sync::Mutex`). On `no_std`, there is no default backend -- you must specify one:

```rust
use surelock::mutex::Mutex;

// With lock-api feature + spin backend
type SpinMutex<T> = Mutex<T, surelock::level::Base, spin::mutex::SpinMutex<()>>;

let counter: SpinMutex<u32> = Mutex::new(0);
```

### No `lock_scope` / `try_lock_scope`

These ambient entry points are `#[cfg(feature = "std")]` only. They use `thread_local!` for nesting prevention, which isn't available on `no_std`.

On `no_std`, use `KeyHandle` directly:

```rust
use surelock::key_handle::KeyHandle;

let mut handle = KeyHandle::claim();
handle.scope(|key| {
    // lock things here
});
```

`scope(&mut self)` prevents nesting at compile time via the borrow checker -- no `thread_local!` needed.

### No `thread_local!` Scope Uniqueness

On `std`, `KeyHandle::try_claim()` checks a `thread_local!` flag and returns `None` if a handle already exists on the current thread. On `no_std`, `try_claim()` always returns `Some` -- there is no per-context uniqueness check.

This means multiple `KeyHandle`s can coexist on the same core on `no_std`. Each handle's `scope(&mut self)` prevents nesting _within that handle_, but two handles on the same core could produce independent keys that defeat the ordering guarantee.

## Recommended Patterns

### Single-Core (Cortex-M0, Cortex-M4, etc.)

Create one `KeyHandle` in `main()` and thread `&mut handle` to wherever you need scopes:

```rust
#[entry]
fn main() -> ! {
    let mut handle = KeyHandle::claim();

    loop {
        do_work(&mut handle);
    }
}

fn do_work(handle: &mut KeyHandle) {
    handle.scope(|key| {
        // lock things
    });
}
```

One core, one handle, one scope at a time. The `&mut` borrow ensures this at compile time.

### Multi-Core (Cortex-A, RISC-V SMP, etc.)

Use `Locksmith` to distribute one `KeyHandle` per core during init:

```rust
use surelock::locksmith::Locksmith;

static SMITH: Locksmith = Locksmith::create(NUM_CORES);

fn core0_init() {
    // Issue a voucher for each other core
    let voucher_1 = SMITH.issue().unwrap();
    send_to_core(1, voucher_1);

    // Claim a handle for this core
    let mut handle = SMITH.issue().unwrap().redeem().unwrap();
    handle.scope(|key| { /* ... */ });
}

fn core1_main(voucher: KeyVoucher) {
    let mut handle = voucher.redeem().unwrap();
    handle.scope(|key| { /* ... */ });
}
```

The `Locksmith` limits total issuance to `NUM_CORES`. Each core redeems its voucher for a `KeyHandle`. The discipline is: send exactly one voucher per core during init.

### What To Avoid

Do not call `KeyHandle::claim()` more than once per core. On `no_std`, `claim()` always succeeds -- there is no check for an existing handle. Two handles on the same core can produce independent keys that bypass ordering enforcement.

If you need nested locking contexts, use `key.subscope()` within an existing scope -- not a second `KeyHandle`.

## Targets Without CAS (thumbv6m)

Cortex-M0 and similar targets lack hardware compare-and-swap (CAS) operations. Surelock uses `fetch_add`, `compare_exchange`, and `fetch_update` on atomics, which require CAS.

For Cortex-M targets, use the `cortex-m` convenience feature:

```toml
surelock = { version = "0.1", default-features = false, features = ["cortex-m"] }
```

This enables `portable-atomic` with `critical-section` support. You will also need a `critical-section` implementation for your target. For Cortex-M, the [`cortex-m`](https://crates.io/crates/cortex-m) crate provides one:

```toml
[dependencies]
surelock = { version = "0.1", default-features = false, features = ["cortex-m"] }
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
```

For other targets without CAS, use the `critical-section` feature directly and provide your own `critical-section` implementation:

```toml
surelock = { version = "0.1", default-features = false, features = ["critical-section"] }
```

For targets that have native CAS but lack `AtomicU64` (e.g., some RISC-V), use `portable-atomic` without `critical-section`:

```toml
surelock = { version = "0.1", default-features = false, features = ["portable-atomic"] }
```

## Summary

| Concern                   | `std`                              | `no_std`                               |
|---------------------------|------------------------------------|----------------------------------------|
| Default backend           | `StdMutex`                         | Must specify (e.g., `spin`)            |
| Scope entry               | `lock_scope` / `KeyHandle`         | `KeyHandle` only                       |
| Nesting prevention        | `thread_local!` + `&mut`           | `&mut` only (compile-time)             |
| Per-context uniqueness    | `thread_local!` flag               | Setup discipline                       |
| Multi-core distribution   | One `KeyHandle` per thread (auto)  | `Locksmith` + `KeyVoucher` (explicit)  |
| Targets without CAS       | N/A                                | Enable `portable-atomic` feature       |