흐르는 시간의 블로그...

아.. 모르고 썼으면 큰일날뻔 했군요.

혹시 모르고 뒤지다가 찾았네요.
현재 코드에서 여러가지 면에서 재귀적인 lock을 사용중인데다가...
원래부터 윈도우즈에서부터 코딩했던터라 좀 고민했는데... 다행히 재귀적으로 처리하는 방법이 있네요.

글의 출처는http://rein.kr/blog/archives/648<= 이곳입니다.
이 글에 대한 변동사항이나 중요한 점은 저 링크를 참조하시기 바랍니다.
(여기는 혹시 모를 상황에 대비해서 그저 잠시 갖다놨습니다.)


pthread 프로그래머가 Win32에서 밟기 쉬운 지뢰 하나

미리 밝혀두지만 rein이 정말 본격적으로 Windows 시스템 프로그래밍을 한 것은 작년 부터다. 그런 의미에서 겪었던 삽질 하나를 밝혀둔다.[1]

Linux system에서 널리 사용되는 posix thread (이하 pthread) 라이브러리의 가장 기본적인 동기화 메커니즘은 pthread_mutex_t 라는 타입으로 불리는 일종의 mutex다.[2]이걸 쓰던 사람이 Windows의 CRITICAL_SECTION 이나 Win32 mutex를 사용할 때 가장 실수하기 쉬운 것.

Win32의 CRITICAL_SECTION이나 mutex는 recursive하게[3]lock을 잡는 것을 허용한다[4]

거꾸로 Win32 프로그래머가 linux pthread를 쓴다면 주의할 것,

pthread의 mutex는기본적으로recursive–locking을 허용하지 않는다

즉, 다음과 같은 코드는 영원히 정지한다[...]

pthread_mutex_lock( &lock );
pthread_mutex_lock( &lock ); // 이 줄을 벗어날 수 없다

pthread_mutex_unlock( &lock );
pthread_mutex_unlock( &lock );

Win32에 익숙한 프로그래머라면 내가 주석처리한 저 코드를 보고 갸웃할지도 모른다. 그렇지만 pthread_mutex의기본동작은 recursive–locking에 대한 거부다. 그렇지만 다음과 같은 방식으로 mutex를 생성하면 recursive–locking을 허용하게 만들 수 있다.

pthread_mutexattr_t attr;
pthread_mutexattr_init( &attr );
int val = PTHREAD_MUTEX_RECURSIVE_NP;
pthread_mutexattr_settype( &attr, val);
pthread_mutex_init( &lock, &attr );

이런 조금 귀찮은 과정을 거치면 recursive–locking을 지원하는 — win32 프로그래머가 좀 더 익숙해 할, 혹은 그런 종류의 lock이 필요한 상황에서 써야하는 — pthread용 mutex가 생성된다.

근본(…)이 *nix 세계의 사람인 rein은[5]pthread의 기본 동작을 조금 더 좋아한다. 이유는 예전에 설명했던Dead-lock;데드락을 막는 locking protocol((Lock들에 번호를 부여하고, 특정 순서로만 lock잡는걸 허용하는 것))때문이다.Win32의 기본적인 recursive–locking에서는 다음 시나리오에서 필패한다(잠재적인 dead–lock을 허용한다)

  1. lock L2
  2. … some code block …
  3. lock L1
  4. lock L2
  5. lock L3

이 경우에 pthread_mutex라면 4에서 이미 프로그램이 멈추기 때문에(기본적인 pthread_mutex 동작에서), 상대적으로 빨리 눈치채고 대응할 수 있다. (아마도 프로그램이 릴리즈 되기 전에). 반면에 Win32환경에서는 운만 좋으면[6]운이 나쁘다고 봐도 된다— 릴리즈되기 전에 데드락이 숨어있다는 것을 알 수 없게 된다.

물론win32도 좀 쓰기 시작한지라lock 전체의 잡는 순서를 로깅하는 간단한 per-thread 객체를 하나 써서 lock순서를 추적하기 때문에, 이런 일은 바로 눈치채고 있긴하지만, 가끔은 pthread_mutex가 그리워지기도 해서 이런 글을 끄적대 본다.

ps. 덤으로 intel TBB의 hash_table의숨겨져있는 lock들은pthread의 convention을 따른다. 즉 두 번 잡으려고하면 해당 스레드는 거의 영원히 멈추게 된다. 그런 의미에서 주의해야.

  1. 덤으로 금요일 저녁에 Rica한테 잘못된 사실을 하나 말했던 것을 정정하려한다 []
  2. 물론 spin–lock도 pthread_spinlock_t로 제공된다 []
  3. 재귀적으로. 즉 thread A가 어떤 mutex나 CS의 락을 얻은 상태에서 또 해당 락을 얻으려고 시도하면 잡을 수 있다 []
  4. 내가 Rica한테 잘못 설명한 부분이 여기. 난 Mutex는 재귀적으로 못잡는다고 생각했다. 근데 되더라고… []
  5. 시스템 프로그래밍을 Solaris 2.x에서 시작해서 Redhat과 Fedora를 거쳐 지금에 이르렀다 []
  6. 3이 실행되기전에 L1을 잡는 스레드가 없기만 하면된다 []

'프로그래밍???' 카테고리의 다른 글

PERL로 파일입출력  (0) 2012.04.06
Perl로 시간 처리하기  (0) 2012.04.06
clock_gettime에 대한 자료  (0) 2011.08.04
FIFO 사용에 대한 오류 및 문제  (0) 2011.08.02
mkdir -p 옵션 구현  (0) 2011.06.17