[C++20 모듈과 IOCP로 완성하는 MMORPG 서버] 5. DeadLock 탐지

MIN·2025년 9월 10일
0

C++20 & IOCP

목록 보기
5/5

깃허브 주소 : CPP20_GameServer

이번에는 멀티스레딩의 대표적인 문제인 DeadLock을 탐지하는 기능을 만들어봤다.
이미 DeadLock을 예방하는 API나 자료구조는 많이 나와 있지만, 그런 API를 과하게 쓰면 오히려 프로그램이 과부하가 오거나 성능적으로 부족하다고 느낄 수 있다.

그래서 이번에는 직접 DeadLock을 탐지해서 로그를 남기는 기능을 만들고 확인해보려 한다.
C++20 기능을 활용하면서, 지난번에 만든 매크로 대체용 CRASH 인라인 함수도 사용해 실제로 적용이 가능한지 시험해본다.

DeadLock 탐지 아이디어

각 스레드가 Lock을 잡는 과정에서 고유한 ID를 부여하고, Lock들의 관계를 기록해 그래프를 만든다.

그 그래프를 탐색할 때 순방향으로만 탐색된다면 정상적으로 Lock이 걸린 상황이지만, 역방향 탐색이 발생한다면 Lock 간에 서로가 서로를 바라보는 구조가 된다.
이 경우 DeadLock이 발생했다고 판단하고 로그를 남기도록 했다.

DeadLock 탐지

첫 번째 이미지 두 번째 이미지

DeadLockProfiler - PushLock

Id와 name을 관리하기 위해 unordered_map을 사용했다. 자동 정렬을 피하면서 두 값을 함께 관리하기에 적합하기 때문이다.
이어서 Lock을 관리한 적이 없다면 새로 만들고 있다면 바로 찾아낸다.

그리고 stack 자료구조를 사용해서 Lock의 고유 id을 넣어서 어떤 Lock이 존재하는지 체크한다.

캡처 이미지

DeadLockProfiler - PopLock

Lock을 다 사용하면 해제를 해야 한다.
그래서 각각 예외 안전성을 체크하면서 마지막에 stack에 있는 요소를 삭제한다.
이 과정에서도 예외가 발생하면 CRASH 인라인 함수를 호출해 문제를 드러낸다.

결과 이미지

DeadLockProfiler - CheckCycle

새로운 Lock이 들어오면 Lock들의 연결 상태를 확인하기 위해 CheckCycle을 호출한다.
Lock 개수를 확인한 뒤, DFS 알고리즘으로 탐색하기 위해 필요한 변수를 초기화한다.
탐색이 끝나면 다시 정리해 다른 스레드가 편하게 사용할 수 있도록 한다.

첫 번째 이미지 두 번째 이미지

DeadLockProfiler - Dfs

DFS(깊이 우선 탐색)는 그래프 탐색에서 널리 쓰이는 알고리즘이다.
하나의 노드에서 연결된 노드 중 하나를 선택해 끝까지 탐색한 뒤, 다시 돌아와 탐색하지 않은 노드를 찾는 방식이다.

코드에서는 먼저 _discoveredOrder 배열을 확인해 이미 방문했는지 체크한다.
그리고 그래프 방향을 검사하는 부분으로 넘어간다.

만약 이전에 탐색(here)했던 Lock이 이번에 탐색(there)했던 Lock보다 늦게 발견되고 아직 그래프 탐색이 끝나지 않았다면 그것은 역방향이라는 것을 알 수 있다.

그래서 이제 어떤 Lock들이 DeadLock을 일으켰는지 로그를 찍어볼건데, 이번 C++20에 새로 생긴 format을 사용해봤다.
사실 이부분 빼고는 달라진 점은 없기에 빠르게 format 형식을 이용해서 로그를 찍고 CRASH 인라인 함수를 호출한다.

참고

Inflearn [Rookiss][C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버

profile
게임개발자(진)

0개의 댓글