クリティカルセクションのような振る舞いをする再帰ロックを書きます。しかし、再帰機能を実装すると問題が発生します。このようなコード:
#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 が設定されているという保証はありません。したがって、この比較はスレッドセーフではありません。