mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
342 lines
10 KiB
C++
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|