Chapter 3: Concurrency
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
Chapter 3: Concurrency explores the nature of time in computational systems and the challenges of managing shared state across concurrent processes. In this implementation, the project maps Scheme's concepts of serializers and mutexes to Rust's "fearless concurrency" model, leveraging the ownership system and type-safe synchronization primitives to provide compile-time guarantees against data races.
Sources: rust-examples/chapter3/src/lib.rs:1-12, rust-examples/chapter3/src/section_3_4.rs:1-10
This module specifically addresses modularity, objects, and state by contrasting Scheme's manual serialization with Rust's automated memory safety. It covers the environment model, interior mutability, and the transition from sequential execution to parallel processes using threads and atomic operations.
Sources: rust-examples/chapter3/src/lib.rs:3-9, rust-examples/chapter3/src/section_3_4.rs:55-66
Concurrency Architecture and Shared State
The concurrency model relies on Arc (Atomic Reference Counting) and Mutex (Mutual Exclusion) to manage shared data. Arc allows multiple threads to own a reference to the same memory location on the heap, while Mutex ensures that only one thread can access the underlying data at any given time.
The diagram illustrates how Arc handles shared ownership and Mutex provides exclusive access via a MutexGuard.
Sources: rust-examples/chapter3/src/section_3_4.rs:11-39
Shared State Components
| Component | Rust Type | Description |
|---|---|---|
| Shared Pointer | Arc<T> | Thread-safe reference counting for heap-allocated data. |
| Exclusive Lock | Mutex<T> | Provides mutual exclusion; required for mutating shared state. |
| Atomic Values | AtomicBool, AtomicI64 | Hardware-level atomic operations using CAS (Compare-And-Swap). |
| Lock Guard | MutexGuard<T> | RAII-based handle that unlocks data automatically when dropped. |
| Sources: rust-examples/chapter3/src/section_3_4.rs:41-45, rust-examples/chapter3/src/section_3_4.rs:336-340, rust-examples/chapter3/Cargo.toml:10-14 |
Mechanisms for Controlling Concurrency
To prevent race conditions, such as the "lost update" problem in bank account withdrawals, the system enforces serialization. In Rust, this is achieved by wrapping data in a Mutex. A thread must acquire a lock before it can read or write the balance, effectively serializing concurrent requests.
Sources: rust-examples/chapter3/src/section_3_4.rs:90-112
Serialization and Deadlock Prevention
SICP's make-serializer is implemented using a Serializer wrapper around Arc<Mutex<T>>. While serialization prevents data races, it introduces the risk of deadlocks when multiple resources are involved.
The sequence diagram shows the ordered locking strategy where threads always acquire locks in ascending order of resource IDs to prevent circular wait conditions.
Sources: rust-examples/chapter3/src/section_3_4.rs:242-263, rust-examples/chapter3/src/section_3_4.rs:268-290
Low-Level Synchronization: Atomics
For performance-critical sections or simple flags, the project utilizes hardware-level atomics. The SimpleMutex implementation demonstrates a busy-wait (spin) lock using AtomicBool and the compare_exchange operation.
SimpleMutex Implementation
- acquire(): Repeatedly attempts to set the
AtomicBoolfromfalsetotrue. - release(): Atomically stores
falsein theAtomicBoolusingOrdering::Release. - test_and_set(): An atomic operation that returns the previous state while setting the new state.
Sources: rust-examples/chapter3/src/section_3_4.rs:356-385
Stream-Based Concurrency
While Section 3.4 focuses on shared state, the project also implements lazy sequences (Streams) in Section 3.5. Rust's Iterator trait serves as the equivalent to Scheme's delay and force. Streams allow for functional concurrency where state is managed through lazy evaluation rather than explicit mutation.
Sources: rust-examples/chapter3/STREAMS_GUIDE.md:1-15, rust-examples/chapter3/SECTION_3_3_SUMMARY.md:65-72
| Scheme Concept | Rust Equivalent | Characteristics |
|---|---|---|
delay | Lazy Iterator | Computed only when .next() is called. |
force | .next() / .collect() | Triggers evaluation of the delayed process. |
cons-stream | std::iter::once().chain() | Constructing lazy sequences. |
| Sources: rust-examples/chapter3/STREAMS_GUIDE.md:10-25 |
Conclusion
Chapter 3: Concurrency demonstrates that while the fundamental problems of concurrent time—race conditions and deadlocks—remain constant across languages, Rust's type system provides a more robust framework for addressing them than Scheme. By utilizing Arc, Mutex, and Atomics, the implementation ensures memory safety and data race prevention at compile time, while ordered locking and functional streams provide higher-level patterns for managing complex concurrent systems.
Sources: rust-examples/chapter3/src/section_3_4.rs:5-15, rust-examples/chapter3/STREAMS_GUIDE.md:90-100