원자 비트 AND: atomic_fetch_and(C11)
원자 비트 AND: atomic_fetch_and(C11)
원자 정수에서 특정 비트를 '꺼내는'(끄는) 작업을 하고 싶다면 비트 AND를 써요. 그런데 이 작업도 여러 스레드가 동시에 하면 경쟁이 생길 수 있으니 원자적으로 해야 안전하죠. 그 역할을 하는 함수가 **atomic_fetch_and**예요. 이름에 fetch가 들어간 것처럼 이전 값을 돌려받는 게 특징입니다.
출처: cppreference
본문
시그니처
<stdatomic.h> 헤더에 정의돼 있어요.
| 형태 | 주석 |
|---|---|
C atomic_fetch_and(volatile A* obj, M arg); |
(1) C11부터 |
C atomic_fetch_and_explicit(volatile A* obj, M arg, memory_order order); |
(2) C11부터 |
동작 방식
obj가 가리키는 값과 arg를 비트 단위 AND한 결과로 그 값을 원자적으로 교체해요. 그리고 obj가 이전에 담고 있던 값을 반환해요. read-modify-write 연산이에요. (1) 버전은 memory_order_seq_cst로, (2) 버전은 order로 메모리 접근을 정렬해요.
이 함수는 모든 원자 객체 타입 A에 대해 정의된 제네릭 함수예요. 인자는 volatile 원자 타입의 포인터라서 비휘발성·휘발성(예: 메모리 매핑 I/O) 원자 객체의 주소를 모두 받아들여요. M은 A가 원자 정수 타입이면 A에 대응하는 비원자 타입이고, A가 원자 포인터 타입이면 ptrdiff_t예요.
제네릭 함수의 이름이 매크로인지 외부 연결을 가진 식별자인지는 규정되지 않았어요. 매크로 정의를 막고 실제 함수에 접근하려고 (atomic_fetch_and)(...)처럼 괄호로 감싸 쓰는 경우, 또는 프로그램이 제네릭 함수 이름과 같은 외부 식별자를 정의하는 경우의 동작은 미정의(undefined)예요.
매개변수
| 매개변수 | 설명 |
|---|---|
obj |
수정할 원자 객체를 가리키는 포인터 |
arg |
원자 객체에 저장된 값에 비트 AND할 값 |
order |
이 연산의 메모리 동기화 순서. 모든 값 허용 |
반환값
obj가 이전에 담고 있던 값을 반환해요.
예제
원자 정수에서 특정 비트만 끄고 싶다면, 끄고 싶은 비트 위치를 ~로 반전시킨 arg를 AND하면 돼요. 예를 들어 하위 8비트만 남기고 싶다면 atomic_fetch_and(&flags, 0xFF)처럼 쓰는 식이에요. 단순히 값을 바꾸는 것뿐 아니라 진행 전의 플래그를 로그로 남기고 싶다면, 이 함수가 이전 값을 돌려주기 때문에 그대로 활용할 수 있어요.
더 알아보기
atomic_fetch_or,atomic_fetch_xor: 원자 비트 OR·XOR.memory_order: 메모리 동기화 순서.atomic_fetch_add,atomic_fetch_sub: 원자 증감 연산.