0

クリティカルセクションのような振る舞いをする再帰ロックを書きます。しかし、再帰機能を実装すると問題が発生します。このようなコード:

#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 との比較の前に own_owning_thread_id が設定されているという保証はありません。したがって、この比較はスレッドセーフではありません。

4

1 に答える 1

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 );  
    }  
}  

上記のコードは更新されています。ポイントにコメントを追加します。

于 2012-07-02T06:42:21.223 に答える