Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- **`GhostTreeBstackAllocator` version bumped to 0.1.2** (`alloc` + `set` features): Magic number updated from `ALGT\x00\x01\x01\x00` to `ALGT\x00\x01\x02\x00`. Reflects the addition of `atomic` / `Sync` support. Existing 0.1.x files remain fully compatible (only the first 6 bytes are checked on open).
- **`SlabBStackAllocator` version bumped to 0.1.1** (`alloc` + `set` features): Magic number updated from `ALSL\x00\x01\x00\x00` to `ALSL\x00\x01\x01\x00`. Reflects the addition of `atomic` / `Sync` support. Existing 0.1.x files remain fully compatible (only the first 6 bytes are checked on open).
- **`CheckedSlabBStackAllocator` version bumped to 0.1.1** (`alloc` + `set` features): Magic number updated from `ALCK\x00\x01\x00\x00` to `ALCK\x00\x01\x01\x00`. Reflects the addition of `atomic` / `Sync` support. Existing 0.1.x files remain fully compatible (only the first 6 bytes are checked on open).
- **`SlabBStackAllocator` is `Send + Sync` with the `atomic` feature** (`alloc` + `set` features): Without `atomic`, free-list mutations read then write `free_head` as separate `BStack` calls — a TOCTOU race that can hand the same block to two callers. With `atomic`, an internal mutex serialises free-list pop/push; tail operations use `BStack::try_discard` / `BStack::try_extend_zeros` (atomic check-and-act under `BStack`'s own write lock, no allocator lock needed). Non-tail paths lock only around `push_free_blocks`.
- **`CheckedSlabBStackAllocator` is `Send + Sync` with the `atomic` feature** (`alloc` + `set` features): Same mutex model. Free-list pop in `alloc` is lock-scoped; tail extend runs lock-free. `dealloc` uses `try_discard` for the tail path without the lock; free-list push is locked. In `realloc`, the grow path uses `try_extend_zeros` lock-free; the shrink path holds the lock across tail-check + overhead-write + discard (overhead must be committed before truncation for crash safety). `recover` holds the lock for its full duration.
- **`GhostTreeBstackAllocator` is `Send + Sync` with the `atomic` feature** (`alloc` + `set` features): Without `atomic`, all allocator operations take `&self` and mutate the on-disk AVL tree — concurrent shared access from multiple threads would race on that state. With `atomic`, an internal `Mutex` serialises all AVL tree mutations (`avl_insert`, `avl_find_best_fit_and_remove`, `write_root`); tail operations use `BStack::try_discard` / `BStack::try_extend_zeros` (check-and-act atomically under `BStack`'s own write lock, no allocator lock needed). The `PhantomData<Cell<()>>` field that previously opted out of `Sync` is replaced by the `Mutex`, which confers `Sync` without an `unsafe impl`. Documentation updated across type-level docs, module overview, crate overview, and README.

---

Expand Down
19 changes: 14 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ bstack = { version = "0.2", features = ["alloc"] }
┌─────────────────────────────┐ payload offset 0
│ User-reserved (32 bytes) │
├─────────────────────────────┤ offset 32
│ Magic number (8 bytes) │ "ALGT\x00\x01\x00\x00"
│ Magic number (8 bytes) │ "ALGT\x00\x01\x02\x00"
├─────────────────────────────┤ offset 40
│ AVL root pointer (8 B) │ absolute payload offset of the root node
├─────────────────────────────┤ offset 48 ← arena start (32-byte aligned)
Expand All @@ -865,10 +865,19 @@ imbalanced — corrected on the next `GhostTreeBstackAllocator::new`.

#### Thread safety

`GhostTreeBstackAllocator` is **`Send`** but **not `Sync`**. Ownership can be
transferred to another thread, but concurrent `&self` access from multiple
threads would race on the on-disk AVL tree without any allocator-level lock.
Each instance must be used from at most one thread at a time.
`GhostTreeBstackAllocator` is always **`Send`** — ownership can be transferred
to another thread.

Without the `atomic` feature it is **not `Sync`**: all allocator operations
take `&self` and mutate the on-disk AVL tree, so concurrent shared access from
multiple threads would race on that state. Each instance must be used from at
most one thread at a time.

With the `atomic` feature it is **`Send + Sync`**. An internal `Mutex`
serialises all AVL tree mutations; tail operations use
`BStack::try_discard` / `BStack::try_extend_zeros`, which check-and-act
atomically under `BStack`'s own write lock without holding the allocator
mutex.

#### Example

Expand Down
Loading
Loading