본문 바로가기

작업일지/Embedded

Critical Section Block

WinCE에서 모뎀 드라이버를 만들면서 Critical Section을 좀더 편리하게 관리를 해야겠다는 생각에 뒤지다 보니 생각보다 훌륭한 물건이 있다.
(출처:http://www.codeproject.com/KB/cpp/CriticalSectionBlock.aspx)
아래는 http://www.codeproject.com/KB/cpp/CriticalSectionBlock.aspx의 내용을 바탕으로 재 해석 한것이다..
원본을 링크를 따라 가면 된다.





익히 알고 있듯이 Critical Section을 사용 할 때는 항상 아래와 같이 쌍을 이루어야만 한다.
EnterCriticalSection(&cs);

......

LeaveCriticalSection(&cs);


그런데 이런 쌍을 이루는 작업은 코드가 복잡 해질수록 쌍을 잃어 버리거나 복잡 해질수 있다.
{
    EnterCriticalSection(&cs);
   
    ......

    if (...)
        LeaveCriticalSection(&cs);

    ......

    if (...)
        EnterCriticalSection(&cs);

    ......

    LeaveCriticalSection(&cs);
}

위의 코드는 여러 조건 상황에 따라 쌍을 맞춰 코딩한 상황이다.
(적절한 예제가 생각이 안난다. 억지 스럽지만 위처럼 얽히고 섥히는 경우가 있다고 가정하자.)
이럴 경우 코드도 복잡해지고 자칫 쌍을 잃어버려 데드락에 걸리는 경우도 생길 것이다.
복잡한 코드는 따로 문서를 만들어 두거나 주석을 장황하게 달아 놓아야 실수를 방지 할 수 있다.
그럼에도 불구하고 같은 실수를 매번 일으키기도 한다.

그래서 보통 이렇게 쌍을 이루는 것들은 auto_ptr을 많이 이용 한다.
아래처럼 말이다.
class AutoCriticalSection
{
public:
AutoCriticalSection(CRITICAL_SECTION* pCS)
: m_pCS(pCS)
{
EnterCriticalSection(m_pCS);
}

~AutoCriticalSection()
{
LeaveCriticalSection(m_pCS);
}

private:
CRITICAL_SECTION* m_pCS;
};

나는 이런 방법이 완벽 하다고 생각 하고 많이 써왔었다.
사용시 아래처럼 사용된다.
// Now you limit protected range as you want.
{
AutoCriticalSection(&cs) myAutoCS;
// Manipulate some shared data here.
}
이제 Cretical Section을 사용함에 있어서 블럭으로 감싸는 것만 생각하면 된다.
쌍을 맞추지 않아도 됨으로 참 편하다.

그러나 이또한 단점이 없진 않다.
먼저 블럭을 저렇게 사용하는 경우는 직관적이지 못하다는 것이다.
그럼 어떤점이 직관적이지 못한가?
보통 블럭은 함수의 시작과 끝( void function a () {...} ), 또는 제어문과 같은 경우 ( if (..) {...} )에 사용되는 것처럼 키워드 이후에 호출된다.
그런데 위의 코드는 단지 블럭을 스코프로만 이용하기 위해 호출 했기에 혼란의 소지가 있다는 것이다.

다음 코드를 보자
if (index <= uiCurrIndex) Update();
{
AutoCriticalSection(&cs) myAutoCS;
// Manipulate some shared data here.
}
자칫 블럭이 if(..)문의 블럭처럼 보인다.
줄바꿈 하나가 없어짐으로 이런 혼란이 오는 것이다.

줄바꿈과 주석을 적절히 이용해서 혼란을 줄여봤다.
if (index <= uiCurrIndex)
    Update();

// Now you limit protected range as you want.
{
AutoCriticalSection(&cs) myAutoCS;
// Manipulate some shared data here.
}
이제 좀 낫다.
그러나 보장되지 못한다는게 여전히 문제다.
주석 줄바꿈은 개인적인 취향의 문제이기에 나만 잘해서는 문제를 피해 갈수 없다.
실제로도 이런 혼란은 종종 생긴다.
이런 혼란 때문에 직관적이지 못하다고 말하는 것이다.

다음의 코드는 이를 방지한 코드이다.
class CriticalSectionContainer
{
public:
    CriticalSectionContainer(CRITICAL_SECTION* pCS)
    : m_pCS(pCS)
    {
        EnterCriticalSection(m_pCS);
    }
   
    ~CriticalSectionContainer()
    {
            LeaveCriticalSection(m_pCS);
    }
   
    operator bool()
    {
        return true;
    }
   
private:
    CRITICAL_SECTION* m_pCS;
};
   
#define CSBLOCK(x) if (CriticalSectionContainer __csc = x)

아래처럼 사용된다.
 
CSBLOCK(&cs)
{
// Manipulate some shared data here.
}
정말 간단하게 되었다.
매우 직관적인 코드가 나오며 혼란이 없다.


나는 여기에 아래와 같이 초기화 하고 해제 하는 부분도 일관성을 갖추게 만들었다.
 class CriticalSectionContainer
{
public:
    CriticalSectionContainer(CRITICAL_SECTION* pCS)
    : m_pCS(pCS)
    {
        EnterCriticalSection(m_pCS);
    }
 
    ~CriticalSectionContainer()
    {
        LeaveCriticalSection(m_pCS);
    }
 
    operator bool()
    {
        return true;
    }
 
private:
    CRITICAL_SECTION*    m_pCS;
};
#define    CS_INIT(x)        (InitializeCriticalSection(x))
#define    CS_DELETE(x)    (DeleteCriticalSection(x))
#define    CS_BLOCK(x)        if (CriticalSectionContainer __csc = x)

CS_INIT(x)과 CS_DELETE(x)를 추가 하였다.
이제 Critical Setion은 일관성의 띄고 매우 간결하게 바뀌었다.








제작자를 찾아보니 "hongseok.com" 으로 되어 있다.
codeproject에 댓글을 남기는 방법은 모르겠고, hongseok.com에 방문 했으나 글을 남기는 곳이 없다.
이곳에 감사하다는 말을 남기는 방법밖엔 없다.
"잘 쓰고 있으며 감사합니다. ^-^"