absl::Mutex Conditional Critical Section — Await로 cv 없애기
#한 줄 요약
absl::Mutex::Await(Condition)은 condition variable + notify + predicate while loop이라는 3단 관용구를 한 줄로 압축한다. condition을 predicate 함수로 표현하면, mutex가 unlock될 때마다 abseil이 자동 재평가하므로 producer가 notify를 호출할 필요가 없다.
#표준 condition variable의 함정
std::mutex mu;std::condition_variable cv;std::deque<Task> q;
// producervoid Push(Task t) { { std::lock_guard<std::mutex> l(mu); q.push_back(std::move(t)); } cv.notify_one(); // ① 누락하면 consumer 영원히 대기}
// consumerTask Pop() { std::unique_lock<std::mutex> l(mu); cv.wait(l, [&] { return !q.empty(); }); // ② predicate while 의무 Task t = std::move(q.front()); q.pop_front(); return t;}함정 셋:
- ① notify 누락: producer가 wake 호출을 잊으면 consumer가 영원히 대기.
- ② predicate while loop: spurious wakeup 때문에 if가 아니라 while.
- 추가: notify_one vs notify_all 선택의 미묘함. 잘못 고르면 thundering herd 또는 lost wakeup.
#Mutex::Await의 모델
abseil의 모델은 다르다.
// APIclass Mutex { public: void Await(const Condition& cond); bool AwaitWithTimeout(const Condition& cond, absl::Duration timeout); bool AwaitWithDeadline(const Condition& cond, absl::Time deadline);
bool LockWhen(const Condition& cond); bool LockWhenWithTimeout(const Condition& cond, absl::Duration timeout);};Await(cond)는 cond가 true가 될 때까지 대기한다. 내부적으로 mutex가 unlock될 때마다 cond를 재평가한다. producer는 notify를 호출할 필요가 없다.
absl::Mutex mu;std::deque<Task> q;
void Push(Task t) { absl::MutexLock l(&mu); q.push_back(std::move(t)); // notify 없음}
Task Pop() { absl::MutexLock l(&mu); mu.Await(absl::Condition(+[](std::deque<Task>* q) { return !q->empty(); }, &q)); Task t = std::move(q.front()); q.pop_front(); return t;}코드가 단순해진다. 빠뜨릴 곳이 줄어든다.
#Condition 객체
absl::Condition은 predicate 함수를 캡슐화한다. 세 가지 형태가 있다.
// 1) 멤버 함수 + thisclass Server { bool IsReady() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); void Wait() { absl::MutexLock l(&mu_); mu_.Await(absl::Condition(this, &Server::IsReady)); }};
// 2) bool* 직접bool flag = false;mu.Await(absl::Condition(&flag)); // *flag == true가 될 때까지
// 3) 함수 포인터 + 인자mu.Await(absl::Condition(+[](Queue* q) { return !q->empty(); }, &queue));predicate는 순수해야 한다. mutex가 잡힌 상태에서 자주 호출되므로 부수효과나 다른 lock 획득은 금지.
#LockWhen — lock과 condition 동시
Lock + Await을 합친 단축형.
mu.LockWhen(absl::Condition(&ready));// ready가 true가 된 시점에 lock 보유DoWork();mu.Unlock();MutexLock은 Condition을 받는 생성자로 조건이 참이 될 때까지 대기 후 잠그는 RAII도 제공한다.
absl::MutexLock l(&mu, absl::Condition(&ready));DoWork();// 자동 unlock#Timeout / Deadline
absl::MutexLock l(&mu);if (mu.AwaitWithTimeout(cond, absl::Seconds(5))) { // 조건 만족} else { // timeout}absl::Duration, absl::Time 사용. 표준 std::chrono보다 표현력이 강하다 (Part 7에서 다룬다).
#내부 동작
Await은 wake-on-unlock 메커니즘이다. mutex가 unlock될 때마다 wait queue를 순회해 cond를 평가한다. true가 된 waiter는 wake.
// 의사 코드void Mutex::Unlock() { while (!wait_queue.empty()) { Waiter* w = wait_queue.front(); if (w->cond.Eval()) { // cond 평가 (mu lock 보유) w->Wake(); wait_queue.pop_front(); } break; // 다음 waiter는 다음 unlock 때 } ReleaseLock();}이 모델의 비용: producer가 wake할 의무는 없지만, unlock마다 cond 평가가 일어난다. cond가 비싸면 contention이 증가한다. 그래서 cond는 가벼워야 한다.
#std::condition_variable과의 비교
| 측면 | condition_variable | Mutex::Await |
|---|---|---|
| notify 호출 | producer 의무 | 자동 |
| spurious wakeup | while loop 필수 | 없음 |
| predicate 위치 | 호출 측 람다 | Condition 객체 |
| 코드 라인 | 많음 | 적음 |
| 성능 | wakeup 적게 | unlock마다 cond 평가 |
성능 트레이드오프가 있다. wake 패턴이 분명하고 predicate가 가벼우면 condition_variable이 약간 빠를 수 있다. 일반 코드는 Await의 명료함을 선택한다.
#코드 리뷰 포인트
1. cv.notify + cv.wait 패턴 → Await
// 회피{ std::lock_guard l(mu); ready = true;}cv.notify_one();// ...std::unique_lock l(mu);cv.wait(l, [&] { return ready; });// Good{ absl::MutexLock l(&mu); ready = true;}// ...absl::MutexLock l(&mu);mu.Await(absl::Condition(&ready));2. predicate는 멤버 함수로
람다 + 캡처는 thread-safety 어노테이션이 잘 안 잡힌다. 멤버 함수 + ABSL_EXCLUSIVE_LOCKS_REQUIRED가 컴파일러 검증을 통과한다.
class Server { bool ShouldStop() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_); void Run() { absl::MutexLock l(&mu_); while (!ShouldStop()) { mu_.Await(absl::Condition(this, &Server::HasWork)); DoWork(); } }};3. Condition 객체 재사용
같은 condition을 여러 번 쓰면 미리 만들어 둔다.
const absl::Condition has_work_(this, &Server::HasWork);// ...mu_.Await(has_work_);#안티패턴
predicate에서 lock 획득
// 회피mu.Await(absl::Condition(+[] { other_mu.Lock(); // deadlock 위험 // ...}));predicate는 mutex가 잡힌 상태에서 호출된다. 다른 lock 획득은 lock 순서 위반을 부른다.
무거운 predicate
// 회피 — DB 쿼리, network 호출 등mu.Await(absl::Condition(+[](DB* db) { return db->RowCount() > 0; }, &db));unlock마다 평가되므로 contention 시 hot spot. 가벼운 메모리 검사로 한정.
Await 안에서 외부 상태 변경
predicate는 판정만 한다. 안에서 state mutation은 race 위험.
#정리
Mutex::Await(Condition)은 wait + notify + while loop을 한 줄로.- producer는
notify의무가 없다. Condition은 멤버 함수 / bool* / 함수 포인터 + 인자로 표현.LockWhen/MutexLockWhen으로 lock-then-await 단축.- predicate는 가벼움, 순수, 다른 lock 없음.
#다음 편
Part 6-03 — Notification에서 한 번만 발생하는 signal에 특화된 primitive를 본다.
#관련 항목
Abseil Code Review · 36 of 79
- 1 Abseil Code Review — Google production-grade C++ 라이브러리 분석
- 2 Abseil 개요 — Google이 std를 보완한 이유
- 3 Abseil 설계 철학 — std 호환과 추가 기능의 균형
- 4 Abseil 빌드와 의존성 — Bazel vs CMake
- 5 Abseil LTS vs HEAD 릴리스 모델 분석
- 6 Abseil Versioning과 ABI 호환성 정책
- 7 Abseil 매크로 — ABSL_HAVE_*·ABSL_ATTRIBUTE_*
- 8 Abseil ABSL_PREDICT_TRUE/FALSE — branch hint
- 9 absl::LogSeverity — 로그 레벨 타입
- 10 Abseil type_traits — negation·conjunction·void_t
- 11 Abseil Conformance·Policy 분석
- 12 Abseil Memory utilities 분석
- 13 Abseil raw_logging — heap-free 로깅
- 14 Abseil thread_annotations — clang TSA 통합
- 15 absl::Status — exception-free error handling
- 16 absl::StatusOr<T> — 값 또는 에러
- 17 absl status_macros — ASSIGN_OR_RETURN·RETURN_IF_ERROR
- 18 absl::Status payload — 구조화된 에러 컨텍스트
- 19 absl::Status ↔ exception 변환 패턴
- 20 absl::string_view — non-owning 문자열 참조
- 21 absl::string_view 함정 — dangling·c_str·임시 객체
- 22 absl::StrCat — 가변 인자 문자열 연결과 AlphaNum
- 23 absl::StrSplit — Delimiter·Predicate·컨테이너 변환
- 24 absl::StrJoin — 컨테이너 결합과 Formatter
- 25 absl::StrFormat — type-safe printf·FormatSpec
- 26 Abseil ASCII 함수 — locale-free 분류·대소문자 변환
- 27 Abseil Escape — CEscape·HexEscape·Base64
- 28 absl::flat_hash_map — Swiss Table 기반 hash map
- 29 absl::flat_hash_set — set 버전 Swiss Table
- 30 absl::node_hash_map — stable pointer가 필요할 때
- 31 absl::btree_map — sorted·cache-friendly B-tree
- 32 absl::FixedArray — 런타임 크기 stack 배열
- 33 absl::InlinedVector — small buffer optimization
- 34 Abseil Swiss Table internals — control byte·SIMD probing
- 35 absl::Mutex — reader-writer·fairness·deadlock 검출
- 36 absl::Mutex Conditional Critical Section — Await로 cv 없애기
- 37 absl::Notification — once-only signal
- 38 absl::BlockingCounter·Barrier — 다중 thread 조율
- 39 absl::Mutex annotations — clang thread-safety로 race를 컴파일 타임에
- 40 absl::Time·Duration 분석 — 단단한 type
- 41 absl::Time Format·Parse
- 42 absl::CivilTime 분석
- 43 absl::time_zone 분석
- 44 absl::Time mocking — 테스트 친화 시간
- 45 absl::BitGen — 모던 난수 생성기
- 46 Abseil Random Distributions — Uniform·Exponential
- 47 Abseil Mocking Random — 테스트 결정성
- 48 Abseil Random Seeding·Entropy
- 49 absl::int128·uint128 분석
- 50 absl::bits — popcount·countl_zero
- 51 absl::optional vs std::optional
- 52 absl::variant 분석
- 53 absl::span 분석
- 54 absl::any 분석
- 55 absl::compare — three-way 비교
- 56 Abseil utility — apply·in_place
- 57 Abseil AbslHashValue 분석
- 58 Abseil HashState chaining
- 59 Abseil Custom hashable 구현
- 60 Abseil LOG·VLOG·CHECK 분석
- 61 Abseil LogSink 분석
- 62 Abseil LogEntry·structured logging
- 63 Abseil Stack trace·failure_signal_handler
- 64 ABSL_FLAG 정의 분석
- 65 Abseil ParseCommandLine 동작
- 66 Abseil Flag introspection·validation
- 67 Google 스타일의 Abseil 사용 패턴
- 68 Abseil 자주 보는 anti-pattern
- 69 std → absl 마이그레이션 전략
- 70 absl::Cleanup — 함수 종료 시 실행 보장
- 71 Abseil algorithm container 확장 — c_sort·c_find_if·c_count_if
- 72 absl::function_ref와 any_invocable — 함수 객체 전달의 두 축
- 73 absl::bind_front와 Overload — 함수 객체 보조 도구
- 74 absl::Cord — 분산 시스템용 대용량 문자열
- 75 absl::from_chars·SimpleAtoi — 빠른 숫자 변환
- 76 absl::Cord vs std::string — 선택 기준과 메모리 프로파일
- 77 absl::GetStackTrace와 Symbolize — crash 시 readable stack
- 78 absl::ComputeCrc32c — 하드웨어 가속 체크섬
- 79 absl::PeriodicSampler — 적응형 샘플링·jitter 회피
관련 글
absl::Mutex — reader-writer·fairness·deadlock 검출
Part 6-01: absl::Mutex — std::mutex/shared_mutex 통합, contention profiler, deadlock 검출, fairness 정책.
같은 시리즈에서 이어 읽기
absl::Mutex annotations — clang thread-safety로 race를 컴파일 타임에
Part 6-05: ABSL_GUARDED_BY, ABSL_LOCKS_EXCLUDED, ABSL_EXCLUSIVE_LOCKS_REQUIRED — clang -Wthread-safety와 결합해 lock 누락을 정적 검출.
같은 시리즈에서 이어 읽기
absl::BlockingCounter·Barrier — 다중 thread 조율
Part 6-04: absl::BlockingCounter와 absl::Barrier — N개 작업 완료 대기, N개 thread 동기 합류, fanout-fanin 패턴.
같은 시리즈에서 이어 읽기