Bug#25042 OPTIMIZE TABLE cause race condition in IO CACHE SHARE

- The condition variable implementation "lost" a signal to
  WaitOnSingleObject when a semaphore was released.
- The signal could be consumed by a new call to pthread_cond_wait
  before all waiting threads had awoken.
- The new implementation of pthread_cond_* uses events
  instead of semaphores. It also uses an extra lock to protect entry
  into new cond wait before the broadcast has finished.


include/my_pthread.h:
  - New implementatin of pthread_cond_init. This version uses events
    instead of semaphores
mysys/my_wincond.c:
  - New implementatin of pthread_cond_init. This version uses events
    instead of semaphores
This commit is contained in:
unknown 2007-02-15 14:08:21 +01:00
parent e5c1656e97
commit 448f6003f9
2 changed files with 122 additions and 43 deletions

View File

@ -80,11 +80,17 @@ typedef struct st_pthread_link {
typedef struct {
uint32 waiting;
#ifdef OS2
HEV semaphore;
#else
HANDLE semaphore;
#endif
CRITICAL_SECTION lock_waiting;
enum {
SIGNAL= 0,
BROADCAST= 1,
MAX_EVENTS= 2
} EVENTS;
HANDLE events[MAX_EVENTS];
HANDLE broadcast_block_event;
} pthread_cond_t;
typedef int pthread_mutexattr_t;

View File

@ -27,27 +27,48 @@
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
{
cond->waiting=0;
cond->semaphore=CreateSemaphore(NULL,0,0x7FFFFFFF,NullS);
if (!cond->semaphore)
cond->waiting= 0;
InitializeCriticalSection(&cond->lock_waiting);
cond->events[SIGNAL]= CreateEvent(NULL, /* no security */
FALSE, /* auto-reset event */
FALSE, /* non-signaled initially */
NULL); /* unnamed */
/* Create a manual-reset event. */
cond->events[BROADCAST]= CreateEvent(NULL, /* no security */
TRUE, /* manual-reset */
FALSE, /* non-signaled initially */
NULL); /* unnamed */
cond->broadcast_block_event= CreateEvent(NULL, /* no security */
TRUE, /* manual-reset */
TRUE, /* signaled initially */
NULL); /* unnamed */
if( cond->events[SIGNAL] == NULL ||
cond->events[BROADCAST] == NULL ||
cond->broadcast_block_event == NULL )
return ENOMEM;
return 0;
}
int pthread_cond_destroy(pthread_cond_t *cond)
{
return CloseHandle(cond->semaphore) ? 0 : EINVAL;
DeleteCriticalSection(&cond->lock_waiting);
if (CloseHandle(cond->events[SIGNAL]) == 0 ||
CloseHandle(cond->events[BROADCAST]) == 0 ||
CloseHandle(cond->broadcast_block_event) == 0)
return EINVAL;
return 0;
}
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
InterlockedIncrement(&cond->waiting);
LeaveCriticalSection(mutex);
WaitForSingleObject(cond->semaphore,INFINITE);
InterlockedDecrement(&cond->waiting);
EnterCriticalSection(mutex);
return 0 ;
return pthread_cond_timedwait(cond,mutex,NULL);
}
@ -58,51 +79,103 @@ int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
long timeout;
union ft64 now;
GetSystemTimeAsFileTime(&now.ft);
if( abstime != NULL )
{
GetSystemTimeAsFileTime(&now.ft);
/*
Calculate time left to abstime
- subtract start time from current time(values are in 100ns units)
- convert to millisec by dividing with 10000
*/
timeout= (long)((abstime->tv.i64 - now.i64) / 10000);
/* Don't allow the timeout to be negative */
if (timeout < 0)
timeout= 0L;
/*
Make sure the calucated timeout does not exceed original timeout
value which could cause "wait for ever" if system time changes
*/
if (timeout > abstime->max_timeout_msec)
timeout= abstime->max_timeout_msec;
}
else
{
/* No time specified; don't expire */
timeout= INFINITE;
}
/*
Calculate time left to abstime
- subtract start time from current time(values are in 100ns units)
- convert to millisec by dividing with 10000
Block access if previous broadcast hasn't finished.
This is just for safety and should normally not
affect the total time spent in this function.
*/
timeout= (long)((abstime->tv.i64 - now.i64) / 10000);
WaitForSingleObject(cond->broadcast_block_event, INFINITE);
/* Don't allow the timeout to be negative */
if (timeout < 0)
timeout= 0L;
EnterCriticalSection(&cond->lock_waiting);
cond->waiting++;
LeaveCriticalSection(&cond->lock_waiting);
/*
Make sure the calucated timeout does not exceed original timeout
value which could cause "wait for ever" if system time changes
*/
if (timeout > abstime->max_timeout_msec)
timeout= abstime->max_timeout_msec;
InterlockedIncrement(&cond->waiting);
LeaveCriticalSection(mutex);
result= WaitForSingleObject(cond->semaphore,timeout);
InterlockedDecrement(&cond->waiting);
result= WaitForMultipleObjects(2, cond->events, FALSE, timeout);
EnterCriticalSection(&cond->lock_waiting);
cond->waiting--;
if (cond->waiting == 0 && result == (WAIT_OBJECT_0+BROADCAST))
{
/*
We're the last waiter to be notified or to stop waiting, so
reset the manual event.
*/
/* Close broadcast gate */
ResetEvent(cond->events[BROADCAST]);
/* Open block gate */
SetEvent(cond->broadcast_block_event);
}
LeaveCriticalSection(&cond->lock_waiting);
EnterCriticalSection(mutex);
return result == WAIT_TIMEOUT ? ETIMEDOUT : 0;
}
int pthread_cond_signal(pthread_cond_t *cond)
{
long prev_count;
if (cond->waiting)
ReleaseSemaphore(cond->semaphore,1,&prev_count);
EnterCriticalSection(&cond->lock_waiting);
if(cond->waiting > 0)
SetEvent(cond->events[SIGNAL]);
LeaveCriticalSection(&cond->lock_waiting);
return 0;
}
int pthread_cond_broadcast(pthread_cond_t *cond)
{
long prev_count;
if (cond->waiting)
ReleaseSemaphore(cond->semaphore,cond->waiting,&prev_count);
return 0 ;
EnterCriticalSection(&cond->lock_waiting);
/*
The mutex protect us from broadcasting if
there isn't any thread waiting to open the
block gate after this call has closed it.
*/
if(cond->waiting > 0)
{
/* Close block gate */
ResetEvent(cond->broadcast_block_event);
/* Open broadcast gate */
SetEvent(cond->events[BROADCAST]);
}
LeaveCriticalSection(&cond->lock_waiting);
return 0;
}