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

249 lines
9.7 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Implements Posix-style barriers.
// Note that thread synchronization barriers are different from
// memory synchronization barriers (a.k.a. fences).
/////////////////////////////////////////////////////////////////////////////
#ifndef EATHREAD_EATHREAD_BARRIER_H
#define EATHREAD_EATHREAD_BARRIER_H
#include <EABase/eabase.h>
#include <eathread/eathread.h>
#if defined(EA_DLL) && defined(EA_COMPILER_MSVC)
// Suppress warning about class 'AtomicInt32' needs to have a
// dll-interface to be used by clients of class which have a templated member.
//
// These templates cannot be instantiated outside of the DLL. If you try, a
// link error will result. This compiler warning is intended to notify users
// of this.
EA_DISABLE_VC_WARNING(4251)
#endif
#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
/////////////////////////////////////////////////////////////////////////
/// EABarrierData
///
/// This is used internally by class Barrier.
/// Todo: Consider moving this declaration into a platform-specific
/// header file.
///
#if defined(EA_PLATFORM_SONY)
#include <kernel.h>
#include <eathread/internal/timings.h>
// We implement the barrier manually, as not all Posix thread implementations
// have barriers and even those that have it lack a timeout wait version.
struct EABarrierData{
ScePthreadCond mCV; // Wait for barrier.
ScePthreadMutex mMutex; // Control access to barrier.
int mnHeight; // Number of threads required.
int mnCurrent; // Current number of threads. As threads wait, this value decreases towards zero.
unsigned long mnCycle; // Cycle count.
bool mbValid; // True if valid.
EABarrierData();
};
#elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && EA_THREADS_AVAILABLE
#include <pthread.h>
// We implement the barrier manually, as not all Posix threads implemetnation
// have barrers and even those that have it lack a timeout wait version.
struct EABarrierData{
pthread_cond_t mCV; // Wait for barrier.
pthread_mutex_t mMutex; // Control access to barrier.
int mnHeight; // Number of threads required.
int mnCurrent; // Current number of threads. As threads wait, this value decreases towards zero.
unsigned long mnCycle; // Cycle count.
bool mbValid; // True if valid.
EABarrierData();
};
#else // All other platforms
#include <eathread/eathread_atomic.h>
#include <eathread/eathread_semaphore.h>
struct EATHREADLIB_API EABarrierData{
EA::Thread::AtomicInt32 mnCurrent; // Current number of threads. As threads wait, this value decreases towards zero.
int mnHeight; // Number of threads required.
EA::Thread::AtomicInt32 mnIndex; // Which semaphore we are using.
EA::Thread::Semaphore mSemaphore0; // First semaphore. We can't use an array of Semaphores, because that would
EA::Thread::Semaphore mSemaphore1; // Second semaphore. intefere with our ability to initialize them our way.
EABarrierData();
private:
// Prevent default generation of these functions by declaring but not defining them.
EABarrierData(const EABarrierData& rhs); // copy constructor
EABarrierData& operator=(const EABarrierData& rhs); // assignment operator
};
#endif
namespace EA
{
namespace Thread
{
/// BarrierParameters
/// Specifies barrier settings.
struct EATHREADLIB_API BarrierParameters
{
int mHeight; /// Barrier 'height'. Refers to number of threads which must wait before being released.
bool mbIntraProcess; /// True if the semaphore is intra-process, else inter-process.
char mName[16]; /// Barrier name, applicable only to platforms that recognize named synchronization objects.
BarrierParameters(int height = 0, bool bIntraProcess = true, const char* pName = NULL);
};
/// Barrier
/// A Barrier is a synchronization point for a set of threads. A barrier has
/// a count associated with it and threads call the wait function until the
/// given count of threads have reached the wait point. Then all threads
/// are released. The first thread released is given a special return value
/// that identifies it uniquely so that one-time work can be done.
///
/// A primary use of barriers is to spread out work between a number of threads
/// and wait until the work is complete. For example, if you want to find and
/// count all objects of a given kind in a large grid, you might have four
/// threads each work on a quadrant and wait on the barrier until all are
/// finished. This particular example is more practical on SMP systems than
/// uniprocessor systems, but there are also uniprocessor uses. It should be
/// noted, however, that a Barrier synchronizes the completion of -threads-,
/// and not necessarily the completion of -tasks-. There may or may not be
/// a direct correspondence between the two.
///
class EATHREADLIB_API Barrier
{
public:
enum Result{
kResultPrimary = 0, /// The barrier wait suceeded and this thread is the designated solitary primary thread. Similar to Posix "serial" thread.
kResultSecondary = 1, /// The barrier wait suceeded and this thread is one of the secondary threads.
kResultError = -1, /// The wait resulted in error, due to various possible reasons.
kResultTimeout = -2 /// The barrier wait timed out.
};
/// Barrier
/// For immediate default initialization, use no args.
/// For custom immediate initialization, supply a first argument.
/// For deferred initialization, use Barrier(NULL, false) then later call Init.
/// For deferred initialization of an array of objects, create an empty
/// subclass whose default constructor chains back to Barrier(NULL, false).
Barrier(const BarrierParameters* pBarrierParameters = NULL, bool bDefaultParameters = true);
/// Barrier
/// This is a constructor which initializes the Barrier to a specific height
/// and intializes the other Barrier parameters to default values. See the
/// BarrierParameters struct for info on these default values.
Barrier(int height);
/// ~Barrier
/// Destroys an existing Barrier. The Barrier must not be waited on
/// by any thread, otherwise the resulting behaviour is undefined.
~Barrier();
/// Init
/// Initializes the Barrier; used in cases where it cannot be initialized
/// via the constructor (as in the case with default construction or
/// array initialization.
bool Init(const BarrierParameters* pBarrierParameters);
/// Wait
/// Causes the current thread to wait until the designated number of threads have called Wait.
///
/// Returns one of enum Result.
///
/// A timeout means that the thread gives up its contribution to the height while
/// waiting for the full height to be achieved. A timeout of zero means that a thread
/// only succeeds if it is the final thread (the one which puts the height to full);
/// otherwise the call returns with a timeout.
///
/// 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.
///
Result Wait(const ThreadTime& timeoutAbsolute = kTimeoutNone);
/// GetPlatformData
/// Returns the platform-specific data handle for debugging uses or
/// other cases whereby special (and non-portable) uses are required.
void* GetPlatformData()
{ return &mBarrierData; }
protected:
EABarrierData mBarrierData;
private:
// Objects of this class are not copyable.
Barrier(const Barrier&){}
Barrier& operator=(const Barrier&){ return *this; }
};
/// BarrierFactory
///
/// Implements a factory-based creation and destruction mechanism for class Barrier.
/// A primary use of this would be to allow the Barrier 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 BarrierFactory
{
public:
static Barrier* CreateBarrier(); // Internally implemented as: return new Barrier;
static void DestroyBarrier(Barrier* pBarrier); // Internally implemented as: delete pBarrier;
static size_t GetBarrierSize(); // Internally implemented as: return sizeof(Barrier);
static Barrier* ConstructBarrier(void* pMemory); // Internally implemented as: return new(pMemory) Barrier;
static void DestructBarrier(Barrier* pBarrier); // Internally implemented as: pBarrier->~Barrier();
};
} // namespace Thread
} // namespace EA
#if defined(EA_DLL) && defined(EA_COMPILER_MSVC)
// re-enable warning(s) disabled above.
EA_RESTORE_VC_WARNING()
#endif
#endif // EATHREAD_EATHREAD_BARRIER_H