folly::Baton — one-shot wait 동기화
#한 줄 요약
folly::Baton<>은 한 번 post → 한 번 wait의 one-shot 동기화 primitive. mutex와 condition_variable 없이 4-byte 상태로 thread간 signal을 전달한다. Future/Promise의 핵심 building block.
#동기
다음 같은 패턴은 condition_variable로 흔히 작성한다.
std::mutex mu;std::condition_variable cv;bool ready = false;
// thread Avoid wait_for_ready() { std::unique_lock g(mu); cv.wait(g, []{ return ready; });}
// thread Bvoid signal_ready() { { std::lock_guard g(mu); ready = true; } cv.notify_all();}mutex + bool + cv 세 개를 들고 다녀야 한다. 의도는 signal 한 번인데 코드는 그것보다 일반적인 도구.
Baton은 이 패턴 전용. 한 번 발사하는 사격 신호.
folly::Baton<> b;
// thread Ab.wait(); // post 될 때까지 sleep
// thread Bb.post(); // wake#API & 사용법
#include <folly/synchronization/Baton.h>
// 1. 기본 — single post, multiple-wait OK (n번째 wait도 즉시 통과)folly::Baton<> b;b.wait(); // post 전까지 blockb.post(); // wake all (보통 하나)
// 2. 타임아웃if (b.try_wait_for(std::chrono::seconds(1))) { // posted} else { // timeout}
// 3. try_wait — non-blockingif (b.try_wait()) { /* 이미 posted */ }
// 4. 재사용 — resetb.reset();// 그 후 wait/post 다시 가능
// 5. blocking mode template paramfolly::Baton<true> spin_baton; // spin 한 후 futex (기본)folly::Baton<false> futex_only; // spin 없이 바로 sleepfolly::Baton은 default가 spin-then-futex hybrid. 짧은 wait는 spin, 길면 sleep.
#내부 구현
// 약식 — folly/synchronization/Baton.htemplate <bool MayBlock = true>class Baton { std::atomic<uint32_t> state_; // state values: // INIT = 0 // WAITING = 1 // EARLY_DELIVERY = 2 (post가 wait 전에 옴) // TIMED_OUT = 3};4-byte. 추가 mutex/cv 없음.
#post
// 약식void post() { uint32_t prev = state_.exchange(EARLY_DELIVERY, std::memory_order_release); if (prev == WAITING) { // 누가 sleep 중 futex_wake_one(&state_); }}CAS 없이 exchange 한 번. WAITING이면 wake.
#wait
// 약식void wait() { // 1. spin 시도 for (int spin = 0; spin < kMaxSpins; ++spin) { uint32_t s = state_.load(std::memory_order_acquire); if (s == EARLY_DELIVERY) return; cpu_pause(); } // 2. WAITING으로 표시 후 futex sleep uint32_t expected = INIT; if (state_.compare_exchange_strong(expected, WAITING, std::memory_order_acquire)) { while (state_.load() == WAITING) { futex_wait(&state_, WAITING); } } // 그 외 EARLY_DELIVERY 였음 — 즉시 반환}spin → CAS WAITING → futex_wait. linux의 futex(FUTEX_WAIT)가 state가 더 이상 WAITING이 아니면 즉시 반환.
#왜 condition_variable보다 빠른가
cv는 보통 mutex.lock() → predicate 확인 → wait → predicate 확인 사이클. Baton은:
Baton: state load (acquire) → spin → CAS → futex_wait state exchange (release) → futex_wake
cv: mutex.lock → predicate → mutex.unlock → futex_wait mutex.lock → state change → mutex.unlock → futex_wake_all → cv_mu.lockcv는 mutex 두 개 (사용자 mutex + cv 내부)를 잡았다 놓는다. Baton은 0개. 따라서 cv보다 3-5배 빠른 시그널.
#release/acquire가 보장하는 것
Baton의 state.store(POSTED, release) + state.load(acquire)가 mutex 없이 publish-subscribe를 안전하게 만드는 이유.
release store 이전의 모든 쓰기가 acquire load 이후의 모든 읽기에 visible. poster가 채워 놓은 데이터를 waiter가 그대로 읽을 수 있다. 이게 lock-free 자료구조의 토대다.
#사용 사례
#1. Future/Promise 내부
// 약식 — folly::Future가 내부적으로struct State { folly::Baton<> baton; std::optional<T> value;};
void Promise::setValue(T v) { state_->value = std::move(v); state_->baton.post();}
T Future::get() { state_->baton.wait(); return std::move(*state_->value);}Future가 wait/post의 깊은 곳에 Baton.
#2. Worker thread startup signal
folly::Baton<> ready;std::thread worker([&]() { Setup(); ready.post(); // ready RunLoop();});
ready.wait(); // setup 끝까지 대기StartUsingWorker();#3. Test에서 lifecycle 동기
TEST(MyTest, AsyncCallback) { folly::Baton<> done; service.DoAsync([&]() { done.post(); }); ASSERT_TRUE(done.try_wait_for(std::chrono::seconds(5)));}cv보다 코드가 짧다.
#std/abseil 비교
// std — cv 풀세트std::condition_variable cv;std::mutex mu;bool flag = false;void wait() { std::unique_lock g(mu); cv.wait(g, []{ return flag; }); }
// abseil — Notificationabsl::Notification n;n.WaitForNotification();n.Notify(); // 한 번만 가능
// follyfolly::Baton<> b;b.wait();b.post(); // 여러 번 호출 가능, 추가 post는 no-op| 항목 | std::cv + mutex | absl::Notification | folly::Baton |
|---|---|---|---|
| Use case | general | one-shot | one-shot |
| sizeof | 80+ byte | ~16 byte | 4 byte |
| Spurious wakeup | 가능 | 없음 | 없음 |
| Reset | N/A | X | O |
| Multiple post | N/A | 한 번만 (assert) | 무해 |
abseil Notification이 가장 가깝다. 둘 다 한 번 signal 의미. folly가 약간 더 작고 reset 가능.
#코드 리뷰 포인트
// Bad — Baton을 반복 사용 (reset 없이)folly::Baton<> b;for (int i = 0; i < N; ++i) { worker.Submit([&]{ ... b.post(); }); b.wait(); // 두 번째부터는 EARLY_DELIVERY 라 즉시 통과 → race!}
// Good — 매번 resetfor (int i = 0; i < N; ++i) { b.reset(); worker.Submit([&]{ ... b.post(); }); b.wait();}post는 idempotent하나 wait는 state를 한번만 본다. 반복은 reset 필수.
// Good — timeout으로 deadlock 검출if (!b.try_wait_for(std::chrono::seconds(30))) { LOG(ERROR) << "deadlock?";}production에서 영원 wait는 위험. 합리적 timeout.
// 주의 — Baton이 stack object일 때 lifetime{ folly::Baton<> b; worker.Submit([&b]{ b.post(); }); b.wait();} // worker가 아직 b를 본다면? — wait 후 destruct이므로 OKwait 반환 = post가 이미 일어남. wait return 후 baton destruct 안전.
#안티패턴
- Baton을 cv 대체로 일반 signal에 사용: Baton은 한 번 의미. 반복 신호는 cv 또는 semaphore.
- post 후 reset 없이 wait 재호출: 즉시 통과 → 동기화 실패. reset 필수.
- wait 안에서 다른 lock 잡기: Baton wait는 spin → futex로 lock-free path. 다른 lock 잡으면 inversion 위험. wait는 단순히 signal 대기로만.
#정리
folly::Baton<>은 4-byte one-shot signal primitive.- spin → futex hybrid로 짧은 wait는 spin, 길면 sleep.
- cv보다 3-5배 빠름. Future/Promise 내부의 building block.
try_wait_for로 timeout,reset()으로 재사용.- 반복 신호는 cv 또는 semaphore.
#다음 편
RWSpinLock은 매우 짧은 critical section 용 spin-only RW lock. SharedMutex보다 가벼우나 longer wait에 부적합.
#관련 항목
- Part 9-02: SharedMutex — 일반 RW lock
- Part 2-02: Promise / makeFuture — Baton의 main client
- Part 9-04: RWSpinLock — spin-only variant
- 원문 — folly/synchronization/Baton.h
Folly Code Review · 43 of 89
- 1 Folly Code Review — Meta의 production-grade C++ 라이브러리 코드 분석
- 2 Folly 개요 — Meta가 production에서 검증한 utility 모음 분석
- 3 Folly vs Abseil 철학 비교 — performance-first vs std-compatible
- 4 Folly 빌드와 fbcode 환경 — monorepo의 그림자
- 5 Folly API stability 정책 — 어떤 보장도 없다는 솔직함
- 6 Folly production validation 문화 — peta-scale에서 단련된 코드
- 7 folly::Future 분석 — std::future의 한계를 넘는 composable async
- 8 folly::Promise·makeFuture — Future를 만드는 두 길
- 9 folly::SemiFuture vs Future — executor binding의 명시화
- 10 folly::Future thenValue·thenError·thenTry — continuation 체인 분석
- 11 folly::collect·collectAll·collectAny — fan-in 패턴 분석
- 12 folly::Future retry·window·via — 제어 흐름 조합자
- 13 folly::fibers 분석 — M:N stackful coroutine
- 14 folly::InlineExecutor — 호출자 thread에서 즉시 실행
- 15 folly::CPUThreadPoolExecutor — CPU-bound 작업의 표준 thread pool
- 16 folly::IOThreadPoolExecutor — libevent 기반 I/O pool
- 17 folly::ManualExecutor — 결정적 테스트를 위한 수동 진행
- 18 folly::EventBase 분석 — libevent 이벤트 루프의 핵심
- 19 folly::IOBuf 분석 — zero-copy buffer chain의 기본 단위
- 20 folly::IOBufQueue — chain의 push/pull 추상화
- 21 folly::io::Cursor·RWCursor — chain 위의 stream
- 22 folly Zero-copy 패턴 — IOBuf로 ScatterGather I/O 표현
- 23 folly::IOBuf shared semantics — clone·unshare·takeOwnership
- 24 folly::FBString 분석 — SSO + COW 구현
- 25 folly의 fmt::format 통합 — 모던 포맷팅 채택
- 26 folly::StringPiece — string_view 호환 분석
- 27 folly Join·Split utilities — 문자열 분해와 결합
- 28 folly::to·tryTo — text↔num 변환 분석
- 29 folly Conv Customization — 사용자 타입 지원
- 30 folly Conv 성능 비교 — sprintf·stringstream 대비
- 31 folly::F14ValueMap vs std::unordered_map
- 32 folly::F14NodeMap — stable pointer가 필요할 때
- 33 folly::F14VectorMap — cache-friendly iteration
- 34 folly::F14FastMap — auto-select 동작
- 35 folly F14 internals — SIMD probing 메커니즘
- 36 folly::small_vector — inline storage 분석
- 37 folly::FixedString — compile-time string
- 38 folly::AtomicHashMap — lock-free read 분석
- 39 folly::ConcurrentHashMap — sharded 동시 해시 맵
- 40 folly::EvictingCacheMap — LRU 구현 분석
- 41 folly::Synchronized — lock wrapper 패턴
- 42 folly::SharedMutex 분석
- 43 folly::Baton — one-shot wait 동기화
- 44 folly::RWSpinLock 분석
- 45 folly::PicoSpinLock — 1-byte spinlock
- 46 folly::ProducerConsumerQueue — SPSC 큐 분석
- 47 folly::MPMCQueue — multi-producer multi-consumer
- 48 folly::UnboundedQueue — 동적 크기 lock-free
- 49 folly::fibers::Channel — Go-like channel
- 50 folly::dynamic — JSON-like dynamic type 분석
- 51 folly JSON conversion — toJson·parseJson
- 52 folly dynamic ↔ struct — manual marshaling
- 53 folly dynamic Visitor pattern — type별 분기
- 54 folly::Singleton vs Meyers/static — 왜 Folly의 Singleton인가
- 55 folly::SingletonVault 분석 — 등록·소멸·의존성
- 56 folly::Singleton try_get·try_get_fast — TLS-cached 접근
- 57 folly::ExceptionWrapper — type-erased exception holder
- 58 folly::ScopeGuard·SCOPE_EXIT — RAII cleanup
- 59 folly::Optional vs std::optional
- 60 folly::Function vs std::function
- 61 folly::Lazy — 지연 초기화 wrapper
- 62 folly Meta 스타일 code review 패턴
- 63 folly anti-patterns — 잘못 쓰면 std보다 느림
- 64 folly vs std 선택 기준 분석
- 65 folly::coro 개요 — production C++20 코루틴 어댑터
- 66 folly::coro::Task — lazy single-shot 코루틴
- 67 folly::coro::AsyncGenerator — 비동기 스트림
- 68 folly coro blockingWait·collectAll — 동기 경계와 fan-in
- 69 folly::coro::Baton·Mutex — 코루틴-aware 동기화
- 70 folly::Expected — 결과 또는 오류
- 71 folly::Try — Future 결과 wrapper
- 72 folly::Try vs Expected 선택 기준
- 73 folly::Range — 일반 iterator pair
- 74 folly::Uri — URL 파서
- 75 folly Fingerprint64·128 — 분산 hash
- 76 folly SpookyHashV2 — fast non-crypto hash
- 77 folly::Init — main() 부트스트랩
- 78 folly::Indestructible — global lifetime 패턴
- 79 folly::MicroLock — 1-byte 락
- 80 folly::MicroSpinLock — 가장 좁은 spin lock
- 81 folly::format — legacy formatter 분석
- 82 folly::demangle — typeid 디망글링
- 83 folly::DynamicConverter — dynamic ↔ struct
- 84 folly::RecordIO — append-only 로그 파일 포맷
- 85 folly::io::Compression — zstd·lz4·snappy wrapper
- 86 folly::AsyncIO — io_uring·Linux AIO
- 87 folly::CancellationToken — 코루틴·Future 취소 전파
- 88 folly::observer — hot config의 atomic refresh
- 89 fbcode 패턴 모음 — folly 사용의 실전
관련 글
folly::coro::Baton·Mutex — 코루틴-aware 동기화
coro::Baton과 coro::Mutex — thread를 block하지 않고 코루틴만 suspend하는 동기화 프리미티브.
같은 시리즈에서 이어 읽기
folly::PicoSpinLock — 1-byte spinlock
PicoSpinLock — integer type의 한 bit을 lock으로 사용. 객체 안에 lock을 끼워 넣어 메모리 절약.
같은 시리즈에서 이어 읽기
folly::RWSpinLock 분석
folly::RWSpinLock — spin-only reader-writer lock, 매우 짧은 critical section에 SharedMutex보다 빠르다.
같은 시리즈에서 이어 읽기
이 글을 참조하는 글 (5)
- folly::coro::Baton·Mutex — 코루틴-aware 동기화 — Folly Code Review
- folly coro blockingWait·collectAll — 동기 경계와 fan-in — Folly Code Review
- folly::fibers::Channel — Go-like channel — Folly Code Review
- folly::UnboundedQueue — 동적 크기 lock-free — Folly Code Review
- folly::MPMCQueue — multi-producer multi-consumer — Folly Code Review