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

342 lines
10 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Implements a lightweight mutex.
/////////////////////////////////////////////////////////////////////////////
// TODO(rparolin): Consider adding support for static thread safety analysis.
// https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
#ifndef EATHREAD_EATHREAD_MUTEX_H
#define EATHREAD_EATHREAD_MUTEX_H
#if defined(EA_COMPILER_MSVC)
#include <math.h> // #include math.h because VC++ has a header file but that requires math.h to be #included before some other headers, lest you get a warning.
#endif
#include <stddef.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
/////////////////////////////////////////////////////////////////////////
/// EAMutexData
///
/// This is used internally by class Mutex.
/// Todo: Consider moving this declaration into a platform-specific
/// header file.
///
#if !EA_THREADS_AVAILABLE
#define EA_THREAD_NONTHREADED_MUTEX 1
struct EAMutexData
{
int mnLockCount;
EAMutexData();
};
#elif EA_USE_CPP11_CONCURRENCY
EA_DISABLE_ALL_VC_WARNINGS()
#include <mutex>
EA_RESTORE_ALL_VC_WARNINGS()
#if defined EA_PLATFORM_MICROSOFT
#ifdef CreateMutex
#undef CreateMutex // Windows #defines CreateMutex to CreateMutexA or CreateMutexW.
#endif
#endif
struct EAMutexData
{
std::recursive_timed_mutex mMutex;
int mnLockCount;
#if EAT_ASSERT_ENABLED
EA::Thread::ThreadId mThreadId; // This value is only valid in debug builds.
#endif
EAMutexData();
private:
EAMutexData(const EAMutexData&);
EAMutexData& operator=(const EAMutexData&);
};
#elif defined(EA_PLATFORM_SONY)
#include <kernel.h>
#include <eathread/internal/timings.h>
struct EAMutexData
{
ScePthreadMutex mMutex;
int mnLockCount;
#if EAT_ASSERT_ENABLED
EA::Thread::ThreadId mThreadId; // This value is only valid in debug builds.
#endif
EAMutexData();
void SimulateLock(bool bLock);
};
#elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE
#include <pthread.h>
#if defined(EA_PLATFORM_WINDOWS)
#ifdef CreateMutex
#undef CreateMutex // Windows #defines CreateMutex to CreateMutexA or CreateMutexW.
#endif
#endif
struct EAMutexData
{
pthread_mutex_t mMutex;
int mnLockCount;
#if EAT_ASSERT_ENABLED
EA::Thread::ThreadId mThreadId; // This value is only valid in debug builds.
#endif
EAMutexData();
void SimulateLock(bool bLock);
};
#elif defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE
#if defined(EA_PROCESSOR_X86_64) || defined(EA_PROCESSOR_ARM64)
static const int MUTEX_PLATFORM_DATA_SIZE = 40; // CRITICAL_SECTION is 40 bytes on Win64 & ARM64.
#else
static const int MUTEX_PLATFORM_DATA_SIZE = 32; // CRITICAL_SECTION is 24 bytes on Win32, 28 bytes on XBox 360.
#endif
#ifdef CreateMutex
#undef CreateMutex // Windows #defines CreateMutex to CreateMutexA or CreateMutexW.
#endif
struct EATHREADLIB_API EAMutexData
{
uint64_t mData[MUTEX_PLATFORM_DATA_SIZE / sizeof(uint64_t)]; // Holds either CRITICAL_SECTION or HANDLE if mbIntraProcess is true or false, respectively.
int mnLockCount;
bool mbIntraProcess;
#if EAT_ASSERT_ENABLED
EA::Thread::ThreadId mThreadId; // This value is only valid in debug builds.
EA::Thread::SysThreadId mSysThreadId; // This value is only valid in debug builds.
#endif
EAMutexData();
};
#else
#define EA_THREAD_NONTHREADED_MUTEX 1
struct EAMutexData
{
int mnLockCount;
EAMutexData();
};
#endif
/////////////////////////////////////////////////////////////////////////
namespace EA
{
namespace Thread
{
/// MutexParameters
/// Specifies mutex settings.
struct EATHREADLIB_API MutexParameters
{
bool mbIntraProcess; /// True if the mutex is intra-process, else inter-process.
char mName[128]; /// Mutex name, applicable only to platforms that recognize named synchronization objects.
MutexParameters(bool bIntraProcess = true, const char* pName = NULL);
};
/// class Mutex
///
/// Mutex are assumed to always be 'recursive', meaning that a given thread
/// can lock the mutex more than once. If you want a specifically non-recursive
/// mutex, you can use a semaphore with a lock count of 1.
class EATHREADLIB_API Mutex
{
public:
enum Result
{
kResultError = -1,
kResultTimeout = -2
};
/// Mutex
/// For immediate default initialization, use no args.
/// For custom immediate initialization, supply a first argument.
/// For deferred initialization, use Mutex(NULL, false) then later call Init.
/// For deferred initialization of an array of objects, create an empty
/// subclass whose default constructor chains back to Mutex(NULL, false).
Mutex(const MutexParameters* pMutexParameters = NULL, bool bDefaultParameters = true);
/// ~Mutex
/// Destroys an existing mutex. The mutex must not be locked by any thread,
/// otherwise the resulting behaviour is undefined.
~Mutex();
/// Init
/// Initializes the mutex if not done so in the constructor.
/// This should only be called in the case that this class was constructed
/// with RWMutex(NULL, false).
bool Init(const MutexParameters* pMutexParameters);
/// Lock
/// Locks the mutex, with a timeout specified. This function will
/// return immediately if the mutex is not locked or if the calling
/// thread already has it locked at least once. If the mutex is
/// locked by another thread, this function will block until the mutex
/// is unlocked by the owning thread or until the timeout time has
/// passed. This function may return before the specified timeout has passed
/// and so should not be implicitly used as a timer. Some platforms may
/// return immediately if the timeout is specified as anything but kTimeoutNone.
///
/// 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 Error
/// kResultTimeout Timeout
/// > 0 The new lock count.
int Lock(const ThreadTime& timeoutAbsolute = EA::Thread::kTimeoutNone);
/// Unlock
/// Unlocks the mutex. The mutex must already be locked at least once by
/// the calling thread. Otherwise the behaviour is not defined.
/// Return value is the lock count value immediately upon unlock.
int Unlock();
/// GetLockCount
/// Returns the number of locks on the mutex. The return value from this
/// function is only reliable if the calling thread already has one lock on
/// the critical section. Otherwise the value could be changing as other
/// threads lock or unlock the mutex soon after the call.
/// This function is useful in debugging and asserting and useful for backing
/// out of recursive locks under the case of exceptions and other abortive
/// situations. This function will not necessarily call memory synchronization
/// primitives (e.g. ReadBarrier) itself on systems that require SMP synchronization.
int GetLockCount() const;
/// HasLock
/// Returns true if the current thread has the mutex locked.
/// This function is reliable only in a debug build whereby
/// EAT_ASSERT_ENABLED is defined to 1. This function can thus
/// only be used in debugging situations whereby you want to
/// assert that you have a mutex locked or not. To make this
/// function work in a non-debug environment would necessitate
/// adding an undesirable amount of code and data.
bool HasLock() 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 &mMutexData; }
protected:
EAMutexData mMutexData;
private:
// Objects of this class are not copyable.
Mutex(const Mutex&){}
Mutex& operator=(const Mutex&){ return *this; }
};
/// MutexFactory
///
/// Implements a factory-based creation and destruction mechanism for class Mutex.
/// A primary use of this would be to allow the Mutex 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 MutexFactory
{
public:
static Mutex* CreateMutex(); // Internally implemented as: return new Mutex;
static void DestroyMutex(Mutex* pMutex); // Internally implemented as: delete pMutex;
static size_t GetMutexSize(); // Internally implemented as: return sizeof(Mutex);
static Mutex* ConstructMutex(void* pMemory); // Internally implemented as: return new(pMemory) Mutex;
static void DestructMutex(Mutex* pMutex); // Internally implemented as: pMutex->~Mutex();
};
} // namespace Thread
} // namespace EA
namespace EA
{
namespace Thread
{
/// class AutoMutex
/// An AutoMutex locks the Mutex in its constructor and
/// unlocks the Mutex in its destructor (when it goes out of scope).
class EATHREADLIB_API AutoMutex
{
public:
inline AutoMutex(Mutex& mutex)
: mMutex(mutex)
{ mMutex.Lock(); }
inline ~AutoMutex()
{ mMutex.Unlock(); }
protected:
Mutex& mMutex;
// Prevent copying by default, as copying is dangerous.
AutoMutex(const AutoMutex&);
const AutoMutex& operator=(const AutoMutex&);
};
} // namespace Thread
} // namespace EA
#endif // EATHREAD_EATHREAD_MUTEX_H