Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

339 lines
11 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Implements a semaphore thread synchronization class.
/////////////////////////////////////////////////////////////////////////////
#ifndef EATHREAD_EATHREAD_SEMAPHORE_H
#define EATHREAD_EATHREAD_SEMAPHORE_H
#include <eathread/internal/config.h>
#include <eathread/eathread.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
///////////////////////////////////////////////////////////////////////////////
// EATHREAD_USE_SYNTHESIZED_SEMAPHORE
//
// Defined as 0 or 1. Defined as 1 if the OS provides no native semaphore support.
//
#ifndef EATHREAD_USE_SYNTHESIZED_SEMAPHORE
#define EATHREAD_USE_SYNTHESIZED_SEMAPHORE 0
#endif
///////////////////////////////////////////////////////////////////////////////
// EATHREAD_FAST_MS_SEMAPHORE_ENABLED
//
// Defined as 0 or 1.
// Enables the usage of a faster intra-process semaphore on Microsoft platforms.
// By faster we mean that it is typically 10x or more faster.
// Has the downside that it is not interchangeable with the SEMAPHORE built-in
// type and it's behaviour won't be strictly identical.
// Even if this option is enabled, you can still get the built-in behaviour
// of Microsoft semaphores by specifying the semaphore as inter-process.
//
#ifndef EATHREAD_FAST_MS_SEMAPHORE_ENABLED
#define EATHREAD_FAST_MS_SEMAPHORE_ENABLED 1
#endif
/////////////////////////////////////////////////////////////////////////
/// EASemaphoreData
///
/// This is used internally by class Semaphore.
/// Todo: Consider moving this declaration into a platform-specific
/// header file.
///
#if !EA_THREADS_AVAILABLE
struct EASemaphoreData
{
volatile int mnCount;
int mnMaxCount;
EASemaphoreData();
};
#elif EATHREAD_USE_SYNTHESIZED_SEMAPHORE
#include <eathread/eathread_condition.h>
#include <eathread/eathread_mutex.h>
#include <eathread/eathread_atomic.h>
struct EASemaphoreData
{
EA::Thread::Condition mCV;
EA::Thread::Mutex mMutex;
EA::Thread::AtomicInt32 mnCount;
int mnMaxCount;
bool mbValid;
EASemaphoreData();
};
#elif defined(EA_PLATFORM_APPLE)
#include <mach/semaphore.h>
#include <eathread/eathread_atomic.h>
struct EASemaphoreData
{
semaphore_t mSemaphore;
EA::Thread::AtomicInt32 mnCount;
int mnMaxCount;
bool mbIntraProcess;
EASemaphoreData();
};
#elif defined(EA_PLATFORM_SONY)
#include <kernel/semaphore.h>
#include <eathread/eathread_atomic.h>
#include <eathread/internal/timings.h>
struct EASemaphoreData
{
SceKernelSema mSemaphore;
int mnMaxCount;
EA::Thread::AtomicInt32 mnCount;
EASemaphoreData();
};
#elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE
#include <semaphore.h>
#include <eathread/eathread_atomic.h>
#if defined(EA_PLATFORM_WINDOWS)
#ifdef CreateSemaphore
#undef CreateSemaphore // Windows #defines CreateSemaphore to CreateSemaphoreA or CreateSemaphoreW.
#endif
#endif
struct EASemaphoreData
{
sem_t mSemaphore;
EA::Thread::AtomicInt32 mnCount;
int mnMaxCount;
bool mbIntraProcess;
EASemaphoreData();
};
#elif defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE
#ifdef CreateSemaphore
#undef CreateSemaphore // Windows #defines CreateSemaphore to CreateSemaphoreA or CreateSemaphoreW.
#endif
struct EATHREADLIB_API EASemaphoreData
{
void* mhSemaphore; // We use void* instead of HANDLE in order to avoid #including windows.h. HANDLE is typedef'd to (void*) on all Windows-like platforms.
int32_t mnCount; // Number of available posts. Under the fast semaphore pathway, a negative value means there are waiters.
int32_t mnCancelCount; // Used by fast semaphore logic. Is the deferred cancel count.
int32_t mnMaxCount; //
bool mbIntraProcess; // Used under Windows, which can have multiple processes. Always true for XBox.
EASemaphoreData();
void UpdateCancelCount(int32_t n);
};
#endif
/////////////////////////////////////////////////////////////////////////
namespace EA
{
namespace Thread
{
/// SemaphoreParameters
/// Specifies semaphore settings.
struct EATHREADLIB_API SemaphoreParameters
{
int mInitialCount; /// Initial available count
int mMaxCount; /// Max possible count. Defaults to INT_MAX.
bool mbIntraProcess; /// True if the semaphore is intra-process, else inter-process.
char mName[16]; /// Semaphore name, applicable only to platforms that recognize named synchronization objects.
SemaphoreParameters(int initialCount = 0, bool bIntraProcess = true, const char* pName = NULL);
};
/// class Semaphore
/// A semaphore is an object which has an associated count which is >= 0 and
/// a value > 0 means that a thread can 'grab' the semaphore and decrement its
/// value by one. A value of 0 means that threads must wait until another thread
/// 'un-grabs' the semaphore. Thus a semaphore is like a car rental agency which
/// has a limited number of cars for rent and if they are out of cars, you have
/// to wait until one of the renters returns their car.
class EATHREADLIB_API Semaphore
{
public:
enum Result{
kResultError = -1,
kResultTimeout = -2
};
/// Semaphore
/// For immediate default initialization, use no args.
/// For custom immediate initialization, supply a first argument.
/// For deferred initialization, use Semaphore(NULL, false) then later call Init.
/// For deferred initialization of an array of objects, create an empty
/// subclass whose default constructor chains back to Semaphore(NULL, false).
Semaphore(const SemaphoreParameters* pSemaphoreParameters = NULL, bool bDefaultParameters = true);
/// Semaphore
/// This is a constructor which initializes the Semaphore to a specific count
/// and intializes the other Semaphore parameters to default values. See the
/// SemaphoreParameters struct for info on these default values.
Semaphore(int initialCount);
/// ~Semaphore
/// Destroys an existing semaphore. The semaphore must not be locked
/// by any thread, otherwise the resulting behaviour is undefined.
~Semaphore();
/// Init
/// Initializes the semaphore with given parameters.
bool Init(const SemaphoreParameters* pSemaphoreParameters);
/// Wait
/// Locks the semaphore (reducing its count by one) or gives up trying to
/// lock it after a given timeout has expired. If the semaphore count is > 0
/// then the count will be reduced by one. If the semaphore count is 0, the
/// call will block until another thread unlocks it or the timeout expires.
///
/// Note that the timeout is specified in absolute time and not relative time.
///
/// Note also that due to the way thread scheduling works -- particularly in a
/// time-sliced threading environment -- that the timeout value is a hint and
/// the actual amount of time passed before the timeout occurs may be significantly
/// more or less than the specified timeout time.
///
/// Return value:
/// kResultError The semaphore could not be obtained due to error.
/// kResultTimeout The semaphore could not be obtained due to timeout.
/// >= 0 The new count for the semaphore.
///
/// It's possible that two threads waiting on the same semaphore will return
/// with a result of zero. Thus you cannot rely on the semaphore's return value
/// to ascertain which was the last thread to return from the Wait.
int Wait(const ThreadTime& timeoutAbsolute = kTimeoutNone);
/// Post
/// Increments the signalled value of the semaphore by the count.
/// Returns the available count after the operation has completed.
/// Returns kResultError upon error. A Wait is often eventually
/// followed by a corresponding Post.
/// For the case of count being greater than 1, not all platforms
/// act the same. If count results in exceeding the max count then
/// kResultError is returned. Some platforms return kResultError
/// before any of account is applied, while others return
/// kResultError after some of count has been applied.
int Post(int count = 1);
/// GetCount
/// Returns current number of available locks associated with the semaphore.
/// This is useful for debugging and for quick polling checks of the
/// status of the semaphore. This value changes over time as multiple
/// threads wait and post to the semaphore. This value cannot be trusted
/// to exactly represent the count upon its return if multiple threads are
/// using this Semaphore at the time.
int GetCount() const;
/// GetPlatformData
/// Returns the platform-specific data handle for debugging uses or
/// other cases whereby special (and non-portable) uses are required.
void* GetPlatformData()
{ return &mSemaphoreData; }
// Objects of this class are not copyable.
Semaphore(const Semaphore&) = delete;
Semaphore& operator=(const Semaphore&) = delete;
protected:
EASemaphoreData mSemaphoreData;
};
/// SemaphoreFactory
///
/// Implements a factory-based creation and destruction mechanism for class Semaphore.
/// A primary use of this would be to allow the Semaphore implementation to reside in
/// a private library while users of the class interact only with the interface
/// header and the factory. The factory provides conventional create/destroy
/// semantics which use global operator new, but also provides manual construction/
/// destruction semantics so that the user can provide for memory allocation
/// and deallocation.
class EATHREADLIB_API SemaphoreFactory
{
public:
static Semaphore* CreateSemaphore(); // Internally implemented as: return new Semaphore;
static void DestroySemaphore(Semaphore* pSemaphore); // Internally implemented as: delete pSemaphore;
static size_t GetSemaphoreSize(); // Internally implemented as: return sizeof(Semaphore);
static Semaphore* ConstructSemaphore(void* pMemory); // Internally implemented as: return new(pMemory) Semaphore;
static void DestructSemaphore(Semaphore* pSemaphore); // Internally implemented as: pSemaphore->~Semaphore();
};
} // namespace Thread
} // namespace EA
namespace EA
{
namespace Thread
{
/// class AutoSemaphore
/// An AutoSemaphore grabs the Semaphore in its constructor and posts
/// the Semaphore once in its destructor (when it goes out of scope).
class EATHREADLIB_API AutoSemaphore
{
public:
AutoSemaphore(Semaphore& semaphore)
: mSemaphore(semaphore)
{ mSemaphore.Wait(); }
~AutoSemaphore()
{ mSemaphore.Post(1); }
protected:
Semaphore& mSemaphore;
// Prevent copying by default, as copying is dangerous.
AutoSemaphore(const AutoSemaphore&);
const AutoSemaphore& operator=(const AutoSemaphore&);
};
} // namespace Thread
} // namespace EA
#endif // EATHREAD_EATHREAD_SEMAPHORE_H