[Concurrency] ABA Problem
ABA ๋ฌธ์ ๋ Lock-Free ์๊ณ ๋ฆฌ์ฆ, ํนํ Compare and Swap ๊ธฐ๋ฐ์ ๊ตฌํ์ฒด์์ ๋ฐ์ํ ์ ์๋ ๋ํ์ ์ธ ๋์์ฑ ๋ฒ๊ทธ ํจํด์ด๋ค.
์ด์ ๋ํ ์ถฉ๋ถํ ์ดํด ์์ด๋ ์๋๋๋ก ๋์ํ๋ Lock-Free ์๋ฃ๊ตฌ์กฐ๋ฅผ ๋ง๋ค ์ ์๋ค.
์ผ๋ฐ์ ์ผ๋ก non-GC ์ธ์ด์์๋ง ๋ฐ์ํ๋ค.
๊ธฐ๋ณธ ๊ตฌํ (Linked Stack)
๋จผ์ ๊ธฐ๋ณธ์ ์ธ Lock-Free Stack ๊ตฌํ์ฒด๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ๋ค. ์ธ์ด๋ Rust๊ณ , ๋จ์ํ ํํ๋ก ๊ตฌํํ๋ค.
use std::ptr;
use std::sync::atomic::{AtomicPtr, Ordering};
use std::sync::Arc;
struct Node {
value: i32,
next: *mut Node,
}
struct Stack {
head: AtomicPtr<Node>,
}
unsafe impl Sync for Stack {}
unsafe impl Send for Stack {}
impl Stack {
fn new() -> Self {
Stack {
head: AtomicPtr::new(ptr::null_mut()),
}
}
fn push(&self, value: i32) {
let new_node = Box::into_raw(Box::new(Node {
value,
next: ptr::null_mut(),
}));
let mut current_head = self.head.load(Ordering::Relaxed);
loop {
unsafe {
(*new_node).next = current_head;
}
match self.head.compare_exchange(
current_head,
new_node,
Ordering::Release,
Ordering::Relaxed,
) {
Ok(_) => break, // ์ฑ๊ณต์ ์ผ๋ก ๊ต์ฒด๋จ
Err(head) => current_head = head, // ์คํจ, ์ head ์ฌ์ฉํด ์ฌ์๋
}
}
}
fn pop(&self) -> Option<i32> {
let mut current_head = self.head.load(Ordering::Relaxed);
loop {
if current_head.is_null() {
return None; // ์คํ์ด ๋น์ด ์์
}
let next = unsafe { (*current_head).next };
match self.head.compare_exchange(
current_head,
next,
Ordering::Release,
Ordering::Relaxed,
) {
Ok(_) => {
let value = unsafe { (*current_head).value };
// ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง๋ฅผ ์ํด Box๋ก ๋ณํํ์ฌ ์๋ฉธ
let _ = unsafe { Box::from_raw(current_head) };
return Some(value);
}
Err(head) => current_head = head, // ์คํจ, ์ head ์ฌ์ฉํด ์ฌ์๋
}
}
}
}
#[tokio::main]
async fn main() {
let stack = Arc::new(Stack::new());
// ์ด๊ธฐ ์ํ: [1, 2] (์์์๋ถํฐ)
stack.push(2);
stack.push(1);
println!("์ด๊ธฐ ์คํ: [1, 2]");
let a = stack.pop();
println!("ํ: {:?}", a); // 1
let b = stack.pop();
println!("ํ: {:?}", b); // 2
}
์ผ๋จ์ ์ฑ๊ธ์ค๋ ๋์์ ์ ๋์ํ๋ค.
๊ตฌํ๋ ๊ฐ๋จํ ํ๋ฒ ํ์ด๋ณด๊ฒ ๋ค.
์ฌ์ค ๋ง ๊ทธ๋ ๊ฒ ํน๋ณํ ๊ฒ์ ์๋ค.
Stack์ด๋ผ์ Pushํ ๋๋ ๋งจ ์์ head์ ์ ๋
ธ๋๋ฅผ ํ ๋นํ๊ณ , ์ ๋
ธ๋๊ฐ (๊ตฌ) head๋ฅผ ๊ฐ๋ฆฌํค๊ฒ ํ๋ค.
๊ทผ๋ฐ Push/Pop์ ๋์์ ์ผ๋ก ๋ค์ด์ฌ ์ ์๊ณ head๊ฐ ์ธ์ ๋ ๋ฐ๋์ด์์ ์ ์๊ธฐ ๋๋ฌธ์, compare_exchange(compare and swap)์ผ๋ก ๋ณธ์ธ์ด ์๋ ์ ๋ณด๊ฐ ์ต์ ์ผ ๋๋ง ์์ ์ ํ๋๋ก ํ๋ค.
์๋๋ฉด ๋ง์๋๊น์ง ๋บ๋บ์ด๋ฅผ ๋๋ค. ๊ทธ๋์ data race๊ฐ ์ฌํ ๋๋ ๋ฃจํ ๋บ๋บ์ด๋๋ฉด์ CPU๋ฅผ ํน์ฌ์ํฌ ์ ์๋ค.
pop๋ ๋ฐฉ๋ฒ๋ก ์ ๋น์ทํ๋ค.
head๋ฅผ ๋ฃ๊ณ ๋ฐ์ด๋ด๋๊ฒ ์๋๋ผ, head๋ฅผ ๋นผ๊ณ ๋์ด๋น๊ธฐ๋๊ฒ ๋ค๋ฅผ ๋ฟ์ด๋ค.
ABA ๋ฌธ์
๊ทธ๋ผ ๋ญ๊ฐ ๋ฌธ์ ์ผ๊น? ๊ทธ๋ฅ ๋ด์๋ ์ ๋์ํ ๊ฒ์ฒ๋ผ ๋ณด์ธ๋ค.
ํ์ง๋ง ์ด๋ฉด์๋ ์๊ฐ๋ณด๋ค ๋๊ฐํ ๊ทธ.. ๋ญ์๊ธฐ๊ฐ ์กด์ฌํด์ ๊ผฌ์ด๋ ์ํฉ์ด ๋ฐ์ํ ์ ์๋ค.
์๋๋ฆฌ์ค๋ ์ด๋ ๋ค.
-
์ฒซ๋ฒ์งธ ์ค๋ ๋๊ฐ pop/push์ ์์ head ๋ฐ์ดํฐ๋ฅผ Loadํ๋ค. = A(v1)
-
์ฒซ๋ฒ์งธ ์ค๋ ๋๊ฐ ์ผ์ ๋๋ด๊ธฐ ์ ์, ๋๋ฒ์งธ ์ค๋ ๋๊ฐ head๋ฅผ popํ๋ค. = (์์)
-
๋๋ฒ์งธ ์ค๋ ๋๊ฐ B๋ฅผ pushํ๋ค. = B
-
๋๋ฒ์งธ ์ค๋ ๋๊ฐ A๋ฅผ pushํ๋ค. = A(v2)->B
-
์ฒซ๋ฒ์งธ ์ค๋ ๋๊ฐ A(v1)์ A(v2)์ ๋ค๋ฅด๋ค๋ ๊ฒ์ ์์ง ๋ชปํด(๊ฐ์ ) A(v1)๋ฅผ ์๋ชป๋ ํํ๋ก CAS ์ฒ๋ฆฌํด๋ฒ๋ฆฐ๋ค.
-
์ฌ๊ธฐ์ C๋ฅผ pushํ ๊ฒฝ์ฐ์๋ C->A(v1)??, A(v2)->B ๊ฐ์ ํํ๋ก ์ฐ๊ฒฐ์ด ๊ผฌ์ฌ๋ฒ๋ฆฐ๋ค.
-
pop์ ํ ๊ฒฝ์ฐ์๋ ์๋ค๊ณ ์๊ฐํ๊ณ head๋ฅผ ๋น์๋ฒ๋ฆฌ๋, A(v2)->B๊ฐ ๋์๋ ํํ๋ก ๋ถ ๋ ๋ฒ๋ฆฐ๋ค.
๊ทธ๋ฆผ์ผ๋ก ๋ํ๋ด๋ฉด ์ด๋ ๋ค.
์ฐธ๊ณ ๋ก A -> B -> A ์ด๋ฐ ์ค๋ช
์ด ์ ๋๊ฐ ๋์ด์ ABA ๋ฌธ์ ๋ผ๊ณ ๋ถ๋ฅธ๋ค.
๊ทผ๋ฐ
*์ฒซ๋ฒ์งธ ์ค๋ ๋๊ฐ A(v1)์ A(v2)์ ๋ค๋ฅด๋ค๋ ๊ฒ์ ์์ง ๋ชปํด(๊ฐ์ ) *
-
์ด ๋ถ๋ถ์ ์ฝ๊ฐ ํผ๋์ค๋ฝ๊ฒ ๋๊ปด์ง ์ ์๋ค. ํ์ฌ ๊ตฌํ ๋ฒ์ ์์๋ CAS ๋จ์๊ฐ ํฌ์ธํฐ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
-
ํ ๋น์ด ๋ค๋ฅธ ์๊ธฐ์ ๋์๋ค๋ฉด A(v1)์ A(v2)์ ์ฃผ์๊ฐ์ด ๋ค๋ฅด๋ค๊ณ ํ๋จํ ์ ์์ํ ๋ฐ, ์ด๊ฒ ์ด๋ป๊ฒ ๊ฐ๋ฅํ ๊น?
**๋ฌธ์ : ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฌ์ฉ **
-
์ฌ์ค ๋ฉ๋ชจ๋ฆฌ์ ์ฃผ์๋ผ๋ ๊ฒ์ ๊ทธ๋ค์ง uniqueํ์ง ์๊ณ , ์ฌ์ฌ์ฉ์ด ๋๊ธฐ๋ ์ข์ ๋์์ด๋ค.
-
๋๋ถ๋ถ์ ๋ฉ๋ชจ๋ฆฌ allocator๋ ๋์ผํ ํฌ๊ธฐ์ ํ ๋น ์์ฒญ์ ๋ฐ์ผ๋ฉด ์ต๊ทผ์ ์ฌ์ฉ๋์๋ค ํด์ ๋ ๋ฉ๋ชจ๋ฆฌ ๋ธ๋ก์ ๋ค์ ์ฌ์ฌ์ฉํ๊ฒ ์ฃผ๊ธฐ๋ ํ๋ค.
-
๊ฒ๋ค๊ฐ ์ค๋ ๋ ๋ก์ปฌ ์บ์๋ฅผ ์ ๊ทน ์ฌ์ฉํ๋ allocator์ ๊ฒฝ์ฐ์๋ ๋์ผ ์ค๋ ๋์์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌํ ๋นํ ๊ฒฝ์ฐ ๊ฐ์ ์ฃผ์๋ฅผ ๋ฐ์ ๊ฐ๋ฅ์ฑ์ด ํนํ๋ ๋ ๋๋ค.
GC ์ธ์ด
-
GC ์ธ์ด๋ค์ ์๋ํ ๊ฒ์ ์๋์ง๋ง ์ด ๋ฌธ์ ๋ฅผ ์๋๋ฌ์ ํํผํ๋ค.
-
๋ฉ๋ชจ๋ฆฌ ์ฌ์ฌ์ฉ์ด ๋ฐ์ํ๋ ค๋ฉด ์ฒซ๋ฒ์งธ A(v1)๊ฐ ํ ๋นํด์ ๋์ด์ผ ํ๋๋ฐ, ์ ์ด์ Loadํด์ ๋ค๊ณ ์๋ ์์ ์์๋ ์ฐธ์กฐ๊ฐ ์กด์ฌํด์ ํ ๋นํด์ ๋ฅผ ํ์ง ์๊ณ , ๊ทธ ๋์์๋ ๋น์ฐํ ๊ทธ ์ฃผ์๋ฅผ ๋ค์ ์ฐ์ง๋ ์๊ธฐ ๋๋ฌธ์ด๋ค.
-
๊ทธ๋์ Java ๋ฑ GC ์ธ์ด๋ค์์๋ ๊ณ ๋ฏผ์ ๋ณ๋ก ํ ํ์๊ฐ ์๋ ๋ฌธ์ ๋ค.
์ด ๋ฌธ์ ์ ๋ํ ํด๊ฒฐ์ฑ ์ ์ด๊ฒ์ ๊ฒ ์์ง๋ง, ๊ฐ์ฅ ๋ํ์ ์ธ ๊ฒ์ 2๊ฐ์ง ์ ๋๋ค.
ํด๊ฒฐ์ฑ 1: Tagged Pointer
์ผ๋ฐ์ ์ธ ํด๊ฒฐ์ฑ
์ค ํ๋๋, ์์ ์ฃผ์ ๊ณต๊ฐ์๋ค๊ฐ ์ถ๊ฐ์ ์ธ ํ๊ทธ๊ฐ์ ๋ฃ์ด์ ํ ๋น๋๊ฑฐ๋ ์์ ๋ ๋๋ง๋ค ๊ฐ์ด ๊ณ ์ ์ฑ์ ๊ฐ์ง๊ฒ๋ ๋ง๋๋ ๊ฒ์ด๋ค. ๊ทธ๋ฌ๋ฉด ์ค์ ์ฃผ์ ๊ณต๊ฐ์ด ๊ฐ๋๋ผ๋, ์ผ๋ถ ๋นํธ๊ฐ ๋ฌ๋ผ์ CAS ์ฐ์ฐ์ด ์คํจํ ๊ฒ์ด๋ค.
์ด์ฐจํผ CAS๋ ์ฃผ์ ์์ฒด๋ฅผ ์๋ฏธ์๊ฒ ๋ณด๋๊ฒ ์๋๋ผ ์ฃผ์๋ผ๋ ์ ์๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ์ฒ๋ฆฌํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
64๋นํธ ํ๊ฒฝ์์๋ ๋นํธ ๊ณต๊ฐ์ด ์ข ๋จ๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ ์ ์๋ ๊ผผ์๋ค. ๋ณดํต 10-20๋นํธ ์ ๋๋ ๋ฉํ๋ฐ์ดํฐ๋ก ์์ฉํ ์ ์๋ค.
ํด๊ฒฐ์ฑ 2: ๋ฉ๋ชจ๋ฆฌ ํ ๋น ํด์ ์ง์ฐ์ํค๊ธฐ
ABA ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ ์์ธ ์ค ํ๋๋, ํ ๋น์ด ํด์ ๋ ๋ค์ ๊ทธ ์ฃผ์๋ฅผ ๋ค์ ์ค ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ฌ๋ฉด... ํด์ ๋ฅผ ์์ํค๋ฉด ๊ทธ ์ฃผ์๋ฅผ ๋ค์ ์์ฃผ๋๊น ๋๋๊ฑฐ ์๋๊ฐ?
๊ทธ๋์ ์์ ๊ฐ์ฒด์ ์ญ์ ๋ฅผ ๋ฏธ๋ค์ ์ฌ์ฌ์ฉ์ ๋ฐฉ์งํ๋ ๋ฐฉ๋ฒ๋ ๋ช๊ฐ์ง ์กด์ฌํ๋ค.
**Hazard Pointer **
๋ฉ๋ชจ๋ฆฌ ํด์ ์ง์ฐ์ ๋ํ์ ์ธ ์ ๊ทผ ๋ฐฉ๋ฒ์ด๋ค.
ํ์ฌ ์ฌ์ฉ๋๊ณ ์๋, ํด์ ๋์ง ๋ง์์ผ ํ๋ ์ฃผ์ ๋ชฉ๋ก์ Hazard Pointer๋ผ๊ณ ํด์ ์ค์ํํด์ ๊ด๋ฆฌํ๋ค.
๊ทธ๋ฆฌ๊ณ Hazard Pointer ๋ชฉ๋ก์ ์ํด์๋ ์ฃผ์๋ ํ ๋นํด์ ๋ฅผ ํ์ง ์๊ฒ๋ ํ๋ ๊ฒ์ด๋ค.
https://chfhrqnfrhc.tistory.com/entry/ABA-Problem-1
๊ทผ๋ฐ ๊ทธ๋ฅ ํด์ ๋ฅผ ๋ฏธ๋ฃจ๊ธฐ๋ง ํ๋ฉด ๋์๊ฐ ๋ ํ
๋, RetireList๋ผ๋ ํด์ ์ฉ ํ์ ๋ ์ค๋ ๋๋ณ๋ก ๋ง๋ค์ด์ ๊ด๋ฆฌํ๋ค.
pop์ผ๋ก ์ธํด์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํด์ ํด์ผ ํ๋ค๋ฉด, ๊ทธ๊ฑธ ํด์ ํ๋๊ฒ ์๋๋ผ ๊ฐ์ฒด๋ฅผ RetireList๋ก ์ฎ๊ธด ๋ค์์ Hazard Pointer ๋ฆฌ์คํธ์ ๋น๊ตํด์ ๊ด์ฐฎ์ผ๋ฉด lazyํ๊ฒ ์ ๊ฑฐํ๋ ๊ฒ์ด๋ค.
์ถ๊ฐ์ ์ธ ์ฑ๋ฅ ์๋ชจ๊ฐ ์ข ์ผ์ด๋ ์ ์์ง๋ง, ABA์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฌ์ฉ ๋ฌธ์ ๋ ํํผํ ์ ์๋ค.
์ฐธ์กฐ
https://chfhrqnfrhc.tistory.com/m/entry/ABA-Problem-1
https://minikin.me/blog/solving-the-aba-problem-in-rust-tagged-pointers
https://en.wikipedia.org/wiki/ABA_problem
https://en.wikipedia.org/wiki/Hazard_pointer
https://chfhrqnfrhc.tistory.com/entry/ABA-Problem-1