Synchronization
The synchronization crate standardizes synchronization primitives used across Core.
Role
- Re-exports
embassy_syncprimitives through one crate-local entry point. - Provides portable
Arc/Weaksupport, 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": usesalloc::sync::Arc/Weak. - Without pointer atomics: uses
arc/arc_lock.rsmutex-backed Arc implementation.
- With
Runtime interaction
- Modules and executables import mutex/rwlock/once/channel primitives from this crate.
- Fallback Arc implementation uses
CriticalSectionRawMutex+RefCellcounters 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/Weakcontract.
Contract vs implementation
- Contract: one consistent synchronization import surface across platforms.
- Current implementation: direct
embassy_syncre-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::Mutexandrwlock::RwLocksupport async tasks sharing state; avoid holding a guard across lengthy operations that block protocol progress.blocking_mutexand itsCriticalSectionRawMutexare for short synchronous critical sections. A critical section does not automatically mean independent threads or interrupts may block arbitrarily.signal, channels andonce_lockprovide task notification and one-time publication, used by Task, VFS and other services.Arc/Weakmanage 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.
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
libraries/synchronization/src/lib.rs— re-export surface.libraries/synchronization/src/arc/mod.rs— target gate.libraries/synchronization/src/arc/arc_lock.rs— reference-counted fallback.synchronizationcrate — Rustdoc of re-exported primitives.