2012-06-29 2 views
0

행동이 중요 섹션을 좋아하는 재귀 잠금을 작성합니다. 하지만 재귀 기능을 구현할 때 문제가 있습니다. 같은 코드 :Windows에서 이벤트 및 인터록 프리미티브로만 재귀 잠금을 구현하는 방법은 무엇입니까?

#include "own_cs.h" 
#define WIN32_LEAN_AND_MEAN 
#include <windows.h> 
#include <assert.h> 
#include <malloc.h> 
#include <stdlib.h> 
#include <stdio.h> 


struct own_critical_section 
{ 
    long own_lock_count;   // count of locked thread, -1 means unlock , other means lock. 
    HANDLE own_event;    // auto-reset 
    DWORD own_owning_thread_id; // owner thread of lock 
}; 


void InitialOwnCriticalSection(own_critical_section** own_cs) 
{ 
    *own_cs = (own_critical_section*)malloc(sizeof(own_critical_section)); 
    (*own_cs)->own_lock_count = -1; 
    (*own_cs)->own_event = CreateEventW(NULL, FALSE, FALSE, NULL); 
    (*own_cs)->own_owning_thread_id = 0; 
} 

void DeleteOwnCriticalSection(own_critical_section* own_cs) 
{ 
    assert(own_cs != NULL); 

    CloseHandle(own_cs->own_event); 
    free(own_cs); 
} 

void EnterOwnCriticalSection(own_critical_section* own_cs) 
{ 
    for (int spin_count = 0; spin_count < 500; ++ spin_count) 
    {//spinlock 
     if (-1L == InterlockedCompareExchange(&own_cs->own_lock_count, -1L, -1L)) 
      break; 
     Sleep(0); 
    } 

    if(0 < InterlockedIncrement(&own_cs->own_lock_count) && 
     (own_cs->own_owning_thread_id != ::GetCurrentThreadId()))  
//there is no guarantee that own_owning_thread_id is set before comparison with tid.so this comparison is not thread-safe. 
    { 
     //locked 
     WaitForSingleObject(own_cs->own_event, INFINITE); 
    } 

    own_cs->own_owning_thread_id = ::GetCurrentThreadId(); 
} 

void LeaveOwnCriticalSection(own_critical_section* own_cs) 
{ 
    if(-1L != InterlockedDecrement(&own_cs->own_lock_count) && 
     (::GetCurrentThreadId() == own_cs->own_owning_thread_id)) 
    { 
     SetEvent(own_cs->own_event); 
    } 
} 

문제는 EnterOwnCriticalSection 루틴은,이 함수에서 주석으로,이 비교는 스레드로부터 안전하지 않습니다 tid.so과 비교하기 전에 설정 own_owning_thread_id 보장은 없습니다.

+1

"C++", 예, 완전히보고 있습니다. – Puppy

+0

@DeadMG 범위 지정 연산자가 있으므로 apparantly입니까? (@OP : C++ 기능을 사용해야하고 C 태그를 제거해야합니다. 일반적으로 C 및 C++은 일반적으로 두 가지 언어입니다.) – moooeeeep

+0

원하는 것을 수행하는 것처럼 보이는 CRITICAL_SECTION을 사용하지 않는 이유는 무엇입니까? – Ben

답변

1
#include "own_cs.h" 
#define WIN32_LEAN_AND_MEAN 
#include <windows.h> 
#include <assert.h> 
#include <malloc.h> 
#include <stdlib.h> 
#include <stdio.h> 


struct own_critical_section 
{ 
    long own_lock_count;   // count of locked thread, -1 means unlock , other means lock. 
    HANDLE own_event;    // auto-reset 
    DWORD own_owning_thread_id; // owner thread of lock 
    long own_recursion_count;  // count of recursion 
}; 


void InitialOwnCriticalSection(own_critical_section** own_cs) 
{ 
    *own_cs = (own_critical_section*)malloc(sizeof(own_critical_section)); 
    (*own_cs)->own_lock_count = -1; 
    (*own_cs)->own_event = CreateEventW(NULL, FALSE, FALSE, NULL); 
    (*own_cs)->own_owning_thread_id = 0; 
    (*own_cs)->own_recursion_count = 0; 
} 

void DeleteOwnCriticalSection(own_critical_section* own_cs) 
{ 
    assert(own_cs != NULL); 

    CloseHandle(own_cs->own_event); 
    free(own_cs); 
} 

void EnterOwnCriticalSection(own_critical_section* own_cs) 
{ 
    assert(own_cs->own_lock_count>=-1L); 

    DWORD current_thread_id = GetCurrentThreadId(); 

    if (own_cs->own_owning_thread_id != current_thread_id) 
    { 
     for (int spin_count = 0; spin_count < 500; ++ spin_count) 
     {//spinlock 
      if (-1L == InterlockedCompareExchange(&own_cs->own_lock_count, -1L, -1L)) 
       break; 
      Sleep(0); 
     }  
    } 

    // if current thread is the acquired thread, 
    // own_cs->own_owning_thread_id = current_thread_id 
    // else if current thread is not the acquired thread 
    // own_cs->own_owning_thread_id should be 0 or the thread_id which is acquired thread. 
    if(0 < InterlockedIncrement(&own_cs->own_lock_count) && 
     (own_cs->own_owning_thread_id != current_thread_id)) 
    {  
     //locked 
     WaitForSingleObject(own_cs->own_event, INFINITE);  
    } 

    // inside lock 
    ++own_cs->own_recursion_count; 
    own_cs->own_owning_thread_id = current_thread_id; 
} 

void LeaveOwnCriticalSection(own_critical_section* own_cs) 
{ 
    assert(own_cs->own_lock_count>=-1L && own_cs->own_recursion_count>=0); 

    if (--own_cs->own_recursion_count == 0) 
     own_cs->own_owning_thread_id = 0; 

    if(-1L != InterlockedDecrement(&own_cs->own_lock_count) && 
     (0 == own_cs->own_recursion_count)) 
    { 
     SetEvent(own_cs->own_event); 
    } 
} 

위 코드가 업데이트되었습니다. 포인트에 코멘트를 붙입니다.