496303471d
---ci--- project: atelier phase: 7 milestone: v0.1 status: complete phase_role: final milestone_complete: true requirements: covered: [ATELIER-01, ATELIER-02, ATELIER-03, ATELIER-04, ATELIER-05, ATELIER-06, ATELIER-07, ATELIER-08, ATELIER-09, ATELIER-10, ATELIER-11, ATELIER-12, ATELIER-13, ATELIER-14, ATELIER-15, ATELIER-16, ATELIER-17, ATELIER-18, ATELIER-19, ATELIER-20, ATELIER-21, ATELIER-22, ATELIER-23, ATELIER-24, ATELIER-25, ATELIER-26, ATELIER-27, ATELIER-28, ATELIER-29, ATELIER-30, ATELIER-31, ATELIER-32, ATELIER-33, ATELIER-34, ATELIER-35] partial: [] ship: milestone: v0.1 type: NFR tag: v0.0.7 merge: milestone/v0.1-atelier -> main release: https://git.cloudinit.dev/cloudinit-bot/atelier/releases/tag/v0.0.7 ---/ci--- Milestone v0.1 — Initial Framework (NFR, complete). 8 core principles (C1-C8), 11 domains, 110 domain principles, 27 derived docs, 4 good + 3 bad examples, 4 language docs, full matrix, 3 review docs. All 35 requirements covered. 7 patches (v0.0.0 pre-execution through v0.0.7 final). v0.0.7 IS the v0.1.0 milestone release.
2.7 KiB
2.7 KiB
Concurrency Patterns — Derived Rules
Derives from
domains/concurrency/first-principles.md. Common concurrency patterns and when to use them.
Pattern 1: Message Passing (P5 Lock Minimization)
- Threads/goroutines communicate via channels/queues, not shared memory.
- "Don't communicate by sharing memory; share memory by communicating." (Go proverb)
- Use when the data flows naturally in one direction. Avoids locks entirely.
Pattern 2: Read-Write Lock (P5 Lock Minimization)
- Multiple readers, one writer. A
RwLockallows concurrent reads, exclusive writes. - Use when reads vastly outnumber writes (e.g., a config cache).
- Avoid when writes are frequent — the lock degrades to a mutex.
Pattern 3: Actor Model (P2 Single Responsibility)
- Each actor owns its state. Actors communicate via messages. No shared state.
- Use for isolated, long-lived workers (e.g., a session handler, a chat room).
- Erlang/Akka/Pony are built on this. Implementable in any language with channels.
Pattern 4: Immutable Data Structures (P1 Immutability by Default)
- Data is never mutated; a "change" creates a new value. Old values are safe to share.
- Use in functional languages (Haskell, Clojure) or via persistent data structures (Immer.js).
- Eliminates entire classes of races. The trade-off is allocation cost.
Pattern 5: Bounded Queue with Backpressure (P9 Bounded Queues)
- A queue with a max size. When full, the producer is blocked or signaled.
- Use to bound memory and propagate slowness from consumer to producer.
- An unbounded queue hides a slow consumer until OOM. Always bound.
Pattern 6: Timeout on Every Block (P8 Timeout Discipline)
- Every blocking call (lock acquire, queue send, HTTP request) has a timeout.
- Use a timeout, not a forever-block. Forever is not a duration.
- On timeout: cancel, retry, or fail. Do not hang.
Pattern 7: Cancellation Propagation (P7 Cancellation Support)
- A cancellation signal propagates to all spawned work. Cancel the parent, the children stop.
- Use context (
context.Contextin Go,AbortControllerin JS,CancellationTokenin C#). - Cancellation is fast and complete. No orphaned goroutines/threads.
Pattern 8: Lock-Free Where Possible (P5)
- Atomic operations (compare-and-swap) for simple state. No lock.
- Use for counters, flags, simple pointers.
- Avoid for complex state — lock-free code is subtle and easy to get wrong.
What Violates Concurrency Patterns
| Violation | Pattern |
|---|---|
| Shared mutable state with no lock | (race, P1) |
| Unbounded queue | P5 (OOM) |
channel.send() with no timeout |
P6 (hang) |
| Spawned goroutine with no cancellation | P7 (orphan) |
| A mutex held across an I/O call | P3 (lock scope) |
sync.Mutex for a counter |
P8 (use atomic) |