Skip to content

Synchronization ​

The synchronization crate standardizes synchronization primitives used across Core.

Role ​

  • Re-exports embassy_sync primitives through one crate-local entry point.
  • Provides portable Arc/Weak support, including a custom fallback for targets without atomic pointer support.

Boundaries ​

  • In scope: synchronization primitives and ownership wrappers.
  • Out of scope: scheduler/executor implementation and task lifecycle policy.

Internal structure ​

  • lib.rs: top-level re-exports.
  • arc/mod.rs: target-gated arc selection.
    • With target_has_atomic = "ptr": uses alloc::sync::Arc/Weak.
    • Without pointer atomics: uses arc/arc_lock.rs mutex-backed Arc implementation.

Runtime interaction ​

  • Modules and executables import mutex/rwlock/once/channel primitives from this crate.
  • Fallback Arc implementation uses CriticalSectionRawMutex + RefCell counters for no-atomic targets.

Dependency model ​

  • Primary dependency: embassy-sync (std feature enabled on host targets).
  • Broadly consumed by file-system backends, graphics, and other concurrent modules.

Failure semantics ​

  • Most primitives are infallible at API level; contention manifests as wait/lock behavior.
  • Fallback Arc enforces overflow checks on reference counters and may panic on overflow.

Extension points ​

  • Additional wrappers/adapters can be added to normalize synchronization semantics across targets.
  • Arc fallback can evolve independently while preserving exported Arc/Weak contract.

Contract vs implementation ​

  • Contract: one consistent synchronization import surface across platforms.
  • Current implementation: direct embassy_sync re-export plus conditional Arc fallback implementation.

Limitations and trade-offs ​

  • Behavior still inherits cooperative runtime assumptions from executor usage patterns.
  • Fallback Arc prioritizes portability over lock-free performance.

Choosing an abstraction ​

The crate is #![no_std], re-exports embassy_sync and provides synchronization::Arc/Weak. Choose primitives according to the execution context:

  • mutex::Mutex and rwlock::RwLock support async tasks sharing state; avoid holding a guard across lengthy operations that block protocol progress.
  • blocking_mutex and its CriticalSectionRawMutex are for short synchronous critical sections. A critical section does not automatically mean independent threads or interrupts may block arbitrarily.
  • signal, channels and once_lock provide task notification and one-time publication, used by Task, VFS and other services.
  • Arc/Weak manage heap-backed shared ownership, not scheduling or resource cleanup policy.

Portability and memory model ​

When target_has_atomic = "ptr", Arc and Weak come directly from alloc::sync. Otherwise Xila exports a custom arc_lock implementation using CriticalSectionRawMutex and interior-mutable reference counts. Both expose Arc::new, clone, downgrade, Weak::upgrade, strong_count, weak_count, get_mut and ptr_eq style APIs.

rust
use synchronization::Arc;
let data = Arc::new(42);
let another = Arc::clone(&data);
assert!(Arc::ptr_eq(&data, &another));

The no-atomic fallback allocates an ArcInner<T> on the heap. Its reference counters are checked for overflow (which panics) and protected by critical-section locks, so cloning/dropping has a different performance cost from atomics. get_mut only returns mutable access when the allocation has a unique strong owner with no conflicting weak ownership.

Arc only provides shared ownership. Access to mutable T must still use synchronization appropriate for the target, and dropping the last reference is not a substitute for explicitly closing a VFS file, stopping a graphics animation, or tearing down a device context.

Source and API references ​

References ​