305 lines
7.4 KiB
C
305 lines
7.4 KiB
C
/* refl.h - local one-modifier/several-user lock */
|
|
|
|
/*
|
|
* Copyright (c) 2007~2008, 2010 Wind River Systems, Inc.
|
|
*
|
|
* The right to copy, distribute, modify or otherwise make use
|
|
* of this software may be licensed only pursuant to the terms
|
|
* of an applicable Wind River license agreement.
|
|
*/
|
|
|
|
/*
|
|
modification history
|
|
--------------------
|
|
01f,29apr10,pad Moved extern C statement after include statements.
|
|
01e,05sep08,cwu add static declaration for DSHM_INLINE related function
|
|
01d,05sep07,bwa changed intLockKey into a stack variable (103613)
|
|
01c,17jun07,bwa updated following code review.
|
|
01b,03apr07,bwa added comments.
|
|
01a,15mar07,bwa written.
|
|
*/
|
|
|
|
/* This module implements a read-write lock (use-modify) with a reference
|
|
* count, used for local data structures. It is primarily used to protect
|
|
* service data structures and driver data structures that are mostly accessed
|
|
* in read mode.
|
|
*
|
|
* This type of lock should be used sparingly, since the data structure is
|
|
* relatively heavy, using two semaphores.
|
|
*/
|
|
|
|
#ifndef __INCdshmReflh
|
|
#define __INCdshmReflh
|
|
|
|
#include <dshm/dshm.h>
|
|
#include <private/semLibP.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
typedef struct __REF_LOCK
|
|
{
|
|
int nReaders; /* number of read takes */
|
|
BOOL isWriter; /* modifier wants or has the lock */
|
|
SEMAPHORE wlock; /* writer signal */
|
|
SEMAPHORE rlock; /* reader signal */
|
|
} _REF_LOCK;
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* reflInit - initialize a reference count lock
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
static DSHM_INLINE void reflInit (_REF_LOCK *pLock)
|
|
{
|
|
pLock->nReaders = 0;
|
|
pLock->isWriter = FALSE;
|
|
(void)semBInit (&pLock->rlock, SEM_Q_FIFO, SEM_EMPTY);
|
|
(void)semBInit (&pLock->wlock, SEM_Q_FIFO, SEM_EMPTY);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* reflUseTake - obtain a reference count lock in 'use' mode
|
|
*
|
|
* This routine tries to obtain the lock in 'use' mode. It can block the current
|
|
* thread of control if the lock is currently taken in 'modify' mode. After
|
|
* being unblocked, the thread will retry taking the lock.
|
|
*
|
|
* RETURNS: TRUE, always
|
|
*/
|
|
|
|
static DSHM_INLINE BOOL reflUseTake (_REF_LOCK *pLock)
|
|
{
|
|
FOREVER
|
|
{
|
|
int intLockKey = DSHM_INTLOCK ();
|
|
if (!pLock->isWriter)
|
|
{
|
|
++pLock->nReaders;
|
|
DSHM_INTUNLOCK (intLockKey);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
DSHM_INTUNLOCK (intLockKey);
|
|
semTake (&pLock->rlock, WAIT_FOREVER);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* reflUseGive - give a lock that was previously taken in 'use' mode.
|
|
*
|
|
* This routine gives a lock taken in 'use' mode. Care must be taken to only
|
|
* call this routine if the lock had previously been acquired in 'use' mode,
|
|
* since no verification is done. If this is not observed, the lock will end up
|
|
* in an incorrect state, will most probably cause deadlocks.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
static DSHM_INLINE void reflUseGive (_REF_LOCK *pLock)
|
|
{
|
|
int intLockKey = DSHM_INTLOCK ();
|
|
if (--pLock->nReaders == 0)
|
|
{
|
|
if (pLock->isWriter)
|
|
{
|
|
DSHM_INTUNLOCK (intLockKey);
|
|
semGive (&pLock->wlock);
|
|
return;
|
|
}
|
|
}
|
|
DSHM_INTUNLOCK (intLockKey);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* reflModTake - obtain a lock in 'modify' mode
|
|
*
|
|
* This routine tries to obtain the lock in 'modify' mode. If the lock is
|
|
* currently held in this mode, the operation will fail. If the lock is
|
|
* held in 'use' mode, the thread will block until the lock is released by all
|
|
* threads owning it.
|
|
*
|
|
* When a thread tries to obtain the lock in 'modify' mode, all further attempts
|
|
* to take it in 'use' mode will block until the lock is acquired and released
|
|
* in 'modify' mode.
|
|
*
|
|
* RETURNS: TRUE on success, FALSE on failure.
|
|
*/
|
|
|
|
static DSHM_INLINE BOOL reflModTake (_REF_LOCK *pLock)
|
|
{
|
|
int intLockKey = DSHM_INTLOCK ();
|
|
/* someone is already making changes */
|
|
if (pLock->isWriter)
|
|
{
|
|
DSHM_INTUNLOCK (intLockKey);
|
|
return FALSE;
|
|
}
|
|
pLock->isWriter = TRUE;
|
|
if (pLock->nReaders)
|
|
{
|
|
DSHM_INTUNLOCK(intLockKey);
|
|
semTake (&pLock->wlock, WAIT_FOREVER);
|
|
return TRUE;
|
|
}
|
|
DSHM_INTUNLOCK(intLockKey);
|
|
return TRUE;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* reflDemote - turn a lock held in 'modify' mode to a lock held in 'use' mode
|
|
*
|
|
* This routine demotes a 'modify' lock to a 'use' lock. If threads are waiting
|
|
* to acquire it in 'use' mode, they will awaken.
|
|
*
|
|
* Care must be taken to only
|
|
* call this routine if the lock had previously been acquired in 'modify' mode,
|
|
* since no verification is done. If this is not observed, the lock will end up
|
|
* in an incorrect state, will most probably cause deadlocks.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
static DSHM_INLINE void reflDemote (_REF_LOCK *pLock)
|
|
{
|
|
int intLockKey = DSHM_INTLOCK ();
|
|
pLock->isWriter = FALSE;
|
|
pLock->nReaders = 1;
|
|
DSHM_INTUNLOCK (intLockKey);
|
|
semFlush (&pLock->rlock);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* reflModGive - release a lock that was held in 'modify' mode
|
|
*
|
|
* Give a lock held in 'modify' mode. Threads waiting to acquire the lock in
|
|
* 'use' mode will be unblocked and will obtain the lock.
|
|
*
|
|
* Care must be taken to only
|
|
* call this routine if the lock had previously been acquired in 'modify' mode,
|
|
* since no verification is done. If this is not observed, the lock will end up
|
|
* in an incorrect state, will most probably cause deadlocks.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
static DSHM_INLINE void reflModGive (_REF_LOCK *pLock)
|
|
{
|
|
int intLockKey = DSHM_INTLOCK ();
|
|
pLock->isWriter = FALSE;
|
|
DSHM_INTUNLOCK(intLockKey);
|
|
semFlush (&pLock->rlock);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* __INCdshmReflh */
|
|
|
|
/* test cases */
|
|
#if 0
|
|
void __helper (_REF_LOCK *pLock)
|
|
{
|
|
printf ("__helper spawned\n");
|
|
if (reflModTake (pLock))
|
|
{
|
|
printf ("__helper: got lock\n");
|
|
}
|
|
else
|
|
{
|
|
printf ("error helper 1\n");
|
|
return;
|
|
}
|
|
|
|
if (!reflModTake (pLock))
|
|
{
|
|
printf ("__helper: failed taking lock twice, GOOD\n");
|
|
}
|
|
else
|
|
{
|
|
printf ("error helper 2\n");
|
|
return;
|
|
}
|
|
|
|
printf ("__helper: releasing\n");
|
|
reflModGive (pLock);
|
|
printf ("__helper: released\n");
|
|
|
|
printf ("__helper: done\n");
|
|
}
|
|
|
|
void __test (void)
|
|
{
|
|
_REF_LOCK lock;
|
|
reflInit (&lock);
|
|
|
|
if (reflUseTake (&lock))
|
|
printf ("got ref lock once\n");
|
|
else
|
|
{
|
|
printf ("error 1\n");
|
|
return;
|
|
}
|
|
|
|
if (reflUseTake (&lock))
|
|
printf ("got ref lock twice\n");
|
|
else
|
|
{
|
|
printf ("error 2\n");
|
|
return;
|
|
}
|
|
|
|
if (reflUseTake (&lock))
|
|
printf ("got ref lock thrice\n");
|
|
else
|
|
{
|
|
printf ("error 3\n");
|
|
return;
|
|
}
|
|
|
|
reflUseGive (&lock);
|
|
printf ("gave ref lock once\n");
|
|
|
|
reflUseGive (&lock);
|
|
printf ("gave ref lock twice\n");
|
|
|
|
{
|
|
int prio;
|
|
taskPriorityGet (taskIdSelf(), &prio);
|
|
printf ("spawning task @ priority %d\n", prio);
|
|
if (ERROR == taskSpawn (NULL, prio-1, 0, 20000, (FUNCPTR)__helper, (int)&lock, 0,0,0,0,0,0,0,0,0))
|
|
{
|
|
printf ("can't spawn task\n");
|
|
taskSuspend (taskIdSelf());
|
|
}
|
|
printf ("spawned helper\n");
|
|
}
|
|
|
|
reflUseGive (&lock);
|
|
printf ("gave ref lock thrice\n");
|
|
|
|
if (reflUseTake (&lock))
|
|
printf ("got ref final lock\n");
|
|
else
|
|
{
|
|
printf ("error 7\n");
|
|
return;
|
|
}
|
|
|
|
reflUseGive (&lock);
|
|
printf ("gave ref final lock\n");
|
|
}
|
|
#endif
|
|
|