작성
·
62
·
수정됨
0
안녕하세요!
동시에 TryPop()
을 할 경우, oldHead
를 참조하는 스레드가 있을 수 있고 이 때 무작정 해제해버리면 use after delete 오류가 발생할 수 있다고 하셨습니다.
TryDelete()
함수를 구현하실 때, 1 == popCount 안에서 delete old_head
를 할 때는 이미 분리했으니까 괜찮다고 하셨는데요. 첫 문단에서 언급한 부분과 반대되는 말을 하신 것 같다는 느낌을 받았습니다.
제가 생각했을 때는, 저희 수업 때 사용하신 예제 코드에서는, cas
연산을 통해 oldHead
를 분리했기 때문에 use after delete 오류가 안 날 것 같단 생각이 들더라고요.
궁금해서 테스트를 해봤습니다.TryDelete()
함수를 호출하지 않고 바로 delete old_head;
를 호출하는 코드를 넣었고요, 지금 이 질문을 올리고 있는 와중에 push()
는 sleep_for(1ms)
를 주고 pop()
을 하는 스레드는 5개를 돌려서, 진단 도구에서 확인되는 프로세스 메모리가 1mb이 유지되게 설정해서 30분 넘게 돌려보고 있습니다. 오류가 안 나고 있는 광경을 목격 중입니다. 그냥 운이 좋은 걸까요?
오류가 발생하지 않는 이유는 다음과 같다 생각합니다.
atomic 변수인 head
에서 cas 연산을 하기 때문에 간발의 차이로 먼저 다음 코드를 통과한 스레드가 획득한 oldHead
는 다른 스레드가 이후에 될 oldHead
와 같지 않다. while
조건에서 true를 반환하는 순간 head
는 head
의 next
를 가리키고 있게 되기 때문이다. compare_exchange_weak()
함수가 true를 반환하면 head
는 원자적으로 head->next
를 가리키는 상태가 된다.
while (old && false == _head.compare_exchange_weak(old, old->next))
return false;
고견 부탁드립니다.
멋진 강의 감사합니다.
답변 1
1
안녕하세요, 인프런 AI 인턴입니다. literate_t님.
질문해주신 내용을 통해 Lock-Free Stack #2의 TryPop
함수에서 oldHead
를 삭제하는 상황에 대해 여러 가지 의문을 갖고 계신 것을 알 수 있었습니다. 관련해서 유사한 질문과 답변들이 여러 있어서 도움이 될 것 같습니다.
_head
변경을 다루며 atomic 연산이 어떻게 작동하는지에 대해 상세히 설명하고 있습니다._head.compare_exchange_weak
가 실패하면 _head
가 업데이트되지 않아서 크래시가 발생하지 않는 이유를 잘 설명합니다.oldHead
의 사용과 삭제에 대해 자세히 설명하고 있으며, _popCount
를 사용하여 어떻게 thread-safe한 방법으로 노드를 제거하는지 설명합니다.compare_exchange_weak
함수에서 null pointer 참조 문제와 관련된 내용을 다룹니다.위의 답변들로 기본적인 개념과 동작 방식을 이해하실 수 있을 겁니다. 추가적으로 요약해드리자면, CAS (compare-and-swap)
연산은 atomic하게 작동하기 때문에, 여러 스레드가 동시에 접근해도 oldHead
가 갱신되는 과정을 안전하게 관리합니다. 따라서, while
루프를 통해 CAS
연산이 실패할 때까지 반복하며 head
가 업데이트되는 과정을 보장합니다. 이러한 과정 덕분에 동시에 여러 스레드가 TryPop
를 호출해도 use-after-delete
문제가 발생하지 않게 됩니다.
질문자님께서 실행한 테스트에서 오류가 나지 않은 이유도 바로 이 때문입니다. CAS 연산을 통해 다음 스레드가 참조할 oldHead
는 항상 유효한 값을 가리키고 있으며, 먼저 head
를 갱신한 스레드는 _head.compare_exchange_weak
가 성공하여 oldHead
를 올바르게 분리해 내고 삭제할 수 있습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
인프런 인턴 친구의 답변이 틀리지 않았다면 수업 예제에서는 TryPop()을 구현할 때 분리된 oldHead를 바로 지워도 된다는 의미가 되네요.
루키스님 고견이 궁금합니다.