global-lib 1.0.0

Process-wide values for Rust: set, define, read, edit, and remove globals by name or through typed statics, with lock-free reads. Zero dependencies.
# v1.0.0 — Stable API

`global-lib` is released directly as 1.0. This is its first release. The public API and the semantics of every operation are frozen for the 1.x line under [`STABILITY-1.0.md`](../STABILITY-1.0.md). Every later 1.x release is backwards compatible with this one.

## What `global-lib` Is

Process-wide values for Rust. Set a value once and read it from anywhere in the program, change it later, or remove it, from any thread. Values are addressed by name, through free functions on one process-wide store or through a `Globals` of your own, and may be of any `Send + Sync + 'static` type. For values read on hot paths, a typed `static Global<T>` is checked at compile time and skips the name lookup. Reads are lock-free and allocation-free; writes are serialized and copy-on-write. The crate has no dependencies.

```rust
use global_lib::Global;

global_lib::set("port", 7000u16);
global_lib::define("app_name", "HiveDB")?;
global_lib::edit::<u16>("port", |p| *p += 1)?;
let port: Option<u16> = global_lib::value("port");

static MAX_CONNECTIONS: Global<usize> = Global::new();
MAX_CONNECTIONS.set(64);
let max = MAX_CONNECTIONS.with(|n| *n).unwrap_or(16);
# Ok::<(), global_lib::Error>(())
```

```toml
[dependencies]
global-lib = "1.0"
```

## Features

### Globals by name

- **Free functions** `set`, `define`, `get`, `try_get`, `value`, `with`, `edit`, `remove`, and `contains` operate on one process-wide store, created on first use. **`globals()`** returns that store as `&'static Globals`.
- **`Globals`** is the store type. Create your own for an isolated group of values, or one per test. Beyond the methods above it has `type_of`, `len`, `is_empty`, `names` (sorted), and `clear`.
- **Four ways to read.** `get` returns a shared `Arc<T>`, `try_get` the same with the reason for a miss, `value` a clone, and `with` runs a closure on the value in place, with no clone and no reference-count change.
- **Any type.** Numbers, strings, collections, and trait objects behind `Arc<dyn Trait + Send + Sync>` are all stored the same way.

### Typed statics

- **`static X: Global<T> = Global::new();`** declares a typed global. `Global::new` is a `const fn`.
- **`set`** returns the previous value. **`define`** is set-once and returns `Err(value)`, handing the value back, when one is already set. **`take`** removes the value and returns it.
- **`get`, `value`, `with`, `is_set`** read it; `with` is the fastest read in the crate, about 5 ns.
- **`edit`** returns `false`, without calling the closure, when nothing is set.

### Semantics

- **`set` replaces** a value of any type. **`define` is set-once**: it fails with `Error::AlreadyDefined` and keeps the existing value.
- **Exact types.** A value is found only under the type it was stored with. Otherwise `get`, `value`, and `with` return `None`, and `try_get` and `edit` return `Error::TypeMismatch { name, expected, found }`, which names both types.
- **No lost updates.** `edit` clones the value, runs the closure on the copy with no lock held, and swaps the copy in only if the value has not changed meanwhile; if it has, the edit starts again from the new value. Concurrent edits each take effect, and readers never see a half-edited value. The closure is `FnMut` and may run more than once, so it should only compute the new value.
- **Handles stay valid.** An `Arc` from `get` keeps its value alive and unchanged after the global is replaced or removed.
- **No deadlock from user code.** Writers never wait for readers, and no lock is held while a `with` or `edit` closure runs, so either closure may read or write any global, including the same store. The one unsupported pattern is writing the name being edited from inside its own `edit` closure, which makes the edit retry indefinitely.

### Secrets

Values never appear in `Debug` output or in errors, which tend to end up in logs. `Debug` for `Globals` lists names and types, sorted; `Debug` for `Global<T>` shows the type and whether a value is set. Errors name the global, truncated to 64 characters, and the types involved.

## How Reads Stay Lock-Free

Every store and every `Global<T>` keeps its current snapshot behind an atomic pointer. A read announces the snapshot in a per-thread hazard slot with one sequentially consistent store, validates it with one load, borrows it, and clears the announcement. A write builds a new snapshot (for a `Globals`, a copy of the whole name map), swaps it in under a per-store mutex, and retires the old one, which is freed as soon as no hazard announces it: by the writer immediately, or by the last reader holding it.

Readers share nothing they write, so read throughput grows with the number of threads. The price is on the write side: a write to a `Globals` is `O(n)` in the number of values it holds. Reads nested more than 8 levels deep on one thread fall back to a locked path with the same results.

The hazard-pointer core is the same design as `registry-io` 1.1. The crate's process-wide state is the default store and the per-thread hazard records, which are allocated once, reused by later threads, and bounded by the peak number of threads reading at the same time. `unsafe` code is confined to `src/hazard.rs` and `src/snapshot.rs`, with a `SAFETY` comment on every block; the crate root denies `unsafe_code` everywhere else.

## The Stable Surface

Every item re-exported from the crate root is part of the 1.x contract.

- **Free functions:** `set`, `define`, `get`, `try_get`, `value`, `with`, `edit`, `remove`, `contains`, `globals`.
- **`Globals`:** `new`; `set`, `define`, `get`, `try_get`, `value`, `with`, `edit`, `remove`, `contains`, `type_of`, `len`, `is_empty`, `names`, `clear`.
- **`Global<T>`:** `new` (`const`); `set`, `define`, `get`, `value`, `with`, `edit`, `take`, `is_set`.
- **Errors:** `Error` (`Missing`, `TypeMismatch`, `AlreadyDefined`), `Result`.

The semantics above, lock-free reads, set-once `define`, no lost updates from `edit`, and the guarantee that values never appear in errors or `Debug` output are part of the contract as well. The full list, and what may still change in minor releases, is in [`STABILITY-1.0.md`](../STABILITY-1.0.md).

## Verification

- **Tests.** 42 tests: 30 unit tests, 4 tests of the process-wide free functions, 4 concurrency tests, and 4 documentation examples. All pass on Windows and Linux on stable, on Linux at MSRV 1.85, and in 3 further runs on Linux pinned to 2 CPUs. The unit tests include an edit whose closure writes to the same store, an edit that retries after another write gets in between, an edit that reports the name being removed, and a rejected `define` value being dropped.
- **Concurrency.** The concurrency tests check that readers never see a torn value while two writers replace and edit it; that a typed global under contention only moves forward and loses no edit; that every replaced or removed value is dropped, by counting live instances; and that dropping a store drops its values.
- **Miri.** All 30 unit tests run clean under Miri, and the concurrent ones across 16 thread schedules, covering the hazard-pointer protocol, reclamation by writers and by the last reader, and the nested-read fallback.
- **Coverage.** Line coverage measured with `cargo-llvm-cov` on Windows is about 98%.
- **Lints.** `clippy::pedantic` is clean for `x86_64-pc-windows-msvc`, `x86_64-unknown-linux-gnu`, `aarch64-apple-darwin`, and `x86_64-unknown-freebsd`.

## Performance

| Operation | Median |
|---|---|
| `Global::with`, typed | 5.3 ns |
| `Global::value`, typed | 5.5 ns |
| `Globals::with`, by name | 16.5 ns |
| `Globals::value`, by name, missing name | 15.9 ns |
| `Globals::get`, by name, returns an `Arc` | 28.3 ns |
| `Globals::value`, by name | 33.4 ns |
| `Globals::set`, 32 values in the store | 1.21 µs |
| `Globals::edit`, 32 values in the store | 1.80 µs |
| `Global::set`, typed | 120 ns |

| Contended `Globals::with` | ns per read, aggregate |
|---|---|
| 1 thread | 25.7 |
| 4 threads | 4.7 |
| 16 threads | 2.8 |

Measured on Windows 11, x86_64, with criterion 0.5. A read by name pays for hashing the name with SipHash, chosen because names may come from input; a `Global<T>` skips the hash. Writes copy the name map, the deliberate trade for values that are read far more often than written; an edit is one read and clone followed by that copy-on-write swap. Full methodology and results are in [`docs/PERFORMANCE.md`](../PERFORMANCE.md).

## Compatibility

- **MSRV:** Rust 1.85, frozen for 1.x
- **Edition:** 2024
- **Platforms:** Linux, macOS, Windows (FreeBSD type-checked)
- **Dependencies:** none
- **Thread safety:** `Globals` is `Send + Sync`, and so is `Global<T>` for `T: Send + Sync + 'static`. Every method takes `&self`.

## Out of Scope

These were considered for 1.0 and left out on purpose:

- **Change notifications and subscriptions.** Use `registry-io`, which is built for them.
- **Persistence.** Values live for the life of the process. Use `env-lib` or `config-lib` to load and save them.
- **Scoped and thread-local values.** Every value is visible to every thread. Use `std::thread_local!` for per-thread values.
- **Async-aware waiting** for a value to be set. Set required globals during startup, before the code that reads them runs.