Skip to content

Latest commit

 

History

History
107 lines (78 loc) · 4.89 KB

AGENTS.md

File metadata and controls

107 lines (78 loc) · 4.89 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Build and Test Commands

Use the helper scripts in scripts/ for development. These handle platform differences and set correct flags.

# Run all checks (fmt, check, clippy, tests, docs) for both native and wasm32
./scripts/check_all

# Individual commands (run for both native and wasm32)
./scripts/check      # cargo check
./scripts/clippy     # cargo clippy
./scripts/tests      # cargo test
./scripts/docs       # cargo doc
./scripts/fmt        # cargo fmt --check

# Native-only or wasm32-only
./scripts/native/tests
./scripts/wasm32/tests

# Run a single test
cargo test test_name

# Use --relaxed to disable -D warnings (useful during development)
./scripts/tests --relaxed

Requirements

  • Rust 1.95.0+ (Rust 2024 edition)
  • For WASM testing: cargo +nightly, the wasm_lite runner (cargo install wasm_lite), and a browser with its WebDriver on PATH (Firefox/geckodriver or Chrome/chromedriver). WASM_LITE_BROWSER selects between them.

Architecture Overview

send_cells is a Rust library that provides cell types for safely sending and sharing non-Send/non-Sync types across thread boundaries. The library has two main categories:

Safe Wrappers (Runtime-Checked)

  • SendCell<T> (src/send_cell.rs): Wraps non-Send types with runtime thread checking. Panics if accessed from wrong thread.
  • SyncCell<T> (src/sync_cell.rs): Wraps non-Sync types with mutex-based synchronization for safe concurrent access.
  • SendFuture<T> (src/send_cell.rs): Wraps non-Send futures with runtime thread checking.

Unsafe Wrappers (Zero-Cost)

  • UnsafeSendCell<T> (src/unsafe_send_cell.rs): No runtime checks, requires unsafe blocks for access.
  • UnsafeSyncCell<T> (src/unsafe_sync_cell.rs): No runtime checks for Sync types.
  • UnsafeSendFuture<T> (src/unsafe_send_cell.rs): No runtime checks for futures.

Platform Support

  • src/sys.rs: Platform-specific thread ID implementation
  • Special support for wasm32-unknown-unknown with web workers via the wasm_lite_std dependency

Key Design Patterns

  1. Thread Safety Model: Safe wrappers store the creation thread ID and check it on every access. Unsafe wrappers bypass these checks for performance.

  2. API Design:

    • Safe wrappers provide get(), with(), with_mut() methods
    • Unsafe wrappers require explicit unsafe blocks
    • All types implement Send and/or Sync as appropriate
  3. Error Handling: Safe wrappers panic with descriptive messages when accessed from wrong thread. Include thread IDs in panic messages for debugging.

  4. Documentation: Every module has comprehensive rustdoc comments with examples. Documentation emphasizes safety requirements and use cases.

Development Notes

  • This is a low-level concurrency library - safety is paramount
  • All unsafe code must have clear SAFETY comments explaining invariants
  • Tests should verify both correct usage and panic cases for wrong-thread access
  • Consider platform differences, especially WASM support with web workers

How tests are written

Write each test once and let it run on both targets. #[wasm_lite::wasm_lite_test] is an ordinary libtest #[test] off wasm32 and a browser test on it — do not add a literal #[test] beside it (above the macro it registers twice), and do not reach for the old #[cfg_attr(target_arch = "wasm32", …)] pairing.

Four shapes cover everything here:

the test needs to… write it as
nothing special #[wasm_lite_test] fn
use another thread #[wasm_lite_test] async fn + wasm_lite_std::spawn + .join_async().await
assert something panics #[wasm_lite_test] #[should_panic(expected = "…")]
block (join, lock_block, park) #[wasm_lite_test(worker)]

Never call the blocking join from a test body: it traps on the browser's main thread, and worker creation is delegated to that thread, so blocking there deadlocks before the worker it waits on can start. join_async reports a panicking worker as Err on both targets — natively by unwinding into catch_unwind, on wasm32 through wasm_lite_std's panic hook, which publishes the message before the trap. So "did it panic?" needs no unwinding.

catch_unwind is the one thing that genuinely does. Reach for #[should_panic] instead wherever the test only cares that it panicked. What is left is a short list of legitimately native-only tests — observing what happens on a thread after it panics, and native mutex poisoning — each gated with #[cfg(not(target_arch = "wasm32"))] and a comment saying which of the two it is. Gate the item, never the attribute: an ungated test now runs on both targets.

Doctests that join a thread must be wrapped in wasm_lite_std::worker_doctest!, which moves the body off the browser's main thread and is a plain call natively.