Files
mars-flaim/ftk/src/ftksem.cpp

1338 lines
30 KiB
C++

//------------------------------------------------------------------------------
// Desc: This file contains mutex and semaphore functions
//
// Tabs: 3
//
// Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com
//
// $Id: ftksem.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $
//------------------------------------------------------------------------------
#include "ftksys.h"
/****************************************************************************
Desc:
****************************************************************************/
typedef struct
{
F_MUTEX hMutex;
F_NOTIFY_LIST_ITEM * pNotifyList;
FLMUINT uiWriteThread;
FLMINT iRefCnt;
} F_RWLOCK_IMP;
/****************************************************************************
Desc:
****************************************************************************/
#if (defined( FLM_UNIX) || defined( FLM_LIBC_NLM)) && !defined( FLM_SOLARIS)
typedef struct
{
pthread_mutex_t lock;
pthread_cond_t cond;
int count;
} sema_t;
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_WIN)
typedef struct
{
HANDLE hWinSem;
FLMATOMIC uiSignalCount;
} sema_t;
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_WIN)
RCODE FLMAPI f_mutexCreate(
F_MUTEX * phMutex)
{
f_assert( phMutex != NULL);
if( (*phMutex = (F_MUTEX)malloc( sizeof( F_INTERLOCK))) == F_MUTEX_NULL)
{
return( RC_SET( NE_FLM_MEM));
}
((F_INTERLOCK *)(*phMutex))->locked = 0;
#ifdef FLM_DEBUG
((F_INTERLOCK *)(*phMutex))->uiThreadId = 0;
((F_INTERLOCK *)(*phMutex))->lockedCount = 0;
((F_INTERLOCK *)(*phMutex))->waitCount = 0;
#endif
return( NE_FLM_OK);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_WIN)
void FLMAPI f_mutexDestroy(
F_MUTEX * phMutex)
{
f_assert( phMutex != NULL);
if (*phMutex != F_MUTEX_NULL)
{
free( *phMutex);
*phMutex = F_MUTEX_NULL;
}
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if (defined( FLM_UNIX) || defined( FLM_LIBC_NLM)) && !defined( FLM_SOLARIS)
RCODE FLMAPI f_mutexCreate(
F_MUTEX * phMutex)
{
RCODE rc = NE_FLM_OK;
pthread_mutexattr_t * pMutexAttr = NULL;
f_assert( phMutex != NULL);
// NOTE: Cannot call f_alloc because the memory initialization needs
// to be able to set up mutexes.
if ((*phMutex = (F_MUTEX)malloc(
sizeof( pthread_mutex_t))) == F_MUTEX_NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
#if defined( FLM_DEBUG) && defined( FLM_LINUX)
{
pthread_mutexattr_t mutexAttr;
if( !pthread_mutexattr_init( &mutexAttr))
{
pMutexAttr = &mutexAttr;
pthread_mutexattr_settype( pMutexAttr, PTHREAD_MUTEX_ERRORCHECK_NP);
}
}
#endif
if( pthread_mutex_init( (pthread_mutex_t *)*phMutex, pMutexAttr) != 0)
{
// NOTE: Cannot call f_free because we had to use malloc up above due
// to the fact that the memory subsystem uses a mutex before itis
// completely ready to go.
free( *phMutex);
*phMutex = F_MUTEX_NULL;
rc = RC_SET( NE_FLM_COULD_NOT_CREATE_MUTEX);
goto Exit;
}
Exit:
if( pMutexAttr)
{
pthread_mutexattr_destroy( pMutexAttr);
}
return( rc);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_SOLARIS)
RCODE FLMAPI f_mutexCreate(
F_MUTEX * phMutex)
{
RCODE rc = NE_FLM_OK;
lwp_mutex_t defaultMutex = DEFAULTMUTEX;
f_assert( phMutex != NULL);
// NOTE: Cannot call f_alloc because the memory initialization needs
// to be able to set up mutexes.
if ((*phMutex = (F_MUTEX)malloc( sizeof( lwp_mutex_t))) == F_MUTEX_NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
f_memcpy( *phMutex, &defaultMutex, sizeof( lwp_mutex_t));
Exit:
return( rc);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if (defined( FLM_UNIX) || defined( FLM_LIBC_NLM)) && !defined( FLM_SOLARIS)
void FLMAPI f_mutexDestroy(
F_MUTEX * phMutex)
{
f_assert( phMutex != NULL);
if (*phMutex != F_MUTEX_NULL)
{
pthread_mutex_destroy( (pthread_mutex_t *)*phMutex);
// NOTE: Cannot call f_free because we had to use malloc up above due
// to the fact that the memory subsystem uses a mutex before it is
// completely ready to go.
free( *phMutex);
*phMutex = F_MUTEX_NULL;
}
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_SOLARIS)
void FLMAPI f_mutexDestroy(
F_MUTEX * phMutex)
{
f_assert( phMutex != NULL);
if (*phMutex != F_MUTEX_NULL)
{
free( *phMutex);
*phMutex = F_MUTEX_NULL;
}
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if (defined( FLM_UNIX) || defined( FLM_LIBC_NLM)) && !defined( FLM_SOLARIS)
void FLMAPI f_mutexLock(
F_MUTEX hMutex)
{
(void)pthread_mutex_lock( (pthread_mutex_t *)hMutex);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_SOLARIS)
void FLMAPI f_mutexLock(
F_MUTEX hMutex)
{
for( ;;)
{
if( _lwp_mutex_lock( (lwp_mutex_t *)hMutex) == 0)
{
break;
}
}
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if (defined( FLM_UNIX) || defined( FLM_LIBC_NLM)) && !defined( FLM_SOLARIS)
void FLMAPI f_mutexUnlock(
F_MUTEX hMutex)
{
(void)pthread_mutex_unlock( (pthread_mutex_t *)hMutex);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_SOLARIS)
void FLMAPI f_mutexUnlock(
F_MUTEX hMutex)
{
_lwp_mutex_unlock( (lwp_mutex_t *)hMutex);
}
#endif
#undef f_assertMutexLocked
#undef f_assertMutexNotLocked
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_UNIX) || defined( FLM_NLM)
void FLMAPI f_assertMutexLocked(
F_MUTEX)
{
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_UNIX) || defined( FLM_NLM)
void FLMAPI f_assertMutexNotLocked(
F_MUTEX)
{
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_WIN
void FLMAPI f_assertMutexLocked(
F_MUTEX hMutex)
{
#ifdef FLM_DEBUG
f_assert( ((F_INTERLOCK *)hMutex)->locked == 1);
f_assert( ((F_INTERLOCK *)hMutex)->uiThreadId == _threadid);
#else
F_UNREFERENCED_PARM( hMutex);
#endif
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_WIN
void FLMAPI f_assertMutexNotLocked(
F_MUTEX hMutex)
{
#ifdef FLM_DEBUG
f_assert( ((F_INTERLOCK *)hMutex)->uiThreadId != _threadid);
#else
F_UNREFERENCED_PARM( hMutex);
#endif
}
#endif
/*************************************************************************
Desc:
*************************************************************************/
#if defined( FLM_RING_ZERO_NLM)
RCODE FLMAPI f_mutexCreate(
F_MUTEX * phMutex)
{
if( (*phMutex = (F_MUTEX)kMutexAlloc( (BYTE *)"FTK_MUTEX")) == F_MUTEX_NULL)
{
return( RC_SET( NE_FLM_MEM));
}
return( NE_FLM_OK);
}
#endif
/*************************************************************************
Desc:
*************************************************************************/
#if defined( FLM_RING_ZERO_NLM)
void FLMAPI f_mutexDestroy(
F_MUTEX * phMutex)
{
if (*phMutex != F_MUTEX_NULL)
{
(void)kMutexFree( (MUTEX)(*phMutex));
*phMutex = F_MUTEX_NULL;
}
}
#endif
/*************************************************************************
Desc:
*************************************************************************/
#if defined( FLM_RING_ZERO_NLM)
void FLMAPI f_mutexLock(
F_MUTEX hMutex)
{
(void)kMutexLock( (MUTEX)hMutex);
}
#endif
/*************************************************************************
Desc:
*************************************************************************/
#if defined( FLM_RING_ZERO_NLM)
void FLMAPI f_mutexUnlock(
F_MUTEX hMutex)
{
(void)kMutexUnlock( (MUTEX)hMutex);
}
#endif
/****************************************************************************
Desc: Initializes a semaphore handle on UNIX
****************************************************************************/
#if (defined( FLM_UNIX) || defined( FLM_LIBC_NLM)) && !defined( FLM_SOLARIS)
FINLINE int sema_init(
sema_t * pSem)
{
int iErr = 0;
if( (iErr = pthread_mutex_init( &pSem->lock, NULL)) < 0)
{
goto Exit;
}
if( (iErr = pthread_cond_init( &pSem->cond, NULL)) < 0)
{
pthread_mutex_destroy( &pSem->lock);
goto Exit;
}
pSem->count = 0;
Exit:
return( iErr);
}
#endif
/****************************************************************************
Desc: Frees a semaphore handle on UNIX
****************************************************************************/
#if (defined( FLM_UNIX) || defined( FLM_LIBC_NLM)) && !defined( FLM_SOLARIS)
FINLINE void sema_destroy(
sema_t * pSem)
{
pthread_mutex_destroy( &pSem->lock);
pthread_cond_destroy( &pSem->cond);
}
#endif
/****************************************************************************
Desc: Waits for a semaphore to be signaled on UNIX
****************************************************************************/
#if (defined( FLM_UNIX) || defined( FLM_LIBC_NLM)) && !defined( FLM_SOLARIS)
FINLINE int _sema_wait(
sema_t * pSem)
{
int iErr = 0;
pthread_mutex_lock( &pSem->lock);
while( !pSem->count)
{
if( (iErr = pthread_cond_wait( &pSem->cond, &pSem->lock)) != 0)
{
if( iErr == EINTR)
{
iErr = 0;
}
else
{
f_assert( 0);
goto Exit;
}
}
}
pSem->count--;
f_assert( pSem->count >= 0);
Exit:
pthread_mutex_unlock( &pSem->lock);
return( iErr);
}
#endif
/****************************************************************************
Desc: Waits for a semaphore to be signaled on Solaris
****************************************************************************/
#if defined( FLM_SOLARIS)
FINLINE int _sema_wait(
sema_t * pSem)
{
int iErr = 0;
for( ;;)
{
if( (iErr = sema_wait( pSem)) != 0)
{
if( iErr == EINTR)
{
iErr = 0;
continue;
}
else
{
f_assert( 0);
goto Exit;
}
}
break;
}
Exit:
return( iErr);
}
#endif
/****************************************************************************
Desc: Waits a specified number of milliseconds for a semaphore
to be signaled on UNIX
****************************************************************************/
#if (defined( FLM_UNIX) || defined( FLM_LIBC_NLM)) && !defined( FLM_SOLARIS)
FINLINE int _sema_timedwait(
sema_t * pSem,
unsigned int msecs)
{
int iErr = 0;
struct timeval now;
struct timespec abstime;
// If timeout is F_WAITFOREVER, do sem_wait.
if( msecs == F_WAITFOREVER)
{
iErr = _sema_wait( pSem);
return( iErr);
}
gettimeofday( &now, NULL);
abstime.tv_sec = now.tv_sec + ((msecs) ? (msecs / 1000) : 0);
abstime.tv_nsec = ( now.tv_usec + ((msecs % 1000) * 1000)) * 1000;
pthread_mutex_lock( &pSem->lock);
Restart:
while( !pSem->count)
{
if( (iErr = pthread_cond_timedwait( &pSem->cond,
&pSem->lock, &abstime)) != 0)
{
if( iErr == EINTR)
{
iErr = 0;
goto Restart;
}
goto Exit;
}
}
pSem->count--;
f_assert( pSem->count >= 0);
Exit:
pthread_mutex_unlock( &pSem->lock);
return( iErr);
}
#endif
/****************************************************************************
Desc: Waits a specified number of milliseconds for a semaphore
to be signaled on UNIX
****************************************************************************/
#if defined( FLM_SOLARIS)
FINLINE int _sema_timedwait(
sema_t * pSem,
unsigned int msecs)
{
int iErr = 0;
// If timeout is F_WAITFOREVER, do sem_wait.
if( msecs == F_WAITFOREVER)
{
iErr = _sema_wait( pSem);
return( iErr);
}
for( ;;)
{
if( (iErr = sema_trywait( pSem)) != 0)
{
if( iErr == EINTR)
{
iErr = 0;
}
f_sleep( f_min( msecs, 10));
msecs -= f_min( msecs, 10);
if( !msecs)
{
iErr = -1;
goto Exit;
}
continue;
}
}
Exit:
return( iErr);
}
#endif
/****************************************************************************
Desc: Signals a semaphore on UNIX
****************************************************************************/
#if (defined( FLM_UNIX) || defined( FLM_LIBC_NLM)) && !defined( FLM_SOLARIS)
int sema_signal(
sema_t * pSem)
{
pthread_mutex_lock( &pSem->lock);
pSem->count++;
f_assert( pSem->count > 0);
pthread_cond_signal( &pSem->cond);
pthread_mutex_unlock( &pSem->lock);
return( 0);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
RCODE f_semCreate(
F_SEM * phSem)
{
RCODE rc = NE_FLM_OK;
f_assert( phSem != NULL);
if( RC_BAD( rc = f_alloc( sizeof( sema_t), phSem)))
{
goto Exit;
}
#if defined( FLM_SOLARIS)
if( sema_init( (sema_t *)*phSem, 0, USYNC_THREAD, NULL) < 0)
#else
if( sema_init( (sema_t *)*phSem) < 0)
#endif
{
f_free( phSem);
*phSem = F_SEM_NULL;
rc = RC_SET( NE_FLM_COULD_NOT_CREATE_SEMAPHORE);
goto Exit;
}
Exit:
return( rc);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
void f_semDestroy(
F_SEM * phSem)
{
f_assert( phSem != NULL);
if (*phSem != F_SEM_NULL)
{
sema_destroy( (sema_t *)*phSem);
f_free( phSem);
*phSem = F_SEM_NULL;
}
}
#endif
/****************************************************************************
Desc: Get the lock on a semaphore - p operation
****************************************************************************/
#if defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
RCODE f_semWait(
F_SEM hSem,
FLMUINT uiTimeout)
{
RCODE rc = NE_FLM_OK;
f_assert( hSem != F_SEM_NULL);
// Catch the F_WAITFOREVER flag so we can directly call _sema_wait
// instead of passing F_WAITFOREVER through to _sema_timedwait.
// Note that on AIX the datatype of the uiTimeout (in the timespec
// struct) is surprisingly a signed int, which makes this catch
// essential.
if( uiTimeout == F_WAITFOREVER)
{
if( _sema_wait( (sema_t *)hSem))
{
rc = RC_SET( NE_FLM_ERROR_WAITING_ON_SEMAPHORE);
}
}
else
{
if( _sema_timedwait( (sema_t *)hSem, (unsigned int)uiTimeout))
{
rc = RC_SET( NE_FLM_WAIT_TIMEOUT);
}
}
return( rc);
}
#endif
/****************************************************************************
Desc: Get the lock on a semaphore - p operation
****************************************************************************/
#if defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
void FLMAPI f_semSignal(
F_SEM hSem)
{
#if defined( FLM_SOLARIS)
sema_post( (sema_t *)hSem);
#else
sema_signal( (sema_t *)hSem);
#endif
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
FLMUINT FLMAPI f_semGetSignalCount(
F_SEM hSem)
{
return( (FLMUINT)((sema_t *)hSem)->count);
}
#endif
/*************************************************************************
Desc:
*************************************************************************/
#if defined( FLM_RING_ZERO_NLM)
RCODE FLMAPI f_semCreate(
F_SEM * phSem)
{
if( (*phSem = (F_SEM)kSemaphoreAlloc( (BYTE *)"FTK_SEM", 0)) == F_SEM_NULL)
{
return( RC_SET( NE_FLM_MEM));
}
return( NE_FLM_OK);
}
#endif
/*************************************************************************
Desc:
*************************************************************************/
#if defined( FLM_RING_ZERO_NLM)
void FLMAPI f_semDestroy(
F_SEM * phSem)
{
if (*phSem != F_SEM_NULL)
{
(void)kSemaphoreFree( (SEMAPHORE)(*phSem));
*phSem = F_SEM_NULL;
}
}
#endif
/*************************************************************************
Desc:
*************************************************************************/
#if defined( FLM_RING_ZERO_NLM)
RCODE FLMAPI f_semWait(
F_SEM hSem,
FLMUINT uiTimeout)
{
RCODE rc = NE_FLM_OK;
if( uiTimeout == F_WAITFOREVER)
{
if( kSemaphoreWait( (SEMAPHORE)hSem) != 0)
{
rc = RC_SET( NE_FLM_ERROR_WAITING_ON_SEMAPHORE);
}
}
else
{
if( kSemaphoreTimedWait( (SEMAPHORE)hSem, (UINT)uiTimeout) != 0)
{
rc = RC_SET( NE_FLM_WAIT_TIMEOUT);
}
}
return( rc);
}
#endif
/*************************************************************************
Desc:
*************************************************************************/
#if defined( FLM_RING_ZERO_NLM)
void FLMAPI f_semSignal(
F_SEM hSem)
{
(void)kSemaphoreSignal( (SEMAPHORE)hSem);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_RING_ZERO_NLM)
FLMUINT FLMAPI f_semGetSignalCount(
F_SEM hSem)
{
return( (FLMUINT)kSemaphoreExamineCount( (SEMAPHORE)hSem));
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_WIN
void FLMAPI f_mutexLock(
F_MUTEX hMutex)
{
F_INTERLOCK * pInterlock = (F_INTERLOCK *)hMutex;
#ifdef FLM_DEBUG
if( pInterlock->locked)
{
f_assert( pInterlock->uiThreadId != _threadid);
}
#endif
while( f_atomicExchange( &pInterlock->locked, 1) != 0)
{
#ifdef FLM_DEBUG
f_atomicInc( &pInterlock->waitCount);
#endif
Sleep( 0);
}
#ifdef FLM_DEBUG
f_assert( pInterlock->uiThreadId == 0);
pInterlock->uiThreadId = _threadid;
f_atomicInc( &pInterlock->lockedCount);
#endif
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_WIN
void FLMAPI f_mutexUnlock(
F_MUTEX hMutex)
{
F_INTERLOCK * pInterlock = (F_INTERLOCK *)hMutex;
f_assert( pInterlock->locked == 1);
#ifdef FLM_DEBUG
f_assert( pInterlock->uiThreadId == _threadid);
pInterlock->uiThreadId = 0;
#endif
f_atomicExchange( &pInterlock->locked, 0);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_WIN
RCODE FLMAPI f_semCreate(
F_SEM * phSem)
{
RCODE rc = NE_FLM_OK;
sema_t * pSem = NULL;
f_assert( phSem != NULL);
f_assert( *phSem == F_SEM_NULL);
if( RC_BAD( rc = f_calloc( sizeof( sema_t), &pSem)))
{
goto Exit;
}
if( (pSem->hWinSem = CreateSemaphore( (LPSECURITY_ATTRIBUTES)NULL,
0, 10000, NULL )) == NULL)
{
rc = RC_SET( NE_FLM_COULD_NOT_CREATE_SEMAPHORE);
}
*phSem = pSem;
pSem = NULL;
Exit:
if( pSem)
{
if( pSem->hWinSem)
{
CloseHandle( pSem->hWinSem);
}
f_free( &pSem);
}
return( rc);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_WIN
void FLMAPI f_semDestroy(
F_SEM * phSem)
{
sema_t * pSem = (sema_t *)(*phSem);
if( pSem)
{
if( pSem->hWinSem)
{
CloseHandle( pSem->hWinSem);
}
f_free( &pSem);
}
*phSem = F_SEM_NULL;
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_WIN
RCODE FLMAPI f_semWait(
F_SEM hSem,
FLMUINT uiTimeout)
{
DWORD dwStatus;
for( ;;)
{
dwStatus = WaitForSingleObjectEx( ((sema_t *)hSem)->hWinSem,
uiTimeout, true);
if( dwStatus == WAIT_OBJECT_0)
{
f_atomicDec( &((sema_t *)hSem)->uiSignalCount);
return( NE_FLM_OK);
}
if( dwStatus == WAIT_IO_COMPLETION)
{
continue;
}
break;
}
return( RC_SET( NE_FLM_WAIT_TIMEOUT));
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_WIN
void FLMAPI f_semSignal(
F_SEM hSem)
{
f_atomicInc( &((sema_t *)hSem)->uiSignalCount);
(void)ReleaseSemaphore( ((sema_t *)hSem)->hWinSem, 1, NULL);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
#ifdef FLM_WIN
FLMUINT FLMAPI f_semGetSignalCount(
F_SEM hSem)
{
return( ((sema_t *)hSem)->uiSignalCount);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
FSTATIC void f_rwlockNotify(
F_RWLOCK_IMP * pReadWriteLock)
{
F_NOTIFY_LIST_ITEM * pNotify = pReadWriteLock->pNotifyList;
FLMBOOL bFoundWriter = FALSE;
f_assertMutexLocked( pReadWriteLock->hMutex);
while( pNotify && !bFoundWriter)
{
F_SEM hSem;
*(pNotify->pRc) = NE_FLM_OK;
hSem = pNotify->hSem;
bFoundWriter = (FLMBOOL)((FLMINT)pNotify->pvData);
pNotify = pNotify->pNext;
f_semSignal( hSem);
}
pReadWriteLock->pNotifyList = pNotify;
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_rwlockCreate(
F_RWLOCK * phReadWriteLock)
{
RCODE rc = NE_FLM_OK;
F_RWLOCK_IMP * pReadWriteLock = NULL;
if( RC_BAD( rc = f_calloc( sizeof( F_RWLOCK_IMP), &pReadWriteLock)))
{
goto Exit;
}
pReadWriteLock->hMutex = F_MUTEX_NULL;
if( RC_BAD( rc = f_mutexCreate( &pReadWriteLock->hMutex)))
{
goto Exit;
}
*phReadWriteLock = (F_RWLOCK)pReadWriteLock;
pReadWriteLock = NULL;
Exit:
if( pReadWriteLock)
{
f_rwlockDestroy( (F_RWLOCK *)&pReadWriteLock);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
void FLMAPI f_rwlockDestroy(
F_RWLOCK * phReadWriteLock)
{
F_RWLOCK_IMP * pReadWriteLock = (F_RWLOCK_IMP *)*phReadWriteLock;
if( pReadWriteLock)
{
f_assert( !pReadWriteLock->pNotifyList);
if( pReadWriteLock->hMutex != F_MUTEX_NULL)
{
f_mutexDestroy( &pReadWriteLock->hMutex);
}
f_free( &pReadWriteLock);
}
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_rwlockAcquire(
F_RWLOCK hReadWriteLock,
F_SEM hSem,
FLMBOOL bWriter)
{
RCODE rc = NE_FLM_OK;
F_RWLOCK_IMP * pReadWriteLock = (F_RWLOCK_IMP *)hReadWriteLock;
FLMBOOL bMutexLocked = FALSE;
f_mutexLock( pReadWriteLock->hMutex);
bMutexLocked = TRUE;
if( bWriter)
{
if( pReadWriteLock->iRefCnt != 0)
{
rc = f_notifyWait( pReadWriteLock->hMutex, hSem, (void *)((FLMINT)bWriter),
&pReadWriteLock->pNotifyList);
}
if( RC_OK( rc))
{
f_assert( !pReadWriteLock->iRefCnt);
pReadWriteLock->iRefCnt = -1;
pReadWriteLock->uiWriteThread = f_threadId();
}
}
else
{
if( pReadWriteLock->iRefCnt < 0 || pReadWriteLock->pNotifyList)
{
rc = f_notifyWait( pReadWriteLock->hMutex, hSem, (void *)((FLMINT)bWriter),
&pReadWriteLock->pNotifyList);
}
if( RC_OK( rc))
{
pReadWriteLock->iRefCnt++;
}
}
f_assert( RC_BAD( rc) || pReadWriteLock->iRefCnt);
if( bMutexLocked)
{
f_mutexUnlock( pReadWriteLock->hMutex);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_rwlockPromote(
F_RWLOCK hReadWriteLock,
F_SEM hSem)
{
RCODE rc = NE_FLM_OK;
F_RWLOCK_IMP * pReadWriteLock = (F_RWLOCK_IMP *)hReadWriteLock;
FLMBOOL bMutexLocked = FALSE;
f_mutexLock( pReadWriteLock->hMutex);
bMutexLocked = TRUE;
if( pReadWriteLock->iRefCnt <= 0)
{
rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP);
goto Exit;
}
pReadWriteLock->iRefCnt--;
if( pReadWriteLock->iRefCnt != 0)
{
rc = f_notifyWait( pReadWriteLock->hMutex, hSem, (void *)TRUE,
&pReadWriteLock->pNotifyList);
}
if( RC_OK( rc))
{
f_assert( !pReadWriteLock->iRefCnt);
pReadWriteLock->iRefCnt = -1;
pReadWriteLock->uiWriteThread = f_threadId();
}
Exit:
f_assert( RC_BAD( rc) || pReadWriteLock->iRefCnt);
if( bMutexLocked)
{
f_mutexUnlock( pReadWriteLock->hMutex);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_rwlockTryAcquire(
F_RWLOCK hReadWriteLock,
FLMBOOL bWriter)
{
RCODE rc = NE_FLM_OK;
F_RWLOCK_IMP * pReadWriteLock = (F_RWLOCK_IMP *)hReadWriteLock;
f_mutexLock( pReadWriteLock->hMutex);
if( bWriter)
{
if( pReadWriteLock->iRefCnt != 0)
{
rc = RC_SET( NE_FLM_WAIT_TIMEOUT);
}
else
{
pReadWriteLock->iRefCnt = -1;
pReadWriteLock->uiWriteThread = f_threadId();
}
}
else
{
if( pReadWriteLock->iRefCnt < 0 || pReadWriteLock->pNotifyList)
{
rc = RC_SET( NE_FLM_WAIT_TIMEOUT);
}
else
{
pReadWriteLock->iRefCnt++;
}
}
f_assert( RC_BAD( rc) || pReadWriteLock->iRefCnt);
f_mutexUnlock( pReadWriteLock->hMutex);
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_rwlockRelease(
F_RWLOCK hReadWriteLock)
{
RCODE rc = NE_FLM_OK;
F_RWLOCK_IMP * pReadWriteLock = (F_RWLOCK_IMP *)hReadWriteLock;
FLMBOOL bMutexLocked = FALSE;
f_mutexLock( pReadWriteLock->hMutex);
bMutexLocked = TRUE;
if( pReadWriteLock->iRefCnt > 0)
{
pReadWriteLock->iRefCnt--;
}
else if( pReadWriteLock->iRefCnt == -1)
{
f_assert( pReadWriteLock->uiWriteThread == f_threadId());
pReadWriteLock->iRefCnt = 0;
}
else
{
rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP);
goto Exit;
}
if( !pReadWriteLock->iRefCnt && pReadWriteLock->pNotifyList)
{
f_rwlockNotify( pReadWriteLock);
}
Exit:
f_assert( RC_BAD( rc) || pReadWriteLock->iRefCnt >= 0);
if( bMutexLocked)
{
f_mutexUnlock( pReadWriteLock->hMutex);
}
return( rc);
}
/****************************************************************************
Desc: This routine links a request into a notification list and
then waits to be notified that the event has occurred. The mutex
is assumed to protect the notify list.
****************************************************************************/
RCODE FLMAPI f_notifyWait(
F_MUTEX hMutex,
F_SEM hSem,
void * pvData,
F_NOTIFY_LIST_ITEM ** ppNotifyList)
{
RCODE rc = NE_FLM_OK;
RCODE tmpRc;
F_NOTIFY_LIST_ITEM stackNotify;
F_NOTIFY_LIST_ITEM * pNotify = &stackNotify;
f_assertMutexLocked( hMutex);
f_assert( pNotify != *ppNotifyList);
f_memset( &stackNotify, 0, sizeof( F_NOTIFY_LIST_ITEM));
pNotify->uiThreadId = f_threadId();
pNotify->hSem = F_SEM_NULL;
if( hSem == F_SEM_NULL)
{
if( RC_BAD( rc = f_semCreate( &pNotify->hSem)))
{
goto Exit;
}
}
else
{
pNotify->hSem = hSem;
}
pNotify->pRc = &rc;
pNotify->pvData = pvData;
pNotify->pNext = *ppNotifyList;
*ppNotifyList = pNotify;
// Unlock the mutex and wait on the semaphore
f_mutexUnlock( hMutex);
if( RC_BAD( tmpRc = f_semWait( pNotify->hSem, F_WAITFOREVER)))
{
rc = tmpRc;
}
// Free the semaphore
if( hSem != pNotify->hSem)
{
f_semDestroy( &pNotify->hSem);
}
// Relock the mutex
f_mutexLock( hMutex);
Exit:
return( rc);
}
/****************************************************************************
Desc: This routine notifies threads waiting for a pending read or write
to complete. This routine assumes that the notify list mutex
is already locked.
****************************************************************************/
void FLMAPI f_notifySignal(
F_NOTIFY_LIST_ITEM * pNotifyList,
RCODE notifyRc)
{
while( pNotifyList)
{
F_SEM hSem;
*(pNotifyList->pRc) = notifyRc;
hSem = pNotifyList->hSem;
pNotifyList = pNotifyList->pNext;
f_semSignal( hSem);
}
}