semxroads-generic.cpp revision c58f1213e628a545081c70e26c6b67a841cff880
/* $Id$ */
/** @file
* IPRT Testcase - RTSemXRoads, generic implementation.
*/
/*
* Copyright (C) 2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define RTASSERT_QUIET
#include <iprt/semaphore.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
typedef struct RTSEMXROADSINTERNAL
{
/** Magic value (RTSEMXROADS_MAGIC). */
/* The state variable.
* All accesses are atomic and it bits are defined like this:
* Bits 0..14 - cNorthSouth.
* Bit 15 - Unused.
* Bits 16..31 - cEastWest.
* Bit 31 - fDirection; 0=NS, 1=EW.
* Bits 32..46 - cWaitingNS
* Bit 47 - Unused.
* Bits 48..62 - cWaitingEW
* Bit 63 - Unused.
*/
/** Per-direction data. */
struct
{
/** Indicates whether the semaphore needs resetting. */
bool volatile fNeedReset;
} aDirs[2];
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#define RTSEMXROADS_CNT_BITS 15
#define RTSEMXROADS_CNT_NS_SHIFT 0
#define RTSEMXROADS_CNT_EW_SHIFT 16
#define RTSEMXROADS_DIR_SHIFT 31
#define RTSEMXROADS_WAIT_CNT_NS_SHIFT 32
#define RTSEMXROADS_WAIT_CNT_EW_SHIFT 48
#if 0 /* debugging aid */
static struct
{
void *tsc;
bool fDir;
void *u64State;
void *u64OldState;
bool fNeedResetNS;
bool fNeedResetEW;
const char *psz;
} g_aHist[256];
do \
{ \
} while (0)
#else
#endif
{
if (!pThis)
return VERR_NO_MEMORY;
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
pThis->u32Padding = 0;
return VINF_SUCCESS;
}
}
return rc;
}
{
/*
* Validate input.
*/
if (pThis == NIL_RTSEMXROADS)
return VINF_SUCCESS;
Assert(!(ASMAtomicReadU64(&pThis->u64State) & (RTSEMXROADS_CNT_NS_MASK | RTSEMXROADS_CNT_EW_MASK)));
/*
* Invalidate the object and free up the resources.
*/
AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMXROADS_MAGIC_DEAD, RTSEMXROADS_MAGIC), VERR_INVALID_HANDLE);
return VINF_SUCCESS;
}
/**
* Internal worker for RTSemXRoadsNSEnter and RTSemXRoadsEWEnter.
*
* @returns IPRT status code.
* @param pThis The semaphore instance.
* @param fDir The direction.
* @param uCountShift The shift count for getting the count.
* @param fCountMask The mask for getting the count.
* @param uWaitCountShift The shift count for getting the wait count.
* @param fWaitCountMask The mask for getting the wait count.
*/
{
for (;;)
{
{
/* It flows in the right direction, try follow it before it changes. */
c++;
u64State &= ~fCountMask;
u64State |= c << uCountShift;
{
break;
}
}
{
/* Wrong direction, but we're alone here and can simply try switch the direction. */
{
break;
}
}
else
{
/* Add ourselves to the queue and wait for the direction to change. */
c++;
cWait++;
{
{
return VERR_SEM_DESTROYED;
break;
}
/* Decrement the wait count and maybe reset the semaphore (if we're last). */
for (;;)
{
cWait--;
u64State &= ~fWaitCountMask;
{
if (cWait == 0)
{
{
}
else
}
break;
}
}
break;
}
}
return VERR_SEM_DESTROYED;
ASMNopPause();
}
/* got it! */
Assert((ASMAtomicReadU64(&pThis->u64State) & RTSEMXROADS_DIR_MASK) == (fDir << RTSEMXROADS_DIR_SHIFT));
return VINF_SUCCESS;
}
/**
* Internal worker for RTSemXRoadsNSLeave and RTSemXRoadsEWLeave.
*
* @returns IPRT status code.
* @param pThis The semaphore instance.
* @param fDir The direction.
* @param uCountShift The shift count for getting the count.
* @param fCountMask The mask for getting the count.
*/
DECL_FORCE_INLINE(int) rtSemXRoadsLeave(RTSEMXROADSINTERNAL *pThis, uint64_t fDir, uint64_t uCountShift, uint64_t fCountMask)
{
for (;;)
{
uint64_t c;
/* The direction cannot change until we've left or we'll crash. */
Assert(c > 0);
c--;
if ( c > 0
{
/* We're not the last one across or there aren't any one waiting in the other direction. */
u64State &= ~fCountMask;
u64State |= c << uCountShift;
{
return VINF_SUCCESS;
}
}
else
{
/* Reverse the direction and signal the threads in the other direction. */
{
return VINF_SUCCESS;
}
}
ASMNopPause();
return VERR_SEM_DESTROYED;
}
}
{
/*
* Validate input.
*/
if (pThis == NIL_RTSEMXROADS)
return VINF_SUCCESS;
return rtSemXRoadsEnter(pThis, 0, RTSEMXROADS_CNT_NS_SHIFT, RTSEMXROADS_CNT_NS_MASK, RTSEMXROADS_WAIT_CNT_NS_SHIFT, RTSEMXROADS_WAIT_CNT_NS_MASK);
}
{
/*
* Validate input.
*/
if (pThis == NIL_RTSEMXROADS)
return VINF_SUCCESS;
}
{
/*
* Validate input.
*/
if (pThis == NIL_RTSEMXROADS)
return VINF_SUCCESS;
return rtSemXRoadsEnter(pThis, 1, RTSEMXROADS_CNT_EW_SHIFT, RTSEMXROADS_CNT_EW_MASK, RTSEMXROADS_WAIT_CNT_EW_SHIFT, RTSEMXROADS_WAIT_CNT_EW_MASK);
}
{
/*
* Validate input.
*/
if (pThis == NIL_RTSEMXROADS)
return VINF_SUCCESS;
}