941 lines
24 KiB
C++
941 lines
24 KiB
C++
/**********************************************************************
|
|
|
|
Filename : GThreads.cpp
|
|
Content : System-independent thread-related (safe) functionality
|
|
Created : May 5, 2003
|
|
Authors : Michael Antonov
|
|
|
|
Copyright : (c) 2003 Scaleform Corp. All Rights Reserved.
|
|
|
|
Licensees may use this file in accordance with the valid Scaleform
|
|
Commercial License Agreement provided with the software.
|
|
|
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
|
|
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
|
|
|
|
**********************************************************************/
|
|
|
|
#include "GThreads.h"
|
|
|
|
#ifndef GFC_NO_THREADSUPPORT
|
|
|
|
#define GTHREADS_CPP
|
|
|
|
#include "GMemory.h"
|
|
#include "GTimer.h"
|
|
|
|
|
|
// ***** Classes implemented
|
|
|
|
class GWaitable;
|
|
class GAcquireInterface;
|
|
class GEvent;
|
|
class GSemaphore;
|
|
|
|
|
|
// ***** GWaitable interface implementation
|
|
|
|
GWaitable::GWaitable(bool enable)
|
|
: GRefCountBase<GWaitable, GStat_Default_Mem>()
|
|
{
|
|
pHandlers = enable ? new HandlerArray : 0;
|
|
// if (pHandlers)
|
|
// pHandlers->Handlers.SetSizePolicy(GArrayPolicy::Buffer_NoShrink); // Anachronism
|
|
}
|
|
|
|
|
|
// Override destructor to clean up handlers.
|
|
GWaitable::~GWaitable()
|
|
{
|
|
if (pHandlers)
|
|
pHandlers->Release();
|
|
}
|
|
|
|
bool GWaitable::AddWaitHandler(WaitHandler handler, void* pdata)
|
|
{
|
|
// Ensure that handlers and lock are allocated.
|
|
if (!pHandlers)
|
|
{
|
|
GFC_DEBUG_ERROR(1, "GWaitable::AddWaitHandler - wait handers not supported unless enabled");
|
|
return 0;
|
|
}
|
|
|
|
// NOTE: In general, it is assumed that the thread that installs or
|
|
// removes wait handlers owns a reference to GWaitable or is at least
|
|
// guaranteed that the later will not die. Thus, we do not need to
|
|
// enforce AddRef/Release semantics here; those are only
|
|
// relevant for CallHandlers.
|
|
|
|
HandlerStruct hs(handler, pdata);
|
|
GLock::Locker lock(&pHandlers->HandlersLock);
|
|
pHandlers->Handlers.PushBack(hs);
|
|
return 1;
|
|
}
|
|
|
|
bool GWaitable::RemoveWaitHandler(WaitHandler handler, void* pdata)
|
|
{
|
|
if (!pHandlers)
|
|
return 0;
|
|
|
|
HandlerStruct hs(handler, pdata);
|
|
bool result = 0;
|
|
GLock::Locker lock(&pHandlers->HandlersLock);
|
|
|
|
// Find and erase the element
|
|
for(UInt i=0; i<pHandlers->Handlers.GetSize(); i++)
|
|
if (pHandlers->Handlers[i] == hs)
|
|
{
|
|
pHandlers->Handlers.RemoveAt(i);
|
|
result = 1;
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
void GWaitable::HandlerArray::Release()
|
|
{
|
|
if ((RefCount.ExchangeAdd_NoSync(-1) - 1) == 0)
|
|
delete this;
|
|
}
|
|
|
|
void GWaitable::HandlerArray::CallWaitHandlers()
|
|
{
|
|
GLock::Locker lock(&HandlersLock);
|
|
UPInt size = Handlers.GetSize();
|
|
|
|
// No items... nothing to call
|
|
if (size == 0)
|
|
return;
|
|
|
|
if (size == 1)
|
|
{
|
|
HandlerStruct hs(Handlers[0]);
|
|
// Just one handler to call
|
|
Handlers[0].Handler(Handlers[0].pUserData);
|
|
}
|
|
else
|
|
{
|
|
// Have to copy the handlers.
|
|
HandlerArrayType handlersCopy(Handlers);
|
|
// call each one of the handlers
|
|
for(UPInt i=0; i<handlersCopy.GetSize(); i++)
|
|
handlersCopy[i].Handler(handlersCopy[i].pUserData);
|
|
}
|
|
}
|
|
|
|
void GWaitable::CallWaitHandlers()
|
|
{
|
|
if (pHandlers)
|
|
pHandlers->CallWaitHandlers();
|
|
}
|
|
|
|
// Acquires the current object based on associated acquisition interface
|
|
bool GWaitable::Acquire(UInt delay)
|
|
{
|
|
GWaitable* pw = this;
|
|
return GAcquireInterface::AcquireOneOfMultipleObjects(&pw, 1, delay) == 0;
|
|
}
|
|
|
|
|
|
// Handler used to wait for a single object to become signaled
|
|
struct GWaitable_SingleWaitData
|
|
{
|
|
GWaitable* pWaitable;
|
|
GEvent* pEvent;
|
|
|
|
GWaitable_SingleWaitData(GWaitable* pwaitable, GEvent* pevent)
|
|
: pWaitable(pwaitable), pEvent(pevent)
|
|
{ }
|
|
};
|
|
|
|
void GWaitable_SingleWaitHandler(void* hdata)
|
|
{
|
|
GWaitable_SingleWaitData* pdata = (GWaitable_SingleWaitData*)hdata;
|
|
if (pdata->pWaitable->IsSignaled())
|
|
pdata->pEvent->PulseEvent();
|
|
}
|
|
|
|
|
|
// Wait for the current object to be signaled
|
|
bool GWaitable::Wait(UInt delay)
|
|
{
|
|
if (IsSignaled())
|
|
return 1;
|
|
if (delay == 0)
|
|
return 0;
|
|
|
|
// Event that we will be waiting for
|
|
GEvent event;
|
|
GWaitable_SingleWaitData waitData(this, &event);
|
|
// Install handler
|
|
if (!AddWaitHandler(GWaitable_SingleWaitHandler, &waitData))
|
|
return 0;
|
|
if (IsSignaled())
|
|
{
|
|
RemoveWaitHandler(GWaitable_SingleWaitHandler, &waitData);
|
|
return 1;
|
|
}
|
|
// What about atomic transition from check to wait ?? Is it necessary here?
|
|
|
|
// Wait for the event
|
|
// We need to time the delays in between the functions so the wait is correct
|
|
bool result = 0;
|
|
UInt64 dt = 0;
|
|
UInt adjustedDelay = delay;
|
|
if (delay != GFC_WAIT_INFINITE)
|
|
dt = GTimer::GetTicks();
|
|
|
|
// Wait on the event
|
|
while (event.Wait(adjustedDelay))
|
|
{
|
|
// Acquire all, if possible
|
|
if (IsSignaled())
|
|
{
|
|
result = 1;
|
|
break;
|
|
}
|
|
|
|
// Failed to acquire; adjust the delay and keep waiting
|
|
if (delay != GFC_WAIT_INFINITE)
|
|
{
|
|
UInt delta = (UInt)(GTimer::GetTicks() - dt);
|
|
// If time has passed, fail
|
|
if (delta >= delay)
|
|
break;
|
|
adjustedDelay = delay - delta;
|
|
}
|
|
}
|
|
|
|
// Remove the handler
|
|
RemoveWaitHandler(GWaitable_SingleWaitHandler, &waitData);
|
|
return result;
|
|
}
|
|
|
|
// Always signaled by default
|
|
bool GWaitable::IsSignaled() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
// Just return an acquire interface that is always true
|
|
GAcquireInterface* GWaitable::GetAcquireInterface()
|
|
{
|
|
return GDefaultAcquireInterface::GetDefaultAcquireInterface();
|
|
}
|
|
|
|
|
|
|
|
// *** Acquire Interface implementation
|
|
|
|
|
|
GDefaultAcquireInterface* GDefaultAcquireInterface::GetDefaultAcquireInterface()
|
|
{
|
|
static GDefaultAcquireInterface di;
|
|
return &di;
|
|
}
|
|
|
|
|
|
bool GAcquireInterface::CanAcquire()
|
|
{
|
|
// By default, the resource can always be acquired
|
|
return 1;
|
|
}
|
|
|
|
// Try to acquire the resource, return 1 if succeeded.
|
|
bool GAcquireInterface::TryAcquire()
|
|
{
|
|
// Default acquire does nothing, so it always succeeds
|
|
return 1;
|
|
}
|
|
// Complete resource acquisition. Should not fail in general.
|
|
bool GAcquireInterface::TryAcquireCommit()
|
|
{
|
|
return 1;
|
|
}
|
|
// Cancel resource acquisition. Should not fail in general.
|
|
bool GAcquireInterface::TryAcquireCancel()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
struct GAcquireInterface_AcquireDesc
|
|
{
|
|
// Objects waiting
|
|
GWaitable** pWaitList;
|
|
UInt WaitCount;
|
|
// Event to signal
|
|
GEvent* pEvent;
|
|
// Acquire list, if any
|
|
GAcquireInterface** pAcquireList;
|
|
|
|
// Simple constructor
|
|
GAcquireInterface_AcquireDesc(GWaitable** pwaitList, UInt waitCount, GEvent *pevent, GAcquireInterface** pacquireList = 0)
|
|
{
|
|
pWaitList = pwaitList;
|
|
WaitCount = waitCount;
|
|
pEvent = pevent;
|
|
pAcquireList = pacquireList;
|
|
}
|
|
|
|
// Handler add/remove functions
|
|
bool AddHandlers(GWaitable::WaitHandler handler);
|
|
void RemoveHandlers(GWaitable::WaitHandler handler);
|
|
};
|
|
|
|
// Structure that incorporates a simple acquire list
|
|
struct GAcquireInterface_AcquireList
|
|
{
|
|
GAcquireInterface** pList;
|
|
GAcquireInterface* StaticList[32];
|
|
|
|
GAcquireInterface_AcquireList(GWaitable** pwaitList, UInt waitCount)
|
|
{
|
|
// Allocate & fill in acquire list
|
|
pList = (waitCount>32) ? (GAcquireInterface**)
|
|
GALLOC(sizeof(GAcquireInterface*) * waitCount, GStat_Default_Mem) : StaticList;
|
|
for(UInt i=0; i<waitCount; i++)
|
|
pList[i] = pwaitList[i]->GetAcquireInterface();
|
|
}
|
|
|
|
~GAcquireInterface_AcquireList()
|
|
{
|
|
if (pList != StaticList)
|
|
GFREE(pList);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
bool GAcquireInterface_AcquireDesc::AddHandlers(GWaitable::WaitHandler handler)
|
|
{
|
|
// Install handlers on all waitable objects
|
|
for(UInt i=0; i<WaitCount; i++)
|
|
{
|
|
// Add a handler
|
|
if (!pWaitList[i]->AddWaitHandler(handler, this))
|
|
{
|
|
while(i > 0)
|
|
pWaitList[--i]->RemoveWaitHandler(handler, this);
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void GAcquireInterface_AcquireDesc::RemoveHandlers(GWaitable::WaitHandler handler)
|
|
{
|
|
// Remove handlers from all waitable objects
|
|
for(UInt i=0; i<WaitCount; i++)
|
|
pWaitList[i]->RemoveWaitHandler(handler, this);
|
|
}
|
|
|
|
|
|
void GAcquireInterface_MultipleWaitHandler(void* pdata)
|
|
{
|
|
GAcquireInterface_AcquireDesc* pdesc = (GAcquireInterface_AcquireDesc*)pdata;
|
|
|
|
// If at least a single object cannot be acquired, fail
|
|
for(UInt i=0; i<pdesc->WaitCount; i++)
|
|
if (!pdesc->pAcquireList[i]->CanAcquire())
|
|
return;
|
|
// All resources can be acquired, signal the event
|
|
pdesc->pEvent->PulseEvent();
|
|
}
|
|
|
|
bool GAcquireInterface_TryAcquireAll(GAcquireInterface** pacquireList, UInt waitCount)
|
|
{
|
|
UInt i;
|
|
// First try to acquire all objects, see if it succeeds
|
|
for(i=0; i<waitCount; i++)
|
|
if (!pacquireList[i]->TryAcquire())
|
|
break;
|
|
// If acquisition succeeded, commit and we are done
|
|
if (i == waitCount)
|
|
{
|
|
for(i=0; i<waitCount; i++)
|
|
pacquireList[i]->TryAcquireCommit();
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise cancel all acquisitions that worked
|
|
while(i > 0)
|
|
pacquireList[--i]->TryAcquireCancel();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Static function to acquire multiple objects simultaneously
|
|
bool GAcquireInterface::AcquireMultipleObjects(GWaitable** pwaitList, UInt waitCount, UInt delay)
|
|
{
|
|
GAcquireInterface_AcquireList acquireList(pwaitList, waitCount);
|
|
if (GAcquireInterface_TryAcquireAll(acquireList.pList, waitCount))
|
|
return 1;
|
|
// If delay was 0, just fail
|
|
if (delay == 0)
|
|
return 0;
|
|
|
|
// Create a event & acquire descriptor
|
|
GEvent event;
|
|
GAcquireInterface_AcquireDesc acquireDesc(pwaitList, waitCount, &event, acquireList.pList);
|
|
|
|
// Install handlers on all waitable objects
|
|
if (!acquireDesc.AddHandlers(GAcquireInterface_MultipleWaitHandler))
|
|
return 0;
|
|
|
|
// Must check state again, in case AddHandlers was too
|
|
// late and state of the object already changed before its call.
|
|
if (GAcquireInterface_TryAcquireAll(acquireList.pList, waitCount))
|
|
{
|
|
acquireDesc.RemoveHandlers(GAcquireInterface_MultipleWaitHandler);
|
|
return 1;
|
|
}
|
|
|
|
// We need to time the delays in between the functions so the wait is correct
|
|
bool result = 0;
|
|
UInt64 dt = 0;
|
|
UInt adjustedDelay = delay;
|
|
if (delay != GFC_WAIT_INFINITE)
|
|
dt = GTimer::GetTicks();
|
|
|
|
// Wait on the event
|
|
while (event.Wait(adjustedDelay))
|
|
{
|
|
// Acquire all, if possible
|
|
if (GAcquireInterface_TryAcquireAll(acquireList.pList, waitCount))
|
|
{
|
|
result = 1;
|
|
break;
|
|
}
|
|
|
|
// Failed to acquire; adjust the delay and keep waiting
|
|
if (delay != GFC_WAIT_INFINITE)
|
|
{
|
|
UInt delta = (UInt)(GTimer::GetTicks() - dt);
|
|
// If time has passed, fail
|
|
if (delta >= delay)
|
|
break;
|
|
adjustedDelay = delay - delta;
|
|
}
|
|
}
|
|
|
|
// Remove the wait handlers, done
|
|
acquireDesc.RemoveHandlers(GAcquireInterface_MultipleWaitHandler);
|
|
return result;
|
|
}
|
|
|
|
|
|
// Wait handler for only one of multiple objects to acquire
|
|
void GAcquireInterface_OneOfMultipleWaitHandler(void* pdata)
|
|
{
|
|
GAcquireInterface_AcquireDesc* pdesc = (GAcquireInterface_AcquireDesc*)pdata;
|
|
|
|
// It would be nice to know which waitable caused this, to avoid doing a
|
|
// loop in most cases; but that data can not be passed by caller for object
|
|
// lifetime reasons (by the time handlers are called waitable could have
|
|
// already been killed by another thread).
|
|
|
|
// If at least a single object can be acquired, signal the event
|
|
for(UInt i=0; i<pdesc->WaitCount; i++)
|
|
if (pdesc->pWaitList[i]->GetAcquireInterface()->CanAcquire())
|
|
{
|
|
pdesc->pEvent->PulseEvent();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Function to try acquire one of the objects in the least
|
|
SInt GAcquireInterface_TryAcquireOne(GWaitable** pwaitList, UInt waitCount)
|
|
{
|
|
UInt i;
|
|
// First try to at least one object
|
|
for(i=0; i<waitCount; i++)
|
|
{
|
|
GAcquireInterface* pi = pwaitList[i]->GetAcquireInterface();
|
|
if (pi->TryAcquire())
|
|
{
|
|
pi->TryAcquireCommit();
|
|
// Return the index acquired
|
|
return (SInt) i;
|
|
}
|
|
}
|
|
// Return -1 for fail
|
|
return -1;
|
|
}
|
|
|
|
|
|
// Static function to acquire one object out of a list
|
|
SInt GAcquireInterface::AcquireOneOfMultipleObjects(GWaitable** pwaitList, UInt waitCount, UInt delay)
|
|
{
|
|
SInt result = GAcquireInterface_TryAcquireOne(pwaitList, waitCount);
|
|
|
|
// If acquired, we are done
|
|
if (result !=-1)
|
|
return result;
|
|
// If delay was 0, just fail
|
|
if (delay == 0)
|
|
return -1;
|
|
|
|
// Create a event & acquire descriptor
|
|
GEvent event;
|
|
GAcquireInterface_AcquireDesc acquireDesc(pwaitList, waitCount, &event);
|
|
// Install handlers on all waitable objects
|
|
if (!acquireDesc.AddHandlers(GAcquireInterface_OneOfMultipleWaitHandler))
|
|
return 0;
|
|
|
|
// Must check state again, in case AddHandlers was too
|
|
// late and state of the object already changed before its call.
|
|
result = GAcquireInterface_TryAcquireOne(pwaitList, waitCount);
|
|
if (result != -1)
|
|
{
|
|
acquireDesc.RemoveHandlers(GAcquireInterface_OneOfMultipleWaitHandler);
|
|
return result;
|
|
}
|
|
|
|
// We need to time the delays in between the functions so the wait is correct
|
|
UInt64 dt = 0;
|
|
UInt adjustedDelay = delay;
|
|
if (delay != GFC_WAIT_INFINITE)
|
|
dt = GTimer::GetTicks();
|
|
|
|
// Wait on the event
|
|
if ((result = GAcquireInterface_TryAcquireOne(pwaitList, waitCount)) == -1)
|
|
while (event.Wait(adjustedDelay))
|
|
{
|
|
// Acquire one, if possible
|
|
if ((result = GAcquireInterface_TryAcquireOne(pwaitList, waitCount)) != -1)
|
|
break;
|
|
|
|
// Failed to acquire; adjust the delay and keep waiting
|
|
if (delay != GFC_WAIT_INFINITE)
|
|
{
|
|
// Milliseconds that already passed
|
|
UInt delta = (UInt)(GTimer::GetTicks() - dt);
|
|
// If time has passed, fail
|
|
if (delta >= delay)
|
|
break;
|
|
adjustedDelay = delay - delta;
|
|
}
|
|
}
|
|
|
|
// Remove the wait handlers
|
|
acquireDesc.RemoveHandlers(GAcquireInterface_OneOfMultipleWaitHandler);
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
// ***** GEvent implementation
|
|
|
|
// Constructor/destructor
|
|
GEvent::GEvent(bool setInitially, bool multiWait)
|
|
: GWaitable(multiWait)
|
|
{
|
|
// NOTE: RefCount mode already thread-safe for all waitables.
|
|
State = setInitially;
|
|
Temporary = 0;
|
|
}
|
|
|
|
GEvent::~GEvent()
|
|
{
|
|
}
|
|
|
|
// Wait on an event condition until it is set
|
|
bool GEvent::Wait(UInt delay)
|
|
{
|
|
GMutex::Locker lock(&StateMutex);
|
|
|
|
// Do the correct amount of waiting
|
|
switch(delay)
|
|
{
|
|
case 0:
|
|
break;
|
|
|
|
case GFC_WAIT_INFINITE:
|
|
while(!State)
|
|
StateWaitCondition.Wait(&StateMutex);
|
|
break;
|
|
|
|
default:
|
|
if (!State)
|
|
StateWaitCondition.Wait(&StateMutex, delay);
|
|
break;
|
|
}
|
|
|
|
bool state = State;
|
|
// Take care of temporary 'pulsing' of a state
|
|
if (Temporary)
|
|
{
|
|
Temporary = 0;
|
|
State = 0;
|
|
}
|
|
return state;
|
|
}
|
|
|
|
|
|
// Set en event, releasing objects waiting on it
|
|
void GEvent::SetEvent()
|
|
{
|
|
StateMutex.Lock();
|
|
State = 1;
|
|
Temporary = 0;
|
|
StateWaitCondition.NotifyAll();
|
|
|
|
// Must AddRef to handlers before unlock.
|
|
CallableHandlers handlers;
|
|
GetCallableHandlers(&handlers);
|
|
StateMutex.Unlock();
|
|
handlers.CallWaitHandlers();
|
|
}
|
|
|
|
// Reset an event, un-signaling it
|
|
void GEvent::ResetEvent()
|
|
{
|
|
GMutex::Locker lock(&StateMutex);
|
|
State = 0;
|
|
Temporary = 0;
|
|
}
|
|
|
|
// Set and then reset an event once a waiter is released, will release only one waiter
|
|
// If threads are already waiting, one will be notified and released
|
|
// If threads are not waiting, the event is set until the first thread comes in
|
|
void GEvent::PulseEvent()
|
|
{
|
|
StateMutex.Lock();
|
|
State = 1;
|
|
Temporary = 1;
|
|
StateWaitCondition.Notify();
|
|
|
|
CallableHandlers handlers;
|
|
GetCallableHandlers(&handlers);
|
|
StateMutex.Unlock();
|
|
handlers.CallWaitHandlers();
|
|
}
|
|
|
|
// Signaled override
|
|
bool GEvent::IsSignaled() const
|
|
{
|
|
//GMutex::Locker lock(&StateMutex);
|
|
return State;
|
|
}
|
|
|
|
GAcquireInterface* GEvent::GetAcquireInterface()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
// Acquire interface implementation
|
|
bool GEvent::CanAcquire()
|
|
{
|
|
return IsSignaled();
|
|
}
|
|
bool GEvent::TryAcquire()
|
|
{
|
|
return IsSignaled();
|
|
}
|
|
bool GEvent::TryAcquireCommit()
|
|
{
|
|
GMutex::Locker lock(&StateMutex);
|
|
if (Temporary)
|
|
{
|
|
Temporary = 0;
|
|
State = 0;
|
|
}
|
|
return 1;
|
|
}
|
|
bool GEvent::TryAcquireCancel()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
// ***** GSemaphore implementation
|
|
|
|
GSemaphore::GSemaphore(SInt maxValue, bool multiWait)
|
|
: GWaitable(multiWait)
|
|
{
|
|
// NOTE: RefCount mode already thread-safe for all waitables.
|
|
MaxValue = maxValue;
|
|
Value = 0;
|
|
}
|
|
|
|
GSemaphore::~GSemaphore()
|
|
{
|
|
}
|
|
|
|
// Get current value and max
|
|
SInt GSemaphore::GetMaxValue() const
|
|
{
|
|
return MaxValue;
|
|
}
|
|
SInt GSemaphore::GetValue() const
|
|
{
|
|
return Value;
|
|
}
|
|
SInt GSemaphore::GetAvailable() const
|
|
{
|
|
return MaxValue - Value;
|
|
}
|
|
|
|
// *** Actions
|
|
|
|
// Obtains multiple value of a semaphore
|
|
// Returns 0 if query failed (count > max value error or timeout)
|
|
bool GSemaphore::ObtainSemaphore(SInt count, UInt delay)
|
|
{
|
|
if (count > MaxValue)
|
|
return 0;
|
|
|
|
GMutex::Locker lock(&ValueMutex);
|
|
|
|
// If value in range, done
|
|
if ((Value + count) <= MaxValue)
|
|
{
|
|
Value += count;
|
|
return 1;
|
|
}
|
|
|
|
// If delay == 0, return immediately
|
|
if (delay == 0)
|
|
return 0;
|
|
|
|
// If infinite wait, special loop
|
|
if (delay == GFC_WAIT_INFINITE)
|
|
{
|
|
while ((Value + count) > MaxValue)
|
|
ValueWaitCondition.Wait(&ValueMutex);
|
|
Value += count;
|
|
return 1;
|
|
}
|
|
|
|
// More complicated version of wait needs to take time into account
|
|
UInt adjustedDelay = delay;
|
|
UInt64 dt = GTimer::GetTicks();
|
|
|
|
while(ValueWaitCondition.Wait(&ValueMutex, adjustedDelay))
|
|
{
|
|
// If value in range, done
|
|
if ((Value + count) <= MaxValue)
|
|
{
|
|
Value += count;
|
|
return 1;
|
|
}
|
|
|
|
UInt delta = (UInt)(GTimer::GetTicks() - dt);
|
|
// If time has passed, fail
|
|
if (delta >= delay)
|
|
break;
|
|
adjustedDelay = delay - delta;
|
|
}
|
|
|
|
// Time-out occurred
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Release semaphore values
|
|
// Returns success code
|
|
bool GSemaphore::ReleaseSemaphore(SInt count)
|
|
{
|
|
if (count == 0)
|
|
return 1;
|
|
|
|
ValueMutex.Lock();
|
|
|
|
// Always assign final result
|
|
// If available() is called, it does not maker whether we get the old or new
|
|
// value, but we should never get an intermediate state (such as a negative value)
|
|
if ((Value - count) >= 0)
|
|
Value = Value - count;
|
|
else
|
|
{
|
|
// Value decremented too far!!
|
|
Value = 0;
|
|
}
|
|
|
|
if (count == 1)
|
|
ValueWaitCondition.Notify();
|
|
else
|
|
ValueWaitCondition.NotifyAll();
|
|
|
|
CallableHandlers handlers;
|
|
GetCallableHandlers(&handlers);
|
|
ValueMutex.Unlock();
|
|
handlers.CallWaitHandlers();
|
|
return 1;
|
|
}
|
|
|
|
|
|
// *** Operators
|
|
|
|
// Postfix increment/decrement, return value before operation
|
|
SInt GSemaphore::operator ++ (SInt)
|
|
{
|
|
GMutex::Locker lock(&ValueMutex);
|
|
// Wait for the value available
|
|
while (Value >= MaxValue)
|
|
ValueWaitCondition.Wait(&ValueMutex);
|
|
Value++;
|
|
return Value;
|
|
}
|
|
|
|
SInt GSemaphore::operator -- (SInt)
|
|
{
|
|
ValueMutex.Lock();
|
|
if (Value > 0)
|
|
Value--;
|
|
ValueWaitCondition.Notify();
|
|
|
|
CallableHandlers handlers;
|
|
GetCallableHandlers(&handlers);
|
|
ValueMutex.Unlock();
|
|
handlers.CallWaitHandlers();
|
|
return Value;
|
|
}
|
|
|
|
// Postfix increment/decrement, return value before operation
|
|
SInt GSemaphore::operator += (SInt count)
|
|
{
|
|
GMutex::Locker lock(&ValueMutex);
|
|
// Wait for the value available
|
|
while ((Value + count) > MaxValue)
|
|
ValueWaitCondition.Wait(&ValueMutex);
|
|
Value += count;
|
|
return Value;
|
|
}
|
|
|
|
SInt GSemaphore::operator -= (SInt count)
|
|
{
|
|
ValueMutex.Lock();
|
|
|
|
if ((Value - count) >= 0)
|
|
Value = Value - count;
|
|
else
|
|
Value = 0;
|
|
|
|
ValueWaitCondition.NotifyAll();
|
|
CallableHandlers handlers;
|
|
GetCallableHandlers(&handlers);
|
|
ValueMutex.Unlock();
|
|
handlers.CallWaitHandlers();
|
|
return Value;
|
|
}
|
|
|
|
// Acquire interface implementation
|
|
// Default
|
|
bool GSemaphore::CanAcquire()
|
|
{ return GetAvailable() > 0; }
|
|
bool GSemaphore::TryAcquire()
|
|
{ return ObtainSemaphore(1, 0); }
|
|
bool GSemaphore::TryAcquireCommit()
|
|
{ return 1; }
|
|
bool GSemaphore::TryAcquireCancel()
|
|
{ return ReleaseSemaphore(1); }
|
|
|
|
|
|
// GWaitable implementation
|
|
bool GSemaphore::IsSignaled() const
|
|
{ return GetAvailable() > 0; }
|
|
GAcquireInterface* GSemaphore::GetAcquireInterface()
|
|
{ return this; }
|
|
|
|
|
|
|
|
// Waitable increment semaphore acquire interface
|
|
// Can be used to acquire multiple values of a semaphore at the same time
|
|
class GSemaphoreWaitableIncrement : public GWaitable, public GAcquireInterface
|
|
{
|
|
GSemaphore *pSemaphore;
|
|
SInt Count;
|
|
|
|
public:
|
|
// Constructor/Destructor
|
|
GSemaphoreWaitableIncrement() : GWaitable(1)
|
|
{ pSemaphore = 0; Count = 0; }
|
|
GSemaphoreWaitableIncrement(GSemaphore *psemaphore, SInt count);
|
|
~GSemaphoreWaitableIncrement();
|
|
|
|
|
|
// Acquire interface implementation
|
|
// Default
|
|
virtual bool CanAcquire();
|
|
virtual bool TryAcquire();
|
|
virtual bool TryAcquireCommit();
|
|
virtual bool TryAcquireCancel();
|
|
|
|
// GWaitable implementation
|
|
virtual bool IsSignaled() const;
|
|
virtual GAcquireInterface* GetAcquireInterface();
|
|
|
|
// GInterface - no implementation
|
|
virtual void AddRef() { }
|
|
virtual void Release(UInt flags=0) { GUNUSED(flags); }
|
|
};
|
|
|
|
// Wait handler install in the owned semaphore in order to notify us
|
|
void GSemaphoreWaitableIncrement_SemaphoreWaitHandler(void* hdata)
|
|
{
|
|
GWaitable* pincrement = (GWaitable*) hdata;
|
|
pincrement->CallWaitHandlers();
|
|
}
|
|
|
|
GSemaphoreWaitableIncrement::GSemaphoreWaitableIncrement(GSemaphore *psemaphore, SInt count)
|
|
: GWaitable(1)
|
|
{
|
|
pSemaphore = psemaphore;
|
|
Count = count;
|
|
// Install a handler so that we get informed about semaphore state changes
|
|
pSemaphore->AddWaitHandler(GSemaphoreWaitableIncrement_SemaphoreWaitHandler, this);
|
|
}
|
|
GSemaphoreWaitableIncrement::~GSemaphoreWaitableIncrement()
|
|
{
|
|
pSemaphore->RemoveWaitHandler(GSemaphoreWaitableIncrement_SemaphoreWaitHandler, this);
|
|
}
|
|
|
|
// Acquisition interface
|
|
bool GSemaphoreWaitableIncrement::CanAcquire()
|
|
{
|
|
if (!pSemaphore) return 0;
|
|
return pSemaphore->GetAvailable() >= Count;
|
|
}
|
|
bool GSemaphoreWaitableIncrement::TryAcquire()
|
|
{
|
|
if (!pSemaphore) return 0;
|
|
return pSemaphore->ObtainSemaphore(Count, 0);
|
|
}
|
|
bool GSemaphoreWaitableIncrement::TryAcquireCommit()
|
|
{
|
|
if (!pSemaphore) return 0;
|
|
return 1;
|
|
}
|
|
bool GSemaphoreWaitableIncrement::TryAcquireCancel()
|
|
{
|
|
if (!pSemaphore) return 0;
|
|
return pSemaphore->ReleaseSemaphore(Count);
|
|
}
|
|
|
|
// GWaitable implementation
|
|
bool GSemaphoreWaitableIncrement::IsSignaled() const
|
|
{ if (!pSemaphore) return 0;
|
|
return pSemaphore->GetAvailable() >= Count; }
|
|
GAcquireInterface* GSemaphoreWaitableIncrement::GetAcquireInterface()
|
|
{ return this; }
|
|
|
|
|
|
|
|
// Create a semaphore acquisition object that would increment a semaphore by a user-defined count
|
|
// This object can be passed to AcquireMultipleObjects functions, and will acquire several values form a semaphore
|
|
// This object must be released before the semaphore
|
|
GWaitable* GSemaphore::CreateWaitableIncrement(SInt count)
|
|
{
|
|
return new GSemaphoreWaitableIncrement(this, count);
|
|
}
|
|
|
|
|
|
#endif // GFC_NO_THREADSUPPORT
|