[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,363 @@
|
||||
---
|
||||
id: cs-lock-free-patterns
|
||||
title: Lock-Free Patterns — atomic / CAS / queue
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [cs, concurrency, vibe-coding]
|
||||
tech_stack: { language: "C++ / Rust", applicable_to: ["CS", "Backend"] }
|
||||
applied_in: []
|
||||
aliases: [lock-free, wait-free, CAS, compare and swap, atomic, MPSC, MPMC, ABA problem]
|
||||
---
|
||||
|
||||
# Lock-Free Patterns
|
||||
|
||||
> Lock = 느린, deadlock 위험. **Atomic + CAS = lock-free**. Wait-free 가 강함.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- Atomic: 원자 operation.
|
||||
- CAS: Compare-And-Swap.
|
||||
- Lock-free: 1+ thread 가 progress.
|
||||
- Wait-free: 매 thread 가 progress.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### Atomic counter
|
||||
```rust
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
let counter = AtomicU64::new(0);
|
||||
|
||||
// Thread-safe
|
||||
counter.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
let val = counter.load(Ordering::SeqCst);
|
||||
```
|
||||
|
||||
### CAS (Compare-And-Swap)
|
||||
```rust
|
||||
let atomic = AtomicU64::new(0);
|
||||
|
||||
loop {
|
||||
let old = atomic.load(Ordering::SeqCst);
|
||||
let new = old + 1;
|
||||
if atomic.compare_exchange(old, new, Ordering::SeqCst, Ordering::SeqCst).is_ok() {
|
||||
break;
|
||||
}
|
||||
// 다른 thread 가 변경. Retry.
|
||||
}
|
||||
```
|
||||
|
||||
→ "if 같으면 set". Atomic.
|
||||
|
||||
### Memory order
|
||||
```
|
||||
Relaxed: 매 thread 의 자체 ordering 만.
|
||||
Acquire / Release: 인접 op 의 ordering.
|
||||
SeqCst: total ordering (제일 안전, 느린).
|
||||
```
|
||||
|
||||
### TS / JavaScript SharedArrayBuffer
|
||||
```ts
|
||||
const sab = new SharedArrayBuffer(8);
|
||||
const view = new Int32Array(sab);
|
||||
|
||||
// Atomic
|
||||
Atomics.add(view, 0, 1);
|
||||
Atomics.compareExchange(view, 0, oldVal, newVal);
|
||||
Atomics.wait(view, 0, 0);
|
||||
Atomics.notify(view, 0);
|
||||
```
|
||||
|
||||
→ Worker 간 shared.
|
||||
|
||||
### Lock-free queue (Michael-Scott)
|
||||
```rust
|
||||
struct Node<T> {
|
||||
value: T,
|
||||
next: AtomicPtr<Node<T>>,
|
||||
}
|
||||
|
||||
struct Queue<T> {
|
||||
head: AtomicPtr<Node<T>>,
|
||||
tail: AtomicPtr<Node<T>>,
|
||||
}
|
||||
|
||||
impl<T> Queue<T> {
|
||||
fn enqueue(&self, value: T) {
|
||||
let new_node = Box::into_raw(Box::new(Node { value, next: AtomicPtr::new(null_mut()) }));
|
||||
|
||||
loop {
|
||||
let tail = self.tail.load(SeqCst);
|
||||
let next = unsafe { (*tail).next.load(SeqCst) };
|
||||
|
||||
if next.is_null() {
|
||||
if unsafe { (*tail).next.compare_exchange(null_mut(), new_node, SeqCst, SeqCst) }.is_ok() {
|
||||
self.tail.compare_exchange(tail, new_node, SeqCst, SeqCst).ok();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
self.tail.compare_exchange(tail, next, SeqCst, SeqCst).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
→ Multi-producer multi-consumer.
|
||||
|
||||
### MPSC (single consumer)
|
||||
```rust
|
||||
// Crossbeam (Rust)
|
||||
use crossbeam::channel::unbounded;
|
||||
|
||||
let (tx, rx) = unbounded();
|
||||
|
||||
// Multiple producer
|
||||
thread::spawn(move || tx.send(1).unwrap());
|
||||
|
||||
// Single consumer
|
||||
let v = rx.recv().unwrap();
|
||||
```
|
||||
|
||||
→ Lock-free queue.
|
||||
|
||||
### Crossbeam (Rust)
|
||||
```
|
||||
- Unbounded / bounded channel.
|
||||
- ArrayQueue / SegQueue.
|
||||
- AtomicCell.
|
||||
- Epoch-based GC.
|
||||
```
|
||||
|
||||
### ABA problem
|
||||
```
|
||||
Thread A:
|
||||
- Load X = 1.
|
||||
Thread B:
|
||||
- Set X = 2.
|
||||
- Set X = 1.
|
||||
Thread A:
|
||||
- CAS X (expect 1) = success.
|
||||
- → wrong (X 가 변경 됐는데 안 알아).
|
||||
```
|
||||
|
||||
```rust
|
||||
// Solution: tagged pointer (counter + value).
|
||||
let old: (u64, T) = atomic.load(); // counter + value
|
||||
let new = (old.0 + 1, new_value);
|
||||
atomic.compare_exchange(old, new, ...);
|
||||
```
|
||||
|
||||
→ Counter 가 increment. 같은 value 가 다른 counter.
|
||||
|
||||
### Memory reclamation
|
||||
```
|
||||
Lock-free 의 함정:
|
||||
- Free 한 node 의 다른 thread 가 still 참조.
|
||||
|
||||
Solution:
|
||||
- Hazard pointer.
|
||||
- Epoch-based GC (crossbeam).
|
||||
- RCU (Read-Copy-Update).
|
||||
```
|
||||
|
||||
### Lock-free hash map
|
||||
```
|
||||
DashMap (Rust):
|
||||
- Sharded (16 partition).
|
||||
- 매 partition 의 Lock-free or fine lock.
|
||||
- 큰 throughput.
|
||||
```
|
||||
|
||||
```rust
|
||||
use dashmap::DashMap;
|
||||
let map: DashMap<String, u32> = DashMap::new();
|
||||
|
||||
map.insert("key".into(), 42);
|
||||
let v = map.get("key");
|
||||
```
|
||||
|
||||
### Atomic reference counting (Rust Arc)
|
||||
```rust
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
|
||||
let arc = Arc::new(MyData);
|
||||
let arc2 = arc.clone(); // count++
|
||||
|
||||
// Drop = count--. 0 = free.
|
||||
```
|
||||
|
||||
### Java AtomicInteger / AtomicReference
|
||||
```java
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
counter.incrementAndGet();
|
||||
|
||||
AtomicReference<Node> head = new AtomicReference<>();
|
||||
head.compareAndSet(expected, newValue);
|
||||
```
|
||||
|
||||
### Go (sync/atomic)
|
||||
```go
|
||||
import "sync/atomic"
|
||||
|
||||
var counter int64
|
||||
atomic.AddInt64(&counter, 1)
|
||||
|
||||
old := atomic.LoadInt64(&counter)
|
||||
atomic.CompareAndSwapInt64(&counter, old, old + 1)
|
||||
```
|
||||
|
||||
### Wait-free
|
||||
```
|
||||
Lock-free: 1+ thread 가 progress.
|
||||
Wait-free: 매 thread 가 progress.
|
||||
|
||||
Wait-free = stronger. 보통 lock-free 가 충분.
|
||||
```
|
||||
|
||||
### Software Transactional Memory (STM)
|
||||
```haskell
|
||||
-- Haskell
|
||||
atomically $ do
|
||||
x <- readTVar var
|
||||
writeTVar var (x + 1)
|
||||
```
|
||||
|
||||
```clojure
|
||||
;; Clojure
|
||||
(dosync
|
||||
(alter counter inc))
|
||||
```
|
||||
|
||||
→ Optimistic concurrency. Conflict 시 retry.
|
||||
|
||||
### Use case
|
||||
```
|
||||
- Counter (statistics).
|
||||
- Single producer queue (logging).
|
||||
- Lock-free hash (cache).
|
||||
- Reference count (shared ownership).
|
||||
- LSM tree memtable.
|
||||
- WAL (write-ahead log).
|
||||
```
|
||||
|
||||
### vs Mutex
|
||||
```
|
||||
Mutex:
|
||||
- Simple.
|
||||
- Block 가능 (sleep).
|
||||
- Deadlock 위험.
|
||||
|
||||
Lock-free:
|
||||
- Complex.
|
||||
- Non-blocking.
|
||||
- 더 빠름 (high contention).
|
||||
- 더 어려움.
|
||||
|
||||
→ 작은 critical section = mutex.
|
||||
큰 contention = lock-free.
|
||||
```
|
||||
|
||||
### Lock contention 측정
|
||||
```
|
||||
perf record / perf report.
|
||||
- Hot mutex 발견.
|
||||
- Lock-free 의 candidate.
|
||||
|
||||
→ Profile 가 답.
|
||||
```
|
||||
|
||||
### Lock-free의 함정
|
||||
```
|
||||
- ABA problem: tagged pointer.
|
||||
- Memory reclamation: hazard pointer / epoch.
|
||||
- Memory ordering: SeqCst 가 안전 default.
|
||||
- Algorithm 가 매우 어려움.
|
||||
- Test 어려움 (race rare).
|
||||
|
||||
→ Library (Crossbeam / Folly) 사용. 자체 X.
|
||||
```
|
||||
|
||||
### Folly (Facebook C++)
|
||||
```cpp
|
||||
#include <folly/MPMCQueue.h>
|
||||
folly::MPMCQueue<int> queue(1024);
|
||||
queue.write(42);
|
||||
int x;
|
||||
queue.read(x);
|
||||
```
|
||||
|
||||
### LMAX Disruptor (Java)
|
||||
```java
|
||||
RingBuffer<Event> ringBuffer = ...;
|
||||
|
||||
long sequence = ringBuffer.next();
|
||||
Event event = ringBuffer.get(sequence);
|
||||
event.setValue(42);
|
||||
ringBuffer.publish(sequence);
|
||||
```
|
||||
|
||||
→ 매우 빠른 (millions / sec).
|
||||
|
||||
### Database 의 lock-free
|
||||
```
|
||||
MVCC: lock-free read.
|
||||
LSM tree: lock-free memtable insert.
|
||||
B-tree (some): lock-free split.
|
||||
|
||||
→ Modern DB 의 핵심.
|
||||
```
|
||||
|
||||
### When use?
|
||||
```
|
||||
✓ High contention.
|
||||
✓ Real-time (no block).
|
||||
✓ Embedded / kernel.
|
||||
|
||||
✗ Low contention (mutex 충분).
|
||||
✗ Logic 가 복잡.
|
||||
✗ Team 가 expert X.
|
||||
|
||||
→ Profile 후. Library 사용.
|
||||
```
|
||||
|
||||
### Real-world
|
||||
- **Crossbeam** (Rust).
|
||||
- **Folly** (C++ Facebook).
|
||||
- **DashMap** (Rust hash).
|
||||
- **LMAX Disruptor** (Java).
|
||||
- **Java concurrent.atomic**.
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 작업 | 추천 |
|
||||
|---|---|
|
||||
| Counter | Atomic |
|
||||
| Queue | Lock-free queue (Crossbeam) |
|
||||
| Hash map | DashMap / ConcurrentHashMap |
|
||||
| Ref count | Arc / shared_ptr |
|
||||
| 큰 throughput | LMAX Disruptor |
|
||||
| Database | MVCC + LSM |
|
||||
| Complex sync | STM (Haskell / Clojure) |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **자체 lock-free queue**: bug.
|
||||
- **No memory order**: race.
|
||||
- **No ABA fix**: corruption.
|
||||
- **No memory reclamation**: leak / use-after-free.
|
||||
- **Mutex on hot path**: contention.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- Library 사용 (자체 X).
|
||||
- ABA + memory reclamation 가 큰 함정.
|
||||
- Profile + measure.
|
||||
- MVCC + LSM 가 DB 의 lock-free.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[CS_LockFree_Atomic]]
|
||||
- [[CS_MVCC_Concurrency]]
|
||||
- [[CS_Skip_List_Persistent_Data]]
|
||||
Reference in New Issue
Block a user