absl::Notification — once-only signal
#한 줄 요약
absl::Notification은 한 번만 발생하는 이벤트(서버 초기화 완료, shutdown 요청 등)를 표현하는 primitive다. Notify()를 한 번 호출하면 WaitForNotification()이 즉시 반환되고, 이후의 HasBeenNotified()는 항상 true. atomic<bool>보다 안전하고 condition_variable보다 단순하다.
#동기
“한 번만 일어나는 이벤트”는 매우 흔하다.
- 서버 시작 완료
- shutdown 요청
- 비동기 작업 완료
- 첫 데이터 도착
이를 표준 도구로 표현하는 세 가지 방법:
std::atomic<bool>— busy wait 또는 sleep loop. 비효율적.std::condition_variable+ bool + mutex — 보일러플레이트 많음. 신호 후 condition variable 재사용은 안 됨.std::promise<void>/std::future<void>— 가능하나 의미가 약함, 한 번만 wait 가능.
absl::Notification은 이 정확한 use case에 맞춰진다.
#API와 사용법
#include "absl/synchronization/notification.h"
namespace absl {class Notification { public: Notification(); bool HasBeenNotified() const; void WaitForNotification() const; bool WaitForNotificationWithTimeout(absl::Duration timeout) const; bool WaitForNotificationWithDeadline(absl::Time deadline) const; void Notify();};}상태가 매우 단순하다 — not notified → notified. 한 방향. Notify()를 두 번 호출하면 ABORT.
// 서버 초기화 패턴class Server { public: void Start() { std::thread([this] { Initialize(); ready_.Notify(); // 초기화 완료 알림 }).detach(); }
void WaitReady() { ready_.WaitForNotification(); } bool IsReady() const { return ready_.HasBeenNotified(); }
private: absl::Notification ready_;};// shutdown 요청 패턴class Worker { public: void Shutdown() { shutdown_.Notify(); }
void Run() { while (!shutdown_.HasBeenNotified()) { auto task = TryGetTask(); if (task) Process(*task); else shutdown_.WaitForNotificationWithTimeout(absl::Milliseconds(100)); } }
private: absl::Notification shutdown_;};#내부 구현
Notification은 mutex + 단일 bool로 구현된다.
// absl/synchronization/notification.h (요약)class Notification { mutable absl::Mutex mutex_; std::atomic<bool> notified_yet_;
public: bool HasBeenNotified() const { return notified_yet_.load(std::memory_order_acquire); }
void WaitForNotification() const { if (!HasBeenNotified()) { absl::MutexLock l(&mutex_); mutex_.Await(absl::Condition(&HasBeenNotifiedInternal, this)); } }
void Notify() { absl::MutexLock l(&mutex_); ABSL_CHECK(!notified_yet_.exchange(true, std::memory_order_release)); }};핵심은 fast path다. WaitForNotification은 먼저 atomic load로 확인하고, 이미 notified면 mutex를 잡지 않고 즉시 반환. 한 번 신호된 후의 wait는 사실상 비용 0.
Notify()는 exchange 결과로 이전 값을 확인한다. 이전이 이미 true면 두 번 notify — ABORT.
#std atomic + cv 비교
// 회피 — atomic + busy waitstd::atomic<bool> ready{false};
void Wait() { while (!ready.load(std::memory_order_acquire)) { std::this_thread::yield(); // CPU 낭비 }}// 회피 — cv 패턴 (보일러플레이트 많음)std::mutex mu;std::condition_variable cv;bool ready = false;
void Wait() { std::unique_lock l(mu); cv.wait(l, [&] { return ready; });}
void Signal() { { std::lock_guard l(mu); ready = true; } cv.notify_all();}// Good — Notificationabsl::Notification n;
void Wait() { n.WaitForNotification(); }void Signal() { n.Notify(); }의미가 명확하다 — 한 번만 일어나는 이벤트다라는 의도가 타입에 박혀 있다.
#코드 리뷰 포인트
1. atomic
// 회피std::atomic<bool> done{false};while (!done.load()) std::this_thread::sleep_for(1ms);
// Goodabsl::Notification done;done.WaitForNotification();2. one-shot signal에 condition_variable
condition_variable은 반복 신호용. 한 번만 일어나는 이벤트에는 과한 도구다.
// 회피std::mutex mu; std::condition_variable cv; bool ready = false;
// Goodabsl::Notification ready;3. 멤버 변수로 자연스럽게
class JobRunner { absl::Notification cancelled_; public: void Cancel() { cancelled_.Notify(); } bool IsCancelled() const { return cancelled_.HasBeenNotified(); }};타입 이름이 코드를 설명한다.
4. timeout이 있는 wait
if (!server_ready_.WaitForNotificationWithTimeout(absl::Seconds(30))) { LOG(ERROR) << "server didn't start in 30s"; return absl::DeadlineExceededError("startup timeout");}이 한 줄이 startup 단계 디버깅에 결정적인 정보를 준다.
#안티패턴
두 번 Notify
n.Notify();n.Notify(); // ABORT — Notification 모델 위반Notification은 한 번만 신호. 반복 신호가 필요하면 다른 primitive(BlockingCounter, Mutex::Await).
reset 시도
Notification에는 reset이 없다. 새 라운드가 필요하면 새 Notification 객체.
// 회피absl::Notification n;n.Notify(); n.WaitForNotification();// 두 번째 라운드 — 새 n이 필요Wait이 짧을 거라 가정 + busy spin
Notification이 이미 fast path를 제공한다(이미 notified면 mutex 안 잡음). 외부 busy spin은 불필요.
#BlockingCounter / Barrier와의 차이
| primitive | 의미 |
|---|---|
Notification | 1회 신호 |
BlockingCounter | N개 decrement이 모두 끝나면 wait 해제 |
Barrier | N개 thread가 모두 도착하면 모두 진행 |
상세는 Part 6-04 — BlockingCounter / Barrier에서.
#정리
Notification은 한 번 일어나는 이벤트의 표준 표현.Notify는 1회만 가능, 2회는 ABORT.HasBeenNotified는 lock 없는 빠른 경로.- atomic flag보다 안전, condition variable보다 단순.
- reset 없음 — 반복 신호는 다른 primitive.
#다음 편
Part 6-04 — BlockingCounter / Barrier에서 다중 thread 동기화 primitive를 본다.
#관련 항목
Abseil Code Review · 37 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 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 패턴.
같은 시리즈에서 이어 읽기
absl::Mutex Conditional Critical Section — Await로 cv 없애기
Part 6-02: Mutex::Await로 함수형 condition을 정의해 condition_variable, notify, spurious wakeup 관용구를 모두 제거.
같은 시리즈에서 이어 읽기