absl::Mutex — reader-writer·fairness·deadlock 검출
#한 줄 요약
absl::Mutex는 exclusive/shared lock을 한 타입에 통합하고, Conditional Critical Section, deadlock 검출, contention profiling, thread-safety 어노테이션을 모두 갖춘 동기화 primitive다. 표준의 std::mutex + std::shared_mutex + std::condition_variable을 한 묶음으로 대체한다.
#동기
표준 동기화는 책임이 흩어져 있다.
std::mutex— exclusive onlystd::shared_mutex— reader-writer (C++17)std::condition_variable— wait/notify (predicate를 외부 변수로 표현)std::condition_variable_any— 다른 mutex와도
이들의 조합은 코드를 복잡하게 만든다. condition variable의 spurious wakeup, predicate loop 같은 함정도 매번 직접 처리.
absl::Mutex는 하나의 타입에서 모두 처리한다. 추가로 함수형 조건 표현(Await)을 지원해 condition_variable 패턴을 단순화한다.
표준 mutex 대비 acquire 지연을 시나리오별로 비교하면 다음과 같다.
#API와 사용법
#include "absl/synchronization/mutex.h"
absl::Mutex mu;
// exclusivemu.Lock();// ...mu.Unlock();
// RAII{ absl::MutexLock lock(&mu); // ...}
// shared (read)mu.ReaderLock();mu.ReaderUnlock();
// 또는{ absl::ReaderMutexLock lock(&mu); // ...}MutexLock은 exclusive RAII, ReaderMutexLock은 shared RAII. std::lock_guard/std::shared_lock을 mutex 종류와 맞춰 골라 쓰는 일이 없다.
#Conditional Critical Section
진정한 차별점은 Await 함수다.
class Queue { public: void Push(int x) { absl::MutexLock l(&mu_); items_.push_back(x); }
int PopWaiting() { absl::MutexLock l(&mu_); mu_.Await(absl::Condition(this, &Queue::HasItem)); int x = items_.front(); items_.pop_front(); return x; }
private: bool HasItem() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) { return !items_.empty(); }
absl::Mutex mu_; std::deque<int> items_ ABSL_GUARDED_BY(mu_);};Await(Condition)는 condition이 true가 될 때까지 대기한다. predicate가 mutex 안에서 평가되므로 spurious wakeup이 없고 while loop이 필요 없다. condition variable 관용구가 완전히 사라진다.
상세는 Part 6-02 — Conditional Critical Section에서.
#std 비교
// std — condition variable 관용구std::mutex mu;std::condition_variable cv;std::deque<int> q;
void Push(int x) { { std::lock_guard<std::mutex> l(mu); q.push_back(x); } cv.notify_one();}
int Pop() { std::unique_lock<std::mutex> l(mu); cv.wait(l, [&] { return !q.empty(); }); // predicate while loop int x = q.front(); q.pop_front(); return x;}// absl — Await로 같은 동작absl::Mutex mu;std::deque<int> q;
void Push(int x) { absl::MutexLock l(&mu); q.push_back(x); // notify 불필요 — Await가 자동}
int Pop() { absl::MutexLock l(&mu); mu.Await(absl::Condition(+[](std::deque<int>* q) { return !q->empty(); }, &q)); int x = q.front(); q.pop_front(); return x;}notify 호출이 사라진다. Await는 mutex가 unlock될 때마다 조건을 자동으로 재평가한다.
#thread-safety annotation
absl::Mutex는 clang의 -Wthread-safety와 결합된 매크로를 제공한다.
class Cache { public: std::string Get(absl::string_view key) ABSL_LOCKS_EXCLUDED(mu_);
private: std::string LookupLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);
absl::Mutex mu_; absl::flat_hash_map<std::string, std::string> data_ ABSL_GUARDED_BY(mu_);};ABSL_GUARDED_BY(mu_)는 멤버 접근에 mu_ lock이 필요함을 컴파일러에 알린다. 위반은 컴파일 경고. 상세는 Part 6-05 — Mutex annotations.
#deadlock 검출
-DABSL_INTERNAL_USE_NONPROD_MUTEX 같은 빌드 옵션으로 lock 순서 그래프를 추적해 cycle을 검출한다. 일반적으로는 debug 빌드에서 켜고, release에서는 끈다.
ASSERTION FAILED: lock cycle detected: thread T1: acquired mu_a then waited on mu_b thread T2: acquired mu_b then waited on mu_aproduction 추적은 contention profiler와 결합해 lock graph를 dump하기도 한다.
#contention profiler
abseil은 mutex contention을 sampling profile로 모은다.
absl::RegisterMutexProfiler(&MyProfiler);// MyProfiler가 매 contention 이벤트에서 호출됨production에서 hot mutex를 식별하는 데 쓴다. std::mutex로는 직접 wrapping 없이는 불가능.
#contention이 무엇을 의미하는가
profiler가 보여 주는 contention은 결국 wait 시간이다.
CS 자체는 짧아도 자주 호출되면 대기 행렬이 길어진다. profiler의 hot mutex는 보통 CS가 긴 것이 아니라 호출 빈도가 너무 높은 것이다. 그래서 fix는 CS를 줄이는 게 아니라 lock을 쪼개거나 lock-free 자료구조로 옮기는 방향이 된다.
#fairness
absl::Mutex는 부분 fair 정책이다. 굶주린 writer가 너무 오래 대기하지 않도록 reader를 일정 시점 후에 막는다. 기본 std::shared_mutex는 fairness를 표준이 규정하지 않아 구현마다 다르다.
#코드 리뷰 포인트
1. std::mutex + std::condition_variable 패턴 → absl::Mutex + Await
// 회피 — notify 누락, spurious wakeup 함정std::mutex mu;std::condition_variable cv;bool ready = false;
// producer{ std::lock_guard<std::mutex> l(mu); ready = true;}cv.notify_one(); // ← 누락하면 deadlock
// consumerstd::unique_lock<std::mutex> l(mu);cv.wait(l, [&] { return ready; });// Goodabsl::Mutex mu;bool ready = false;
// producer{ absl::MutexLock l(&mu); ready = true; // notify 불필요}
// consumerabsl::MutexLock l(&mu);mu.Await(absl::Condition(&ready));2. shared lock 적극 활용
read 빈도 >> write 빈도면 ReaderLock으로 처리량 증대.
class ConfigCache { public: std::string Get(absl::string_view key) { absl::ReaderMutexLock l(&mu_); auto it = data_.find(key); return it == data_.end() ? "" : it->second; }
void Set(std::string k, std::string v) { absl::MutexLock l(&mu_); data_[std::move(k)] = std::move(v); }
private: absl::Mutex mu_; absl::flat_hash_map<std::string, std::string> data_ ABSL_GUARDED_BY(mu_);};3. annotation 일관 적용
annotation 한 군데만 적용하면 효과가 적다. 클래스 전체를 annotate.
#안티패턴
MutexLock 객체 무시
// 회피 — 임시 객체가 즉시 소멸, lock 해제됨absl::MutexLock(&mu_); // 변수 이름 없음DoWork(); // unlocked// Goodabsl::MutexLock l(&mu_);DoWork();MutexLock 같은 RAII 객체는 반드시 변수에 binding. clang -Wunused-variable로 잡힌다.
Await 안에서 lock acquire
mu_.Await(absl::Condition([&] { other_mu_.Lock(); // 회피 — Await predicate 안에서 다른 lock // ...}));Await predicate는 mutex가 잡힌 상태에서 자주 호출된다. 안에서 다른 lock을 잡으면 deadlock 위험.
Unlock 잊음
raw Lock/Unlock은 RAII 없이는 위험. 항상 MutexLock/ReaderMutexLock.
#정리
absl::Mutex= exclusive + shared + condition variable + annotation + profiler 통합.Await(Condition)로 condition variable 관용구 제거.- thread-safety 매크로로 컴파일 타임 검증.
- contention profiler, deadlock 검출 기본 제공.
- shared/exclusive 모두 RAII는
MutexLock/ReaderMutexLock.
#다음 편
Part 6-02 — Conditional Critical Section에서 Await 패턴을 더 자세히 본다.
#관련 항목
Abseil Code Review · 35 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 Conditional Critical Section — Await로 cv 없애기
Part 6-02: Mutex::Await로 함수형 condition을 정의해 condition_variable, notify, spurious wakeup 관용구를 모두 제거.
같은 시리즈에서 이어 읽기
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 패턴.
같은 시리즈에서 이어 읽기
이 글을 참조하는 글 (9)
- absl::PeriodicSampler — 적응형 샘플링·jitter 회피 — Abseil Code Review
- Abseil LogSink 분석 — Abseil Code Review
- absl::Time·Duration 분석 — 단단한 type — Abseil Code Review
- absl::Mutex annotations — clang thread-safety로 race를 컴파일 타임에 — Abseil Code Review
- absl::BlockingCounter·Barrier — 다중 thread 조율 — Abseil Code Review
- absl::Notification — once-only signal — Abseil Code Review
- absl::Mutex Conditional Critical Section — Await로 cv 없애기 — Abseil Code Review
- Abseil Swiss Table internals — control byte·SIMD probing — Abseil Code Review
- Abseil thread_annotations — clang TSA 통합 — Abseil Code Review