[development] removal of unused and low stakes code related to Cry-threading (#2896)

Removal highlights include:
 - File indexer (used CryThread<>) linked to long gone asset browser
 - Producer/consumer queues from CryMT
 - set/vector/CLocklessPointerQueue containers also from CryMT
 - Cry interlocked linked list and _InterlockedCompareExchange128
 - CryThread type
 - SAtomicVar types
 - CryAutoSet type
 - Various unused lock types
 -- AutoLockModify
 -- AutoLockRead
 -- CryOptionalAutoLock
 -- CryReadModifyLock
 -- CryRWLock
 -- ReadLock
 -- ReadLockCond
 -- WriteAfterReadLock
 - Misc. unused functions
 -- CryInterLockedAdd (not to be confused with CryInterlockedAdd, using a lower case "locked")
 -- CryInterlockedExchange64 (which was only defined for unix platforms)
 -- SpinLock
 -- JobSpinLock
 -- AtomicAdd
 -- JobAtomicAdd

Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com>
This commit is contained in:
Scott Romero
2021-08-06 13:23:14 -07:00
committed by GitHub
parent 4f9382e8c6
commit 9a8a411a0b
16 changed files with 1 additions and 2460 deletions
-19
View File
@@ -127,7 +127,6 @@ AZ_POP_DISABLE_WARNING
#include "Util/AutoDirectoryRestoreFileDialog.h"
#include "Util/EditorAutoLevelLoadTest.h"
#include "Util/IndexedFiles.h"
#include "AboutDialog.h"
#include <AzToolsFramework/PythonTerminal/ScriptHelpDialog.h>
@@ -1715,18 +1714,6 @@ BOOL CCryEditApp::InitInstance()
if (IsInRegularEditorMode())
{
CIndexedFiles::Create();
if (gEnv->pConsole->GetCVar("ed_indexfiles")->GetIVal())
{
Log("Started game resource files indexing...");
CIndexedFiles::StartFileIndexing();
}
else
{
Log("Game resource files indexing is disabled.");
}
// QuickAccessBar creation should be before m_pMainWnd->SetFocus(),
// since it receives the focus at creation time. It brakes MainFrame key accelerators.
m_pQuickAccessBar = new CQuickAccessBar;
@@ -2163,12 +2150,6 @@ int CCryEditApp::ExitInstance(int exitCode)
}
}
if (IsInRegularEditorMode())
{
CIndexedFiles::AbortFileIndexing();
CIndexedFiles::Destroy();
}
if (GetIEditor() && !GetIEditor()->IsInMatEditMode())
{
//Nobody seems to know in what case that kind of exit can happen so instrumented to see if it happens at all
@@ -56,7 +56,7 @@ void CPerforceSourceControl::ShowSettings()
void CPerforceSourceControl::SetSourceControlState(SourceControlState state)
{
AUTO_LOCK(g_cPerforceValues);
CryAutoLock<CryCriticalSection> lock(g_cPerforceValues);
switch (state)
{
-202
View File
@@ -1,202 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : Tagged files database for 'SmartFileOpen' dialog
#include "EditorDefs.h"
#include "IndexedFiles.h"
volatile TIntAtomic CIndexedFiles::s_bIndexingDone;
CIndexedFiles* CIndexedFiles::s_pIndexedFiles = nullptr;
bool CIndexedFiles::m_startedFileIndexing = false;
void CIndexedFiles::Initialize(const QString& path, IFileUtil::ScanDirectoryUpdateCallBack updateCB)
{
m_files.clear();
m_pathToIndex.clear();
m_tags.clear();
m_rootPath = path;
bool anyFiles = CFileUtil::ScanDirectory(path, "*.*", m_files, true, true, updateCB);
if (anyFiles == false)
{
m_files.clear();
return;
}
if (updateCB)
{
updateCB("Parsing & tagging...");
}
for (int i = 0; i < m_files.size(); ++i)
{
m_pathToIndex[m_files[i].filename] = i;
}
PrepareTagTable();
InvokeUpdateCallbacks();
}
void CIndexedFiles::AddFile(const IFileUtil::FileDesc& path)
{
assert(m_pathToIndex.find(path.filename) == m_pathToIndex.end());
m_files.push_back(path);
m_pathToIndex[path.filename] = m_files.size() - 1;
QStringList tags;
GetTags(tags, path.filename);
for (int k = 0; k < tags.size(); ++k)
{
m_tags[tags[k]].insert(m_files.size() - 1);
}
}
void CIndexedFiles::RemoveFile(const QString& path)
{
if (m_pathToIndex.find(path) == m_pathToIndex.end())
{
return;
}
std::map<QString, int>::iterator itr = m_pathToIndex.find(path);
int index = itr->second;
m_pathToIndex.erase(itr);
m_files.erase(m_files.begin() + index);
QStringList tags;
GetTags(tags, path);
for (int k = 0; k < tags.size(); ++k)
{
m_tags[tags[k]].erase(index);
}
}
void CIndexedFiles::Refresh(const QString& path, bool recursive)
{
IFileUtil::FileArray files;
bool anyFiles = CFileUtil::ScanDirectory(m_rootPath, Path::Make(path, "*.*"), files, recursive, recursive ? true : false);
if (anyFiles == false)
{
return;
}
for (int i = 0; i < files.size(); ++i)
{
if (m_pathToIndex.find(files[i].filename) == m_pathToIndex.end())
{
AddFile(files[i]);
}
}
InvokeUpdateCallbacks();
}
void CIndexedFiles::GetFilesWithTags(IFileUtil::FileArray& files, const QStringList& tags) const
{
files.clear();
if (tags.empty())
{
return;
}
int_set candidates;
TagTable::const_iterator i;
// Gets candidate files from the first tag.
for (i = m_tags.begin(); i != m_tags.end(); ++i)
{
if (i->first.startsWith(tags[0]))
{
candidates.insert(i->second.begin(), i->second.end());
}
}
// Reduces the candidates further using additional tags, if any.
for (int k = 1; k < tags.size(); ++k)
{
// Gathers the filter set.
int_set filter;
for (i = m_tags.begin(); i != m_tags.end(); ++i)
{
if (i->first.startsWith(tags[k]))
{
filter.insert(i->second.begin(), i->second.end());
}
}
// Filters the candidates using it.
for (int_set::iterator m = candidates.begin(); m != candidates.end(); )
{
if (filter.find(*m) == filter.end())
{
int_set::iterator target = m;
++m;
candidates.erase(target);
}
else
{
++m;
}
}
}
// Outputs the result.
files.reserve(candidates.size());
for (int_set::const_iterator m = candidates.begin(); m != candidates.end(); ++m)
{
files.push_back(m_files[*m]);
}
}
void CIndexedFiles::GetTags(QStringList& tags, const QString& path) const
{
tags = path.split(QRegularExpression(QStringLiteral(R"([\\/.])")), Qt::SkipEmptyParts);
}
void CIndexedFiles::GetTagsOfPrefix(QStringList& tags, const QString& prefix) const
{
tags.clear();
TagTable::const_iterator i;
for (i = m_tags.begin(); i != m_tags.end(); ++i)
{
if (i->first.startsWith(prefix))
{
tags.push_back(i->first);
}
}
}
void CIndexedFiles::PrepareTagTable()
{
QStringList tags;
for (int i = 0; i < m_files.size(); ++i)
{
GetTags(tags, m_files[i].filename);
for (int k = 0; k < tags.size(); ++k)
{
m_tags[tags[k]].insert(i);
}
}
}
void CIndexedFiles::AddUpdateCallback(std::function<void()> updateCallback)
{
CryAutoLock<CryMutex> lock(m_updateCallbackMutex);
m_updateCallbacks.push_back(updateCallback);
}
void CIndexedFiles::InvokeUpdateCallbacks()
{
CryAutoLock<CryMutex> lock(m_updateCallbackMutex);
for (auto updateCallback : m_updateCallbacks)
{
updateCallback();
}
}
-176
View File
@@ -1,176 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : Tagged files database for 'SmartFileOpen' dialog
//
// Notice : Refer SmartFileOpenDialog h
#ifndef CRYINCLUDE_EDITOR_UTIL_INDEXEDFILES_H
#define CRYINCLUDE_EDITOR_UTIL_INDEXEDFILES_H
#pragma once
#include "FileUtil.h"
#include <functional>
class CIndexedFiles
{
friend class CFileIndexingThread;
public:
static CIndexedFiles& GetDB()
{
if (!s_pIndexedFiles)
{
assert(!"CIndexedFiles not created! Make sure you use CIndexedFiles::GetDB() after CIndexedFiles::StartFileIndexing() is called.");
}
assert(s_pIndexedFiles);
return *s_pIndexedFiles;
}
static bool HasFileIndexingDone()
{ return s_bIndexingDone > 0; }
static void Create()
{
assert(!s_pIndexedFiles);
s_pIndexedFiles = new CIndexedFiles;
}
static void Destroy()
{
SAFE_DELETE(s_pIndexedFiles);
}
static void StartFileIndexing()
{
assert(s_bIndexingDone == 0);
assert(s_pIndexedFiles);
if (!s_pIndexedFiles)
{
return;
}
GetFileIndexingThread().Start(-1, "FileIndexing");
m_startedFileIndexing = true;
}
static void AbortFileIndexing()
{
if (!m_startedFileIndexing)
{
return;
}
if (HasFileIndexingDone() == false)
{
GetFileIndexingThread().Abort();
}
m_startedFileIndexing = false;
}
static void RegisterCallback(std::function<void()> callback)
{
assert(s_pIndexedFiles);
if (!s_pIndexedFiles)
{
return;
}
s_pIndexedFiles->AddUpdateCallback(callback);
}
public:
void Initialize(const QString& path, IFileUtil::ScanDirectoryUpdateCallBack updateCB = nullptr);
// Adds a new file to the database.
void AddFile(const IFileUtil::FileDesc& path);
// Removes a no-longer-existing file from the database.
void RemoveFile(const QString& path);
// Refreshes this database for the subdirectory.
void Refresh(const QString& path, bool recursive = true);
void GetFilesWithTags(IFileUtil::FileArray& files, const QStringList& tags) const;
//! This method returns all the tags which start with a given prefix.
//! It is useful for the tag auto-completion.
void GetTagsOfPrefix(QStringList& tags, const QString& prefix) const;
uint32 GetTotalCount() const
{ return (uint32)m_files.size(); }
private:
static bool m_startedFileIndexing;
std::vector <std::function<void()> > m_updateCallbacks;
IFileUtil::FileArray m_files;
std::map<QString, int> m_pathToIndex;
typedef std::set<int, std::less<int> > int_set;
typedef std::map<QString, int_set, std::less<QString> > TagTable;
TagTable m_tags;
QString m_rootPath;
void GetTags(QStringList& tags, const QString& path) const;
void PrepareTagTable();
CryMutex m_updateCallbackMutex;
void AddUpdateCallback(std::function<void()> updateCallback);
void InvokeUpdateCallbacks();
// A done flag for the background file indexing
static volatile TIntAtomic s_bIndexingDone;
// A thread for the background file indexing
class CFileIndexingThread
: public CryThread<CFileIndexingThread>
{
public:
virtual void Run()
{
CIndexedFiles::GetDB().Initialize("@assets@", CallBack);
CryInterlockedAdd(CIndexedFiles::s_bIndexingDone.Addr(), 1);
}
CFileIndexingThread()
: m_abort(false) {}
void Abort()
{
m_abort = true;
WaitForThread();
}
virtual ~CFileIndexingThread()
{
Abort();
}
private:
bool m_abort;
static bool CallBack([[maybe_unused]] const QString& msg)
{
if (CIndexedFiles::GetFileIndexingThread().m_abort)
{
return false;
}
return true;
}
};
static CFileIndexingThread& GetFileIndexingThread()
{
static CFileIndexingThread s_fileIndexingThread;
return s_fileIndexingThread;
}
// A global database for tagged files
static CIndexedFiles* s_pIndexedFiles;
};
#endif // CRYINCLUDE_EDITOR_UTIL_INDEXEDFILES_H
-2
View File
@@ -724,8 +724,6 @@ set(FILES
Util/GuidUtil.cpp
Util/GuidUtil.h
Util/IObservable.h
Util/IndexedFiles.cpp
Util/IndexedFiles.h
Util/KDTree.cpp
Util/Mailer.h
Util/NamedData.cpp
-11
View File
@@ -30,17 +30,6 @@
#define MOBILE
#endif
#if (defined(__clang__) && NDK_REV_MAJOR >= 14) || (defined(_CPU_ARM) && defined(PLATFORM_64BIT))
// The version of clang that NDK r14+ ships with is seemingly generating different (for better or worse) code for the atomic operations
// used in the LocklessLinkedList. In either case, this is causing deadlocks in the job system and crashes from memory stomps in
// the bucket allocator. By defining INTERLOCKED_COMPARE_EXCHANGE_128_NOT_SUPPORTED it will disable the Cry job system as well as
// change the implementation of the LocklessLinkedList to use a mutex in it's operations instead, essentially use the same behaviour
// as iOS. While not ideal to use this as a band-aid on the problem, it does fix it with a negligible performance impact.
//
// Additionally, arm64 processors do not provide a cmpxchg16b (or equivalent) instruction required for _InterlockedCompareExchange128
#define INTERLOCKED_COMPARE_EXCHANGE_128_NOT_SUPPORTED
#endif
// Force all allocations to be aligned to TARGET_DEFAULT_ALIGN.
// This is because malloc on Android 32 bit returns memory that is not aligned
// to what some structs/classes need.
-638
View File
@@ -78,77 +78,6 @@ public:
~CryAutoLock() { m_pLock->Unlock(); }
};
//////////////////////////////////////////////////////////////////////////
//
// CryOptionalAutoLock implements a helper class to automatically
// lock critical section (if needed) in constructor and release on destructor.
//
//////////////////////////////////////////////////////////////////////////
template<class LockClass>
class CryOptionalAutoLock
{
private:
LockClass* m_Lock;
bool m_bLockAcquired;
CryOptionalAutoLock();
CryOptionalAutoLock(const CryOptionalAutoLock<LockClass>&);
CryOptionalAutoLock<LockClass>& operator = (const CryOptionalAutoLock<LockClass>&);
public:
CryOptionalAutoLock(LockClass& Lock, bool acquireLock)
: m_Lock(&Lock)
, m_bLockAcquired(false)
{
if (acquireLock)
{
Acquire();
}
}
~CryOptionalAutoLock()
{
Release();
}
void Release()
{
if (m_bLockAcquired)
{
m_Lock->Unlock();
m_bLockAcquired = false;
}
}
void Acquire()
{
if (!m_bLockAcquired)
{
m_Lock->Lock();
m_bLockAcquired = true;
}
}
};
//////////////////////////////////////////////////////////////////////////
//
// CryAutoSet implements a helper class to automatically
// set and reset value in constructor and release on destructor.
//
//////////////////////////////////////////////////////////////////////////
template<class ValueClass>
class CryAutoSet
{
private:
ValueClass* m_pValue;
CryAutoSet();
CryAutoSet(const CryAutoSet<ValueClass>&);
CryAutoSet<ValueClass>& operator = (const CryAutoSet<ValueClass>&);
public:
CryAutoSet(ValueClass& value)
: m_pValue(&value) { *m_pValue = (ValueClass)1; }
~CryAutoSet() { *m_pValue = (ValueClass)0; }
};
//////////////////////////////////////////////////////////////////////////
//
// Auto critical section is the most commonly used type of auto lock.
@@ -156,10 +85,6 @@ public:
//////////////////////////////////////////////////////////////////////////
typedef CryAutoLock<CryCriticalSection> CryAutoCriticalSection;
#define AUTO_LOCK_T(Type, lock) PREFAST_SUPPRESS_WARNING(6246); CryAutoLock<Type> __AutoLock(lock)
#define AUTO_LOCK(lock) AUTO_LOCK_T(CryCriticalSection, lock)
#define AUTO_LOCK_CS(csLock) CryAutoCriticalSection __AL__##csLock(csLock)
/////////////////////////////////////////////////////////////////////////////
//
// Threads.
@@ -235,14 +160,6 @@ struct CryThreadInfo
template<class Runnable = CryRunnable>
class CrySimpleThread;
// Standard thread class.
//
// The class provides a lock (mutex) and an associated condition variable. If
// you don't need the lock, then you should used CrySimpleThread instead of
// CryThread.
template<class Runnable = CryRunnable>
class CryThread;
///////////////////////////////////////////////////////////////////////////////
// Include architecture specific code.
#if AZ_LEGACY_CRYCOMMON_TRAIT_USE_PTHREADS
@@ -265,560 +182,5 @@ class CryThread;
typedef CryLockT<CRYLOCK_RECURSIVE> CryMutex;
#endif // !_CRYTHREAD_CONDLOCK_GLITCH
// The the architecture specific code does not define a class CryRWLock, then
// a default implementation is provided here.
#if !defined _CRYTHREAD_HAVE_RWLOCK && !defined _CRYTHREAD_CONDLOCK_GLITCH
class CryRWLock
{
CryCriticalSection m_lockExclusiveAccess;
CryCriticalSection m_lockSharedAccessComplete;
CryConditionVariable m_condSharedAccessComplete;
int m_nSharedAccessCount;
int m_nCompletedSharedAccessCount;
bool m_bExclusiveAccess;
CryRWLock(const CryRWLock&);
CryRWLock& operator= (const CryRWLock&);
void AdjustSharedAccessCount()
{
m_nSharedAccessCount -= m_nCompletedSharedAccessCount;
m_nCompletedSharedAccessCount = 0;
}
public:
CryRWLock()
: m_nSharedAccessCount(0)
, m_nCompletedSharedAccessCount(0)
, m_bExclusiveAccess(false)
{ }
void RLock()
{
m_lockExclusiveAccess.Lock();
if (++m_nSharedAccessCount == INT_MAX)
{
m_lockSharedAccessComplete.Lock();
AdjustSharedAccessCount();
m_lockSharedAccessComplete.Unlock();
}
m_lockExclusiveAccess.Unlock();
}
bool TryRLock()
{
if (!m_lockExclusiveAccess.TryLock())
{
return false;
}
if (++m_nSharedAccessCount == INT_MAX)
{
m_lockSharedAccessComplete.Lock();
AdjustSharedAccessCount();
m_lockSharedAccessComplete.Unlock();
}
m_lockExclusiveAccess.Unlock();
return true;
}
void RUnlock()
{
Unlock();
}
void WLock()
{
m_lockExclusiveAccess.Lock();
m_lockSharedAccessComplete.Lock();
assert(!m_bExclusiveAccess);
AdjustSharedAccessCount();
if (m_nSharedAccessCount > 0)
{
m_nCompletedSharedAccessCount -= m_nSharedAccessCount;
do
{
m_condSharedAccessComplete.Wait(m_lockSharedAccessComplete);
}
while (m_nCompletedSharedAccessCount < 0);
m_nSharedAccessCount = 0;
}
m_bExclusiveAccess = true;
}
bool TryWLock()
{
if (!m_lockExclusiveAccess.TryLock())
{
return false;
}
if (!m_lockSharedAccessComplete.TryLock())
{
m_lockExclusiveAccess.Unlock();
return false;
}
assert(!m_bExclusiveAccess);
AdjustSharedAccessCount();
if (m_nSharedAccessCount > 0)
{
m_lockSharedAccessComplete.Unlock();
m_lockExclusiveAccess.Unlock();
return false;
}
else
{
m_bExclusiveAccess = true;
}
return true;
}
void WUnlock()
{
Unlock();
}
void Unlock()
{
if (!m_bExclusiveAccess)
{
m_lockSharedAccessComplete.Lock();
if (++m_nCompletedSharedAccessCount == 0)
{
m_condSharedAccessComplete.NotifySingle();
}
m_lockSharedAccessComplete.Unlock();
}
else
{
m_bExclusiveAccess = false;
m_lockSharedAccessComplete.Unlock();
m_lockExclusiveAccess.Unlock();
}
}
};
#endif // !defined _CRYTHREAD_HAVE_RWLOCK
// Thread class.
//
// CryThread is an extension of CrySimpleThread providing a lock (mutex) and a
// condition variable per instance.
template<class Runnable>
class CryThread
: public CrySimpleThread<Runnable>
{
CryMutex m_Lock;
CryConditionVariable m_Cond;
CryThread(const CryThread<Runnable>&);
void operator = (const CryThread<Runnable>&);
public:
CryThread() { }
void Lock() { m_Lock.Lock(); }
bool TryLock() { return m_Lock.TryLock(); }
void Unlock() { m_Lock.Unlock(); }
void Wait() { m_Cond.Wait(m_Lock); }
// Timed wait on the associated condition.
//
// The 'milliseconds' parameter specifies the relative timeout in
// milliseconds. The method returns true if a notification was received and
// false if the specified timeout expired without receiving a notification.
//
// UNIX note: the method will _not_ return if the calling thread receives a
// signal. Instead the call is re-started with the _original_ timeout
// value. This misfeature may be fixed in the future.
bool TimedWait(uint32 milliseconds)
{
return m_Cond.TimedWait(m_Lock, milliseconds);
}
void Notify() { m_Cond.Notify(); }
void NotifySingle() { m_Cond.NotifySingle(); }
CryMutex& GetLock() { return m_Lock; }
};
//////////////////////////////////////////////////////////////////////////
//
// Sync primitive for multiple reads and exclusive locking change access
//
// Desc:
// Useful in case if you have rarely modified object that needs
// to be read quite often from different threads but still
// need to be exclusively modified sometimes
// Debug functionality:
// Can be used for debug-only lock calls, which verify that no
// simultaneous access is attempted.
// Use the bDebug argument of LockRead or LockModify,
// or use the DEBUG_READLOCK or DEBUG_MODIFYLOCK macros.
// There is no overhead in release builds, if you use the macros,
// and the lock definition is inside #ifdef _DEBUG.
//////////////////////////////////////////////////////////////////////////
class CryReadModifyLock
{
public:
CryReadModifyLock()
: m_modifyCount(0)
, m_readCount(0)
{
SetDebugLocked(false);
}
bool LockRead(bool bTry = false, cstr strDebug = 0, bool bDebug = false) const
{
if (!WriteLock(bTry, bDebug, strDebug)) // wait until write unlocked
{
return false;
}
CryInterlockedIncrement(&m_readCount); // increment read counter
m_writeLock.Unlock();
return true;
}
void UnlockRead() const
{
SetDebugLocked(false);
const int counter = CryInterlockedDecrement(&m_readCount); // release read
assert(counter >= 0);
if (m_writeLock.TryLock())
{
m_writeLock.Unlock();
}
else
if (counter == 0 && m_modifyCount)
{
m_ReadReleased.Set(); // signal the final read released
}
}
bool LockModify(bool bTry = false, cstr strDebug = 0, bool bDebug = false) const
{
if (!WriteLock(bTry, bDebug, strDebug))
{
return false;
}
CryInterlockedIncrement(&m_modifyCount); // increment write counter (counter is for nested cases)
while (m_readCount)
{
m_ReadReleased.Wait(); // wait for all threads finish read operation
}
return true;
}
void UnlockModify() const
{
SetDebugLocked(false);
#if !defined(NDEBUG)
int counter =
#endif
CryInterlockedDecrement(&m_modifyCount); // decrement write counter
assert(counter >= 0);
m_writeLock.Unlock(); // release exclusive lock
}
protected:
mutable volatile int m_readCount;
mutable volatile int m_modifyCount;
mutable CryEvent m_ReadReleased;
mutable CryCriticalSection m_writeLock;
mutable bool m_debugLocked;
mutable const char* m_debugLockStr;
void SetDebugLocked([[maybe_unused]] bool b, [[maybe_unused]] const char* str = 0) const
{
#ifdef _DEBUG
m_debugLocked = b;
m_debugLockStr = str;
#endif
}
bool WriteLock(bool bTry, [[maybe_unused]] bool bDebug, [[maybe_unused]] const char* strDebug) const
{
if (!m_writeLock.TryLock())
{
#ifdef _DEBUG
assert(!m_debugLocked);
assert(!bDebug);
#endif
if (bTry)
{
return false;
}
m_writeLock.Lock();
}
#ifdef _DEBUG
if (!m_readCount && !m_modifyCount) // not yet locked
{
SetDebugLocked(bDebug, strDebug);
}
#endif
return true;
}
};
// Auto-locking classes.
template<class T, bool bDEBUG = false>
class AutoLockRead
{
protected:
const T& m_lock;
public:
AutoLockRead(const T& lock, cstr strDebug = 0)
: m_lock(lock) { m_lock.LockRead(bDEBUG, strDebug, bDEBUG); }
~AutoLockRead()
{ m_lock.UnlockRead(); }
};
template<class T, bool bDEBUG = false>
class AutoLockModify
{
protected:
const T& m_lock;
public:
AutoLockModify(const T& lock, cstr strDebug = 0)
: m_lock(lock) { m_lock.LockModify(bDEBUG, strDebug, bDEBUG); }
~AutoLockModify()
{ m_lock.UnlockModify(); }
};
#define AUTO_READLOCK(p) PREFAST_SUPPRESS_WARNING(6246) AutoLockRead<CryReadModifyLock> AZ_JOIN(__readlock, __LINE__)(p, __FUNC__)
#define AUTO_READLOCK_PROT(p) PREFAST_SUPPRESS_WARNING(6246) AutoLockRead<CryReadModifyLock> AZ_JOIN(__readlock_prot, __LINE__)(p, __FUNC__)
#define AUTO_MODIFYLOCK(p) PREFAST_SUPPRESS_WARNING(6246) AutoLockModify<CryReadModifyLock> AZ_JOIN(__modifylock, __LINE__)(p, __FUNC__)
#if defined(_DEBUG)
#define DEBUG_READLOCK(p) AutoLockRead<CryReadModifyLock> AZ_JOIN(__readlock, __LINE__)(p, __FUNC__)
#define DEBUG_MODIFYLOCK(p) AutoLockModify<CryReadModifyLock> AZ_JOIN(__modifylock, __LINE__)(p, __FUNC__)
#else
#define DEBUG_READLOCK(p)
#define DEBUG_MODIFYLOCK(p)
#endif
// producer consumer queue implementations, but here instead of MultiThread_Container.h
// since they requiere platform specific code, and including windows.h in a very common
// header file leads to all kinds of problems
namespace CryMT
{
//////////////////////////////////////////////////////////////////////////
// Producer/Consumer Queue for 1 to 1 thread communication
// Realized with only volatile variables and memory barriers
// *warning* this producer/consumer queue is only thread safe in a 1 to 1 situation
// and doesn't provide any yields or similar to prevent spinning
//////////////////////////////////////////////////////////////////////////
template<typename T>
class SingleProducerSingleConsumerQueue
: public CryMT::detail::SingleProducerSingleConsumerQueueBase
{
public:
SingleProducerSingleConsumerQueue();
~SingleProducerSingleConsumerQueue();
void Init(size_t nSize);
void Push(const T& rObj);
void Pop(T* pResult);
uint32 Size() { return (m_nProducerIndex - m_nComsumerIndex); }
uint32 BufferSize() { return m_nBufferSize; }
uint32 FreeCount() { return (m_nBufferSize - (m_nProducerIndex - m_nComsumerIndex)); }
private:
T* m_arrBuffer;
uint32 m_nBufferSize;
volatile uint32 m_nProducerIndex _ALIGN(16);
volatile uint32 m_nComsumerIndex _ALIGN(16);
} _ALIGN(128);
///////////////////////////////////////////////////////////////////////////////
template<typename T>
inline SingleProducerSingleConsumerQueue<T>::SingleProducerSingleConsumerQueue()
: m_arrBuffer(NULL)
, m_nBufferSize(0)
, m_nProducerIndex(0)
, m_nComsumerIndex(0)
{}
///////////////////////////////////////////////////////////////////////////////
template<typename T>
inline SingleProducerSingleConsumerQueue<T>::~SingleProducerSingleConsumerQueue()
{
CryModuleMemalignFree(m_arrBuffer);
m_nBufferSize = 0;
}
///////////////////////////////////////////////////////////////////////////////
template<typename T>
inline void SingleProducerSingleConsumerQueue<T>::Init(size_t nSize)
{
assert(m_arrBuffer == NULL);
assert(m_nBufferSize == 0);
assert((nSize & (nSize - 1)) == 0);
m_arrBuffer = alias_cast<T*>(CryModuleMemalign(nSize * sizeof(T), 16));
m_nBufferSize = nSize;
}
///////////////////////////////////////////////////////////////////////////////
template<typename T>
inline void SingleProducerSingleConsumerQueue<T>::Push(const T& rObj)
{
assert(m_arrBuffer != NULL);
assert(m_nBufferSize != 0);
SingleProducerSingleConsumerQueueBase::Push((void*)&rObj, m_nProducerIndex, m_nComsumerIndex, m_nBufferSize, m_arrBuffer, sizeof(T));
}
///////////////////////////////////////////////////////////////////////////////
template<typename T>
inline void SingleProducerSingleConsumerQueue<T>::Pop(T* pResult)
{
assert(m_arrBuffer != NULL);
assert(m_nBufferSize != 0);
SingleProducerSingleConsumerQueueBase::Pop(pResult, m_nProducerIndex, m_nComsumerIndex, m_nBufferSize, m_arrBuffer, sizeof(T));
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Producer/Consumer Queue for N to 1 thread communication
// lockfree implemenation, to copy with multiple producers,
// a internal producer refcount is managed, the queue is empty
// as soon as there are no more producers and no new elements
//////////////////////////////////////////////////////////////////////////
template<typename T>
class N_ProducerSingleConsumerQueue
: public CryMT::detail::N_ProducerSingleConsumerQueueBase
{
public:
N_ProducerSingleConsumerQueue();
~N_ProducerSingleConsumerQueue();
void Init(size_t nSize);
void Push(const T& rObj);
bool Pop(T* pResult);
// needs to be called before using, assumes that there is at least one producer
// so the first one doesn't need to call AddProducer, but he has to deregister itself
void SetRunningState();
// to correctly track when the queue is empty(and no new jobs will be added), refcount the producer
void AddProducer();
void RemoveProducer();
uint32 Size() { return (m_nProducerIndex - m_nComsumerIndex); }
uint32 BufferSize() { return m_nBufferSize; }
uint32 FreeCount() { return (m_nBufferSize - (m_nProducerIndex - m_nComsumerIndex)); }
private:
T* m_arrBuffer;
volatile uint32* m_arrStates;
uint32 m_nBufferSize;
volatile uint32 m_nProducerIndex;
volatile uint32 m_nComsumerIndex;
volatile uint32 m_nRunning;
volatile uint32 m_nProducerCount;
} _ALIGN(128);
///////////////////////////////////////////////////////////////////////////////
template<typename T>
inline N_ProducerSingleConsumerQueue<T>::N_ProducerSingleConsumerQueue()
: m_arrBuffer(NULL)
, m_arrStates(NULL)
, m_nBufferSize(0)
, m_nProducerIndex(0)
, m_nComsumerIndex(0)
, m_nRunning(0)
, m_nProducerCount(0)
{}
///////////////////////////////////////////////////////////////////////////////
template<typename T>
inline N_ProducerSingleConsumerQueue<T>::~N_ProducerSingleConsumerQueue()
{
CryModuleMemalignFree(m_arrBuffer);
CryModuleMemalignFree((void*)m_arrStates);
m_nBufferSize = 0;
}
///////////////////////////////////////////////////////////////////////////////
template<typename T>
inline void N_ProducerSingleConsumerQueue<T>::Init(size_t nSize)
{
assert(m_arrBuffer == NULL);
assert(m_arrStates == NULL);
assert(m_nBufferSize == 0);
assert((nSize & (nSize - 1)) == 0);
m_arrBuffer = alias_cast<T*>(CryModuleMemalign(nSize * sizeof(T), 16));
m_arrStates = alias_cast<uint32*>(CryModuleMemalign(nSize * sizeof(uint32), 16));
memset((void*)m_arrStates, 0, sizeof(uint32) * nSize);
m_nBufferSize = nSize;
}
///////////////////////////////////////////////////////////////////////////////
template<typename T>
inline void N_ProducerSingleConsumerQueue<T>::SetRunningState()
{
#if !defined(_RELEASE)
if (m_nRunning == 1)
{
__debugbreak();
}
#endif
m_nRunning = 1;
m_nProducerCount = 1;
}
///////////////////////////////////////////////////////////////////////////////
template<typename T>
inline void N_ProducerSingleConsumerQueue<T>::AddProducer()
{
assert(m_arrBuffer != NULL);
assert(m_arrStates != NULL);
assert(m_nBufferSize != 0);
#if !defined(_RELEASE)
if (m_nRunning == 0)
{
__debugbreak();
}
#endif
CryInterlockedIncrement((volatile int*)&m_nProducerCount);
}
///////////////////////////////////////////////////////////////////////////////
template<typename T>
inline void N_ProducerSingleConsumerQueue<T>::RemoveProducer()
{
assert(m_arrBuffer != NULL);
assert(m_arrStates != NULL);
assert(m_nBufferSize != 0);
#if !defined(_RELEASE)
if (m_nRunning == 0)
{
__debugbreak();
}
#endif
if (CryInterlockedDecrement((volatile int*)&m_nProducerCount) == 0)
{
m_nRunning = 0;
}
}
///////////////////////////////////////////////////////////////////////////////
template<typename T>
inline void N_ProducerSingleConsumerQueue<T>::Push(const T& rObj)
{
assert(m_arrBuffer != NULL);
assert(m_arrStates != NULL);
assert(m_nBufferSize != 0);
CryMT::detail::N_ProducerSingleConsumerQueueBase::Push((void*)&rObj, m_nProducerIndex, m_nComsumerIndex, m_nRunning, m_arrBuffer, m_nBufferSize, sizeof(T), m_arrStates);
}
///////////////////////////////////////////////////////////////////////////////
template<typename T>
inline bool N_ProducerSingleConsumerQueue<T>::Pop(T* pResult)
{
assert(m_arrBuffer != NULL);
assert(m_arrStates != NULL);
assert(m_nBufferSize != 0);
return CryMT::detail::N_ProducerSingleConsumerQueueBase::Pop(pResult, m_nProducerIndex, m_nComsumerIndex, m_nRunning, m_arrBuffer, m_nBufferSize, sizeof(T), m_arrStates);
}
} //namespace CryMT
// Include all multithreading containers.
#include "MultiThread_Containers.h"
@@ -107,195 +107,4 @@ void* CryCreateCriticalSection()
return (void*) new TCritSecType;
}
#if AZ_TRAIT_SKIP_CRYINTERLOCKED
#elif defined(INTERLOCKED_COMPARE_EXCHANGE_128_NOT_SUPPORTED)
//////////////////////////////////////////////////////////////////////////
void CryInterlockedPushEntrySList(SLockFreeSingleLinkedListHeader& list, SLockFreeSingleLinkedListEntry& element)
{
AZStd::lock_guard<AZStd::mutex> lock(list.mutex);
element.pNext = list.pNext;
list.pNext = &element;
}
//////////////////////////////////////////////////////////////////////////
void* CryInterlockedPopEntrySList(SLockFreeSingleLinkedListHeader& list)
{
AZStd::lock_guard<AZStd::mutex> lock(list.mutex);
SLockFreeSingleLinkedListEntry* returnValue = list.pNext;
if (list.pNext)
{
list.pNext = list.pNext->pNext;
}
return returnValue;
}
//////////////////////////////////////////////////////////////////////////
void CryInitializeSListHead(SLockFreeSingleLinkedListHeader& list)
{
AZStd::lock_guard<AZStd::mutex> lock(list.mutex);
list.pNext = NULL;
}
//////////////////////////////////////////////////////////////////////////
void* CryInterlockedFlushSList(SLockFreeSingleLinkedListHeader& list)
{
AZStd::lock_guard<AZStd::mutex> lock(list.mutex);
SLockFreeSingleLinkedListEntry* returnValue = list.pNext;
list.pNext = nullptr;
return returnValue;
}
#elif defined(LINUX32)
//////////////////////////////////////////////////////////////////////////
// Implementation for Linux32 with gcc using uint64
//////////////////////////////////////////////////////////////////////////
void CryInterlockedPushEntrySList(SLockFreeSingleLinkedListHeader& list, SLockFreeSingleLinkedListEntry& element)
{
uint32 curSetting[2];
uint32 newSetting[2];
uint32 newPointer = (uint32) & element;
do
{
curSetting[0] = (uint32)list.pNext;
curSetting[1] = list.salt;
element.pNext = (SLockFreeSingleLinkedListEntry*)curSetting[0];
newSetting[0] = newPointer; // new pointer
newSetting[1] = curSetting[1] + 1; // new salt
}
while (false == __sync_bool_compare_and_swap((volatile uint64*)&list.pNext, *(uint64*)&curSetting[0], *(uint64*)&newSetting[0]));
}
//////////////////////////////////////////////////////////////////////////
void* CryInterlockedPopEntrySList(SLockFreeSingleLinkedListHeader& list)
{
uint32 curSetting[2];
uint32 newSetting[2];
do
{
curSetting[1] = list.salt;
curSetting[0] = (uint32)list.pNext;
if (curSetting[0] == 0)
{
return NULL;
}
newSetting[0] = *(uint32*)curSetting[0]; // new pointer
newSetting[1] = curSetting[1] + 1; // new salt
}
while (false == __sync_bool_compare_and_swap((volatile uint64*)&list.pNext, *(uint64*)&curSetting[0], *(uint64*)&newSetting[0]));
return (void*)curSetting[0];
}
//////////////////////////////////////////////////////////////////////////
void CryInitializeSListHead(SLockFreeSingleLinkedListHeader& list)
{
list.salt = 0;
list.pNext = NULL;
}
//////////////////////////////////////////////////////////////////////////
void* CryInterlockedFlushSList(SLockFreeSingleLinkedListHeader& list)
{
uint32 curSetting[2];
uint32 newSetting[2];
uint32 newSalt;
uint32 newPointer;
do
{
curSetting[1] = list.salt;
curSetting[0] = (uint32)list.pNext;
if (curSetting[0] == 0)
{
return NULL;
}
newSetting[0] = 0;
newSetting[1] = curSetting[1] + 1;
}
while (false == __sync_bool_compare_and_swap((volatile uint64*)&list.pNext, *(uint64*)&curSetting[0], *(uint64*)&newSetting[0]));
return (void*)curSetting[0];
}
#else
// This implementation get's used on multiple platforms that support uint128 compare and swap.
//////////////////////////////////////////////////////////////////////////
// LINUX64 Implementation of Lockless Single Linked List
//////////////////////////////////////////////////////////////////////////
typedef __uint128_t uint128;
//////////////////////////////////////////////////////////////////////////
// Implementation for Linux64 with gcc using __int128_t
//////////////////////////////////////////////////////////////////////////
void CryInterlockedPushEntrySList(SLockFreeSingleLinkedListHeader& list, SLockFreeSingleLinkedListEntry& element)
{
uint64 curSetting[2];
uint64 newSetting[2];
uint64 newPointer = (uint64) & element;
do
{
curSetting[0] = (uint64)list.pNext;
curSetting[1] = list.salt;
element.pNext = (SLockFreeSingleLinkedListEntry*)curSetting[0];
newSetting[0] = newPointer; // new pointer
newSetting[1] = curSetting[1] + 1; // new salt
}
// while (false == __sync_bool_compare_and_swap( (volatile uint128*)&list.pNext,*(uint128*)&curSetting[0],*(uint128*)&newSetting[0] ));
while (0 == _InterlockedCompareExchange128((volatile int64*)&list.pNext, (int64)newSetting[1], (int64)newSetting[0], (int64*)&curSetting[0]));
}
//////////////////////////////////////////////////////////////////////////
void* CryInterlockedPopEntrySList(SLockFreeSingleLinkedListHeader& list)
{
uint64 curSetting[2];
uint64 newSetting[2];
do
{
curSetting[1] = list.salt;
curSetting[0] = (uint64)list.pNext;
if (curSetting[0] == 0)
{
return NULL;
}
newSetting[0] = *(uint64*)curSetting[0]; // new pointer
newSetting[1] = curSetting[1] + 1; // new salt
}
//while (false == __sync_bool_compare_and_swap( (volatile uint128*)&list.pNext,*(uint128*)&curSetting[0],*(uint128*)&newSetting[0] ));
while (0 == _InterlockedCompareExchange128((volatile int64*)&list.pNext, (int64)newSetting[1], (int64)newSetting[0], (int64*)&curSetting[0]));
return (void*)curSetting[0];
}
//////////////////////////////////////////////////////////////////////////
void CryInitializeSListHead(SLockFreeSingleLinkedListHeader& list)
{
list.salt = 0;
list.pNext = NULL;
}
//////////////////////////////////////////////////////////////////////////
void* CryInterlockedFlushSList(SLockFreeSingleLinkedListHeader& list)
{
uint64 curSetting[2];
uint64 newSetting[2];
uint64 newSalt;
uint64 newPointer;
do
{
curSetting[1] = list.salt;
curSetting[0] = (uint64)list.pNext;
if (curSetting[0] == 0)
{
return NULL;
}
newSetting[0] = 0;
newSetting[1] = curSetting[1] + 1;
}
// while (false == __sync_bool_compare_and_swap( (volatile uint128*)&list.pNext,*(uint128*)&curSetting[0],*(uint128*)&newSetting[0] ));
while (0 == _InterlockedCompareExchange128((volatile int64*)&list.pNext, (int64)newSetting[1], (int64)newSetting[0], (int64*)&curSetting[0]));
return (void*)curSetting[0];
}
//////////////////////////////////////////////////////////////////////////
#endif
#endif // CRYINCLUDE_CRYCOMMON_CRYTHREADIMPL_PTHREADS_H
@@ -303,78 +303,6 @@ void CryFastSemaphore::Release()
}
}
//////////////////////////////////////////////////////////////////////////
CryRWLock::CryRWLock()
{
STATIC_ASSERT(sizeof(m_Lock) == sizeof(PSRWLOCK), "RWLock-pointer has invalid size");
InitializeSRWLock(reinterpret_cast<PSRWLOCK>(&m_Lock));
}
//////////////////////////////////////////////////////////////////////////
CryRWLock::~CryRWLock()
{
}
//////////////////////////////////////////////////////////////////////////
void CryRWLock::RLock()
{
AcquireSRWLockShared(reinterpret_cast<PSRWLOCK>(&m_Lock));
}
//////////////////////////////////////////////////////////////////////////
#if defined(_CRYTHREAD_WANT_TRY_RWLOCK)
bool CryRWLock::TryRLock()
{
return TryAcquireSRWLockShared(reinterpret_cast<PSRWLOCK>(&m_Lock)) != 0;
}
#endif
//////////////////////////////////////////////////////////////////////////
void CryRWLock::RUnlock()
{
ReleaseSRWLockShared(reinterpret_cast<PSRWLOCK>(&m_Lock));
}
//////////////////////////////////////////////////////////////////////////
void CryRWLock::WLock()
{
AcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&m_Lock));
}
//////////////////////////////////////////////////////////////////////////
#if defined(_CRYTHREAD_WANT_TRY_RWLOCK)
bool CryRWLock::TryWLock()
{
return TryAcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&m_Lock)) != 0;
}
#endif
//////////////////////////////////////////////////////////////////////////
void CryRWLock::WUnlock()
{
ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&m_Lock));
}
//////////////////////////////////////////////////////////////////////////
void CryRWLock::Lock()
{
WLock();
}
//////////////////////////////////////////////////////////////////////////
#if defined(_CRYTHREAD_WANT_TRY_RWLOCK)
bool CryRWLock::TryLock()
{
return TryWLock();
}
#endif
//////////////////////////////////////////////////////////////////////////
void CryRWLock::Unlock()
{
WUnlock();
}
//////////////////////////////////////////////////////////////////////////
CrySimpleThreadSelf::CrySimpleThreadSelf()
: m_thread(NULL)
@@ -415,181 +343,3 @@ void CrySimpleThreadSelf::StartThread(unsigned (__stdcall * func)(void*), void*
PREFAST_ASSUME(m_thread);
ResumeThread((HANDLE)m_thread);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void CryInterlockedPushEntrySList(SLockFreeSingleLinkedListHeader& list, SLockFreeSingleLinkedListEntry& element)
{
STATIC_CHECK(sizeof(SLockFreeSingleLinkedListHeader) == sizeof(SLIST_HEADER), CRY_INTERLOCKED_SLIST_HEADER_HAS_WRONG_SIZE);
STATIC_CHECK(sizeof(SLockFreeSingleLinkedListEntry) >= sizeof(SLIST_ENTRY), CRY_INTERLOCKED_SLIST_ENTRY_HAS_WRONG_SIZE);
assert(IsAligned(&list, MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Header has wrong Alignment");
assert(IsAligned(&element, MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Entry has wrong Alignment");
InterlockedPushEntrySList(alias_cast<PSLIST_HEADER>(&list), alias_cast<PSLIST_ENTRY>(&element));
}
//////////////////////////////////////////////////////////////////////////
void* CryInterlockedPopEntrySList(SLockFreeSingleLinkedListHeader& list)
{
STATIC_CHECK(sizeof(SLockFreeSingleLinkedListHeader) == sizeof(SLIST_HEADER), CRY_INTERLOCKED_SLIST_HEADER_HAS_WRONG_SIZE);
assert(IsAligned(&list, MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Header has wrong Alignment");
return reinterpret_cast<void*>(InterlockedPopEntrySList(alias_cast<PSLIST_HEADER>(&list)));
}
//////////////////////////////////////////////////////////////////////////
void CryInitializeSListHead(SLockFreeSingleLinkedListHeader& list)
{
assert(IsAligned(&list, MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Header has wrong Alignment");
STATIC_CHECK(sizeof(SLockFreeSingleLinkedListHeader) == sizeof(SLIST_HEADER), CRY_INTERLOCKED_SLIST_HEADER_HAS_WRONG_SIZE);
InitializeSListHead(alias_cast<PSLIST_HEADER>(&list));
}
//////////////////////////////////////////////////////////////////////////
void* CryInterlockedFlushSList(SLockFreeSingleLinkedListHeader& list)
{
assert(IsAligned(&list, MEMORY_ALLOCATION_ALIGNMENT) && "LockFree SingleLink List Header has wrong Alignment");
STATIC_CHECK(sizeof(SLockFreeSingleLinkedListHeader) == sizeof(SLIST_HEADER), CRY_INTERLOCKED_SLIST_HEADER_HAS_WRONG_SIZE);
return InterlockedFlushSList(alias_cast<PSLIST_HEADER>(&list));
}
///////////////////////////////////////////////////////////////////////////////
// base class for lock less Producer/Consumer queue, due platforms specific they
// are implemeted in CryThead_platform.h
namespace CryMT {
namespace detail {
///////////////////////////////////////////////////////////////////////////////
void SingleProducerSingleConsumerQueueBase::Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize)
{
// spin if queue is full
int iter = 0;
while (rProducerIndex - rComsumerIndex == nBufferSize)
{
CryLowLatencySleep(iter++ > 10 ? 1 : 0);
}
MemoryBarrier();
char* pBuffer = alias_cast<char*>(arrBuffer);
uint32 nIndex = rProducerIndex % nBufferSize;
memcpy(pBuffer + (nIndex * nObjectSize), pObj, nObjectSize);
MemoryBarrier();
rProducerIndex += 1;
MemoryBarrier();
}
///////////////////////////////////////////////////////////////////////////////
void SingleProducerSingleConsumerQueueBase::Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize)
{
MemoryBarrier();
// busy-loop if queue is empty
int iter = 0;
while (rProducerIndex - rComsumerIndex == 0)
{
CryLowLatencySleep(iter++ > 10 ? 1 : 0);
}
char* pBuffer = alias_cast<char*>(arrBuffer);
uint32 nIndex = rComsumerIndex % nBufferSize;
memcpy(pObj, pBuffer + (nIndex * nObjectSize), nObjectSize);
MemoryBarrier();
rComsumerIndex += 1;
MemoryBarrier();
}
///////////////////////////////////////////////////////////////////////////////
void N_ProducerSingleConsumerQueueBase::Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, [[maybe_unused]] volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates)
{
MemoryBarrier();
uint32 nProducerIndex;
uint32 nComsumerIndex;
int iter = 0;
do
{
nProducerIndex = rProducerIndex;
nComsumerIndex = rComsumerIndex;
if (nProducerIndex - nComsumerIndex == nBufferSize)
{
CryLowLatencySleep(iter++ > 10 ? 1 : 0);
if (iter > 20) // 10 spins + 10 ms wait
{
uint32 nSizeToAlloc = sizeof(SFallbackList) + nObjectSize - 1;
SFallbackList* pFallbackEntry = (SFallbackList*)CryModuleMemalign(nSizeToAlloc, 128);
memcpy(pFallbackEntry->object, pObj, nObjectSize);
MemoryBarrier();
CryInterlockedPushEntrySList(fallbackList, pFallbackEntry->nextEntry);
return;
}
continue;
}
if (CryInterlockedCompareExchange(alias_cast<volatile LONG*>(&rProducerIndex), nProducerIndex + 1, nProducerIndex) == nProducerIndex)
{
break;
}
} while (true);
MemoryBarrier();
char* pBuffer = alias_cast<char*>(arrBuffer);
uint32 nIndex = nProducerIndex % nBufferSize;
memcpy(pBuffer + (nIndex * nObjectSize), pObj, nObjectSize);
MemoryBarrier();
arrStates[nIndex] = 1;
MemoryBarrier();
}
///////////////////////////////////////////////////////////////////////////////
bool N_ProducerSingleConsumerQueueBase::Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates)
{
MemoryBarrier();
// busy-loop if queue is empty
int iter = 0;
if (rRunning && rProducerIndex - rComsumerIndex == 0)
{
while (rRunning && rProducerIndex - rComsumerIndex == 0)
{
CryLowLatencySleep(iter++ > 10 ? 1 : 0);
}
}
if (rRunning == 0 && rProducerIndex - rComsumerIndex == 0)
{
SFallbackList* pFallback = (SFallbackList*)CryInterlockedPopEntrySList(fallbackList);
IF (pFallback, 0)
{
memcpy(pObj, pFallback->object, nObjectSize);
CryModuleMemalignFree(pFallback);
return true;
}
// if the queue was empty, make sure we really are empty
return false;
}
iter = 0;
while (arrStates[rComsumerIndex % nBufferSize] == 0)
{
CryLowLatencySleep(iter++ > 10 ? 1 : 0);
}
char* pBuffer = alias_cast<char*>(arrBuffer);
uint32 nIndex = rComsumerIndex % nBufferSize;
memcpy(pObj, pBuffer + (nIndex * nObjectSize), nObjectSize);
MemoryBarrier();
arrStates[nIndex] = 0;
MemoryBarrier();
rComsumerIndex += 1;
MemoryBarrier();
return true;
}
} // namespace detail
} // namespace CryMT
-233
View File
@@ -524,57 +524,6 @@ inline void CryFastSemaphore::Release()
}
}
//////////////////////////////////////////////////////////////////////////
#if !defined _CRYTHREAD_HAVE_RWLOCK
class CryRWLock
{
pthread_rwlock_t m_Lock;
CryRWLock(const CryRWLock&);
CryRWLock& operator= (const CryRWLock&);
public:
CryRWLock() { pthread_rwlock_init(&m_Lock, NULL); }
~CryRWLock() { pthread_rwlock_destroy(&m_Lock); }
void RLock() { pthread_rwlock_rdlock(&m_Lock); }
bool TryRLock()
{
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION CRYTHREAD_PTHREADS_H_SECTION_TRY_RLOCK
#include AZ_RESTRICTED_FILE(CryThread_pthreads_h)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
return pthread_rwlock_tryrdlock(&m_Lock) != EBUSY;
#endif
}
void RUnlock() { Unlock(); }
void WLock() { pthread_rwlock_wrlock(&m_Lock); }
bool TryWLock()
{
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION CRYTHREAD_PTHREADS_H_SECTION_TRY_RLOCK
#include AZ_RESTRICTED_FILE(CryThread_pthreads_h)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
return pthread_rwlock_trywrlock(&m_Lock) != EBUSY;
#endif
}
void WUnlock() { Unlock(); }
void Lock() { WLock(); }
bool TryLock() { return TryWLock(); }
void Unlock() { pthread_rwlock_unlock(&m_Lock); }
};
// Indicate that this implementation header provides an implementation for
// CryRWLock.
#define _CRYTHREAD_HAVE_RWLOCK 1
#endif // !defined _CRYTHREAD_HAVE_RWLOCK
////////////////////////////////////////////////////////////////////////////////
// Provide TLS implementation using pthreads for those platforms without __thread
////////////////////////////////////////////////////////////////////////////////
@@ -1145,185 +1094,3 @@ public:
};
#include "MemoryAccess.h"
///////////////////////////////////////////////////////////////////////////////
// base class for lock less Producer/Consumer queue, due platforms specific they
// are implemeted in CryThead_platform.h
namespace CryMT {
namespace detail {
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
class SingleProducerSingleConsumerQueueBase
{
public:
SingleProducerSingleConsumerQueueBase()
{}
void Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize);
void Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize);
};
///////////////////////////////////////////////////////////////////////////////
inline void SingleProducerSingleConsumerQueueBase::Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize)
{
MemoryBarrier();
// spin if queue is full
int iter = 0;
while (rProducerIndex - rComsumerIndex == nBufferSize)
{
Sleep(iter++ > 10 ? 1 : 0);
}
char* pBuffer = alias_cast<char*>(arrBuffer);
uint32 nIndex = rProducerIndex % nBufferSize;
memcpy(pBuffer + (nIndex * nObjectSize), pObj, nObjectSize);
MemoryBarrier();
rProducerIndex += 1;
MemoryBarrier();
}
///////////////////////////////////////////////////////////////////////////////
inline void SingleProducerSingleConsumerQueueBase::Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize)
{
MemoryBarrier();
// busy-loop if queue is empty
int iter = 0;
while (rProducerIndex - rComsumerIndex == 0)
{
Sleep(iter++ > 10 ? 1 : 0);
}
char* pBuffer = alias_cast<char*>(arrBuffer);
uint32 nIndex = rComsumerIndex % nBufferSize;
memcpy(pObj, pBuffer + (nIndex * nObjectSize), nObjectSize);
MemoryBarrier();
rComsumerIndex += 1;
MemoryBarrier();
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
class N_ProducerSingleConsumerQueueBase
{
public:
N_ProducerSingleConsumerQueueBase()
{
CryInitializeSListHead(fallbackList);
}
void Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates);
bool Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates);
SLockFreeSingleLinkedListHeader fallbackList;
struct SFallbackList
{
SLockFreeSingleLinkedListEntry nextEntry;
char alignment_padding[128 - sizeof(SLockFreeSingleLinkedListEntry)];
char object[1]; // struct will be overallocated with enough memory for the object
};
};
///////////////////////////////////////////////////////////////////////////////
inline void N_ProducerSingleConsumerQueueBase::Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates)
{
MemoryBarrier();
uint32 nProducerIndex;
uint32 nComsumerIndex;
int iter = 0;
do
{
nProducerIndex = rProducerIndex;
nComsumerIndex = rComsumerIndex;
if (nProducerIndex - nComsumerIndex == nBufferSize)
{
Sleep(iter++ > 10 ? 1 : 0);
if (iter > 20) // 10 spins + 10 ms wait
{
uint32 nSizeToAlloc = sizeof(SFallbackList) + nObjectSize - 1;
SFallbackList* pFallbackEntry = (SFallbackList*)CryModuleMemalign(nSizeToAlloc, 128);
memcpy(pFallbackEntry->object, pObj, nObjectSize);
CryInterlockedPushEntrySList(fallbackList, pFallbackEntry->nextEntry);
return;
}
continue;
}
if (CryInterlockedCompareExchange(alias_cast<volatile LONG*>(&rProducerIndex), nProducerIndex + 1, nProducerIndex) == nProducerIndex)
{
break;
}
} while (true);
char* pBuffer = alias_cast<char*>(arrBuffer);
uint32 nIndex = nProducerIndex % nBufferSize;
memcpy(pBuffer + (nIndex * nObjectSize), pObj, nObjectSize);
MemoryBarrier();
arrStates[nIndex] = 1;
MemoryBarrier();
}
///////////////////////////////////////////////////////////////////////////////
inline bool N_ProducerSingleConsumerQueueBase::Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates)
{
MemoryBarrier();
// busy-loop if queue is empty
int iter = 0;
do
{
SFallbackList* pFallback = (SFallbackList*)CryInterlockedPopEntrySList(fallbackList);
IF (pFallback, 0)
{
memcpy(pObj, pFallback->object, nObjectSize);
CryModuleMemalignFree(pFallback);
return true;
}
if (iter > 10)
{
Sleep(iter > 100 ? 1 : 0);
}
iter++;
} while (rRunning && rProducerIndex - rComsumerIndex == 0);
if (rRunning == 0 && rProducerIndex - rComsumerIndex == 0)
{
// if the queue was empty, make sure we really are empty
SFallbackList* pFallback = (SFallbackList*)CryInterlockedPopEntrySList(fallbackList);
IF (pFallback, 0)
{
memcpy(pObj, pFallback->object, nObjectSize);
CryModuleMemalignFree(pFallback);
return true;
}
return false;
}
iter = 0;
while (arrStates[rComsumerIndex % nBufferSize] == 0)
{
Sleep(iter++ > 10 ? 1 : 0);
}
char* pBuffer = alias_cast<char*>(arrBuffer);
uint32 nIndex = rComsumerIndex % nBufferSize;
memcpy(pObj, pBuffer + (nIndex * nObjectSize), nObjectSize);
MemoryBarrier();
arrStates[nIndex] = 0;
MemoryBarrier();
rComsumerIndex += 1;
MemoryBarrier();
return true;
}
} // namespace detail
} // namespace CryMT
-78
View File
@@ -180,41 +180,6 @@ private:
volatile int32 m_nCounter;
};
//////////////////////////////////////////////////////////////////////////
#if !defined(_CRYTHREAD_HAVE_RWLOCK)
class CryRWLock
{
void* /*SRWLOCK*/ m_Lock;
CryRWLock(const CryRWLock&);
CryRWLock& operator= (const CryRWLock&);
public:
CryRWLock();
~CryRWLock();
void RLock();
void RUnlock();
void WLock();
void WUnlock();
void Lock();
void Unlock();
#if defined(_CRYTHREAD_WANT_TRY_RWLOCK)
// Enabling TryXXX requires Windows 7 or newer
bool TryRLock();
bool TryWLock();
bool TryLock();
#endif
};
// Indicate that this implementation header provides an implementation for
// CryRWLock.
#define _CRYTHREAD_HAVE_RWLOCK 1
#endif
//////////////////////////////////////////////////////////////////////////
class CrySimpleThreadSelf
{
@@ -420,46 +385,3 @@ public:
bool IsStarted() const { return m_bIsStarted; }
bool IsRunning() const { return m_bIsRunning; }
};
///////////////////////////////////////////////////////////////////////////////
// base class for lock less Producer/Consumer queue, due platforms specific they
// are implemented in CryThead_platform.h
namespace CryMT {
namespace detail {
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
class SingleProducerSingleConsumerQueueBase
{
public:
SingleProducerSingleConsumerQueueBase()
{}
void Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize);
void Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, uint32 nBufferSize, void* arrBuffer, uint32 nObjectSize);
};
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
class N_ProducerSingleConsumerQueueBase
{
public:
N_ProducerSingleConsumerQueueBase()
{
CryInitializeSListHead(fallbackList);
}
void Push(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates);
bool Pop(void* pObj, volatile uint32& rProducerIndex, volatile uint32& rComsumerIndex, volatile uint32& rRunning, void* arrBuffer, uint32 nBufferSize, uint32 nObjectSize, volatile uint32* arrStates);
private:
SLockFreeSingleLinkedListHeader fallbackList;
struct SFallbackList
{
SLockFreeSingleLinkedListEntry nextEntry;
char alignment_padding[128 - sizeof(SLockFreeSingleLinkedListEntry)];
char object[1]; // struct will be overallocated with enough memory for the object
};
};
} // namespace detail
} // namespace CryMT
@@ -145,9 +145,6 @@ inline void MemoryBarrier() {
typedef int64 __m128;
#endif
#if defined(LINUX64) || defined(APPLE)
unsigned char _InterlockedCompareExchange128(int64 volatile* dst, int64 exchangehigh, int64 exchangelow, int64* comperand);
#endif
//////////////////////////////////////////////////////////////////////////
// io.h stuff
#if !defined(ANDROID)
-318
View File
@@ -29,78 +29,13 @@
#define MULTITHREAD_H_SECTION_IMPLEMENT_CRYINTERLOCKEDCOMPAREEXCHANGE64 8
#endif
#define THREAD_NAME_LENGTH_MAX 64
#define WRITE_LOCK_VAL (1 << 16)
// Traits
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION MULTITHREAD_H_SECTION_TRAITS
#include AZ_RESTRICTED_FILE(MultiThread_h)
#else
#define MULTITHREAD_H_TRAIT_SLOCKFREESINGLELINKEDLISTENTRY_ATTRIBUTE_ALIGN_16 0
#if defined(WIN64)
#define MULTITHREAD_H_TRAIT_SLOCKFREESINGLELINKEDLISTENTRY_MSALIGN_16 1
#endif
#if defined(APPLE) || defined(LINUX)
#define MULTITHREAD_H_TRAIT_USE_SALTED_LINKEDLISTHEADER 1
#endif
#endif
//as PowerPC operates via cache line reservation, lock variables should reside ion their own cache line
template <class T>
struct SAtomicVar
{
T val;
inline operator T() const{return val; }
inline operator T() volatile const{return val; }
inline SAtomicVar& operator =(const T& rV){val = rV; return *this; }
inline void Assign(const T& rV){val = rV; }
inline void Assign(const T& rV) volatile{val = rV; }
inline T* Addr() {return &val; }
inline volatile T* Addr() volatile {return &val; }
inline bool operator<(const T& v) const{return val < v; }
inline bool operator<(const SAtomicVar<T>& v) const{return val < v.val; }
inline bool operator>(const T& v) const{return val > v; }
inline bool operator>(const SAtomicVar<T>& v) const{return val > v.val; }
inline bool operator<=(const T& v) const{return val <= v; }
inline bool operator<=(const SAtomicVar<T>& v) const{return val <= v.val; }
inline bool operator>=(const T& v) const{return val >= v; }
inline bool operator>=(const SAtomicVar<T>& v) const{return val >= v.val; }
inline bool operator==(const T& v) const{return val == v; }
inline bool operator==(const SAtomicVar<T>& v) const{return val == v.val; }
inline bool operator!=(const T& v) const{return val != v; }
inline bool operator!=(const SAtomicVar<T>& v) const{return val != v.val; }
inline T operator*(const T& v) const{return val * v; }
inline T operator/(const T& v) const{return val / v; }
inline T operator+(const T& v) const{return val + v; }
inline T operator-(const T& v) const{return val - v; }
inline bool operator<(const T& v) volatile const{return val < v; }
inline bool operator<(const SAtomicVar<T>& v) volatile const{return val < v.val; }
inline bool operator>(const T& v) volatile const{return val > v; }
inline bool operator>(const SAtomicVar<T>& v) volatile const{return val > v.val; }
inline bool operator<=(const T& v) volatile const{return val <= v; }
inline bool operator<=(const SAtomicVar<T>& v) volatile const{return val <= v.val; }
inline bool operator>=(const T& v) volatile const{return val >= v; }
inline bool operator>=(const SAtomicVar<T>& v) volatile const{return val >= v.val; }
inline bool operator==(const T& v) volatile const{return val == v; }
inline bool operator==(const SAtomicVar<T>& v) volatile const{return val == v.val; }
inline bool operator!=(const T& v) volatile const{return val != v; }
inline bool operator!=(const SAtomicVar<T>& v) volatile const{return val != v.val; }
inline T operator*(const T& v) volatile const{return val * v; }
inline T operator/(const T& v) volatile const{return val / v; }
inline T operator+(const T& v) volatile const{return val + v; }
inline T operator-(const T& v) volatile const{return val - v; }
};
typedef SAtomicVar<int> TIntAtomic;
typedef SAtomicVar<unsigned int> TUIntAtomic;
typedef SAtomicVar<float> TFloatAtomic;
#define __add_db16cycl__ NIntrinsics::YieldFor16Cycles();
void CrySpinLock(volatile int* pLock, int checkVal, int setVal);
void CryReleaseSpinLock (volatile int*, int);
@@ -208,37 +143,6 @@ ILINE void CryReleaseSpinLock(volatile int* pLock, int setVal)
}
//////////////////////////////////////////////////////////////////////////
#if defined(APPLE) || defined(LINUX64)
// Fixes undefined reference to CryInterlockedAdd(unsigned long volatile*, long) on
// Mac and linux.
ILINE void CryInterLockedAdd(volatile LONG* pVal, LONG iAdd)
{
(void) CryInterlockedExchangeAdd(pVal, iAdd);
}
/*
ILINE void CryInterLockedAdd(volatile unsigned long *pVal, long iAdd)
{
long r;
__asm__ __volatile__ (
#if defined(LINUX64) || defined(MAC) // long is 64 bits on amd64.
"lock ; xaddq %0, (%1) \n\t"
#else
"lock ; xaddl %0, (%1) \n\t"
#endif
: "=r" (r)
: "r" (pVal), "0" (iAdd)
: "memory"
);
(void) r;
}*/
/*ILINE void CryInterlockedAdd(volatile size_t *pVal, ptrdiff_t iAdd) {
//(void)CryInterlockedExchangeAdd((volatile long*)pVal,(long)iAdd);
(void) __sync_fetch_and_add(pVal,iAdd);
}*/
#endif
ILINE void CryInterlockedAdd(volatile int* pVal, int iAdd)
{
#ifdef _CPU_X86
@@ -311,123 +215,6 @@ ILINE void CryInterlockedAddSize(volatile size_t* pVal, ptrdiff_t iAdd)
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// CryInterlocked*SList Function, these are specialized C-A-S
// functions for single-linked lists which prevent the A-B-A problem there
// there are implemented in the platform specific CryThread_*.h files
// TODO clean up the interlocked function the same was the CryThread_* header are
//TODO somehow get their real size on WIN (without including windows.h...)
//NOTE: The sizes are verifyed at compile-time in the implementation functions, but this is still ugly
#if MULTITHREAD_H_TRAIT_SLOCKFREESINGLELINKEDLISTENTRY_MSALIGN_16
_MS_ALIGN(16)
#elif defined(WIN32)
_MS_ALIGN(8)
#endif
struct SLockFreeSingleLinkedListEntry
{
SLockFreeSingleLinkedListEntry* volatile pNext;
}
#if MULTITHREAD_H_TRAIT_SLOCKFREESINGLELINKEDLISTENTRY_ATTRIBUTE_ALIGN_16
__attribute__ ((aligned(16)))
#elif defined(LINUX32)
_ALIGN(8)
#elif defined(APPLE) || defined(LINUX64)
_ALIGN(16)
#endif
;
#if MULTITHREAD_H_TRAIT_SLOCKFREESINGLELINKEDLISTENTRY_MSALIGN_16
_MS_ALIGN(16)
#elif defined(WIN32)
_MS_ALIGN(8)
#endif
struct SLockFreeSingleLinkedListHeader
{
SLockFreeSingleLinkedListEntry* volatile pNext;
#if defined(INTERLOCKED_COMPARE_EXCHANGE_128_NOT_SUPPORTED)
// arm64 processors do not provide a cmpxchg16b (or equivalent) instruction,
// so _InterlockedCompareExchange128 is not implemented on arm64 platforms,
// and we have to use a mutex to ensure thread safety.
AZStd::mutex mutex;
#elif MULTITHREAD_H_TRAIT_USE_SALTED_LINKEDLISTHEADER
// If pointers 32bit, salt should be as well. Otherwise we get 4 bytes of padding between pNext and salt and CAS operations fail
#if defined(PLATFORM_64BIT)
volatile uint64 salt;
#else
volatile uint32 salt;
#endif
#endif
}
#if MULTITHREAD_H_TRAIT_SLOCKFREESINGLELINKEDLISTENTRY_ATTRIBUTE_ALIGN_16
__attribute__ ((aligned(16)))
#elif defined(LINUX32)
_ALIGN(8)
#elif defined(APPLE) || defined(LINUX64)
_ALIGN(16)
#endif
;
// push a element atomically onto a single linked list
void CryInterlockedPushEntrySList(SLockFreeSingleLinkedListHeader& list, SLockFreeSingleLinkedListEntry& element);
// push a element atomically from a single linked list
void* CryInterlockedPopEntrySList(SLockFreeSingleLinkedListHeader& list);
// initialzied the lock-free single linked list
void CryInitializeSListHead(SLockFreeSingleLinkedListHeader& list);
// flush the whole list
void* CryInterlockedFlushSList(SLockFreeSingleLinkedListHeader& list);
ILINE void CryReadLock(volatile int* rw, bool yield)
{
CryInterlockedAdd(rw, 1);
#ifdef NEED_ENDIAN_SWAP
volatile char* pw = (volatile char*)rw + 1;
#else
volatile char* pw = (volatile char*)rw + 2;
#endif
uint64 loops = 0;
for (; *pw; )
{
if (yield)
{
# if !defined(ANDROID) && !defined(IOS) && !defined(MULTITHREAD_H_TRAIT_NO_MM_PAUSE)
_mm_pause();
# endif
if (!(++loops & 0x7F))
{
// give other threads with other prio right to run
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION MULTITHREAD_H_SECTION_CRYINTERLOCKEDFLUSHSLIST_PT1
#include AZ_RESTRICTED_FILE(MultiThread_h)
#elif defined (LINUX)
usleep(1);
#endif
}
else if (!(loops & 0x3F))
{
// give threads with same prio chance to run
#if defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION MULTITHREAD_H_SECTION_CRYINTERLOCKEDFLUSHSLIST_PT2
#include AZ_RESTRICTED_FILE(MultiThread_h)
#elif defined (LINUX)
sched_yield();
#endif
}
}
}
}
ILINE void CryReleaseReadLock(volatile int* rw)
{
CryInterlockedAdd(rw, -1);
}
ILINE void CryWriteLock(volatile int* rw)
{
CrySpinLock(rw, 0, WRITE_LOCK_VAL);
@@ -438,78 +225,6 @@ ILINE void CryReleaseWriteLock(volatile int* rw)
CryInterlockedAdd(rw, -WRITE_LOCK_VAL);
}
//////////////////////////////////////////////////////////////////////////
struct ReadLock
{
ILINE ReadLock(volatile int& rw)
{
CryInterlockedAdd(prw = &rw, 1);
#ifdef NEED_ENDIAN_SWAP
volatile char* pw = (volatile char*)&rw + 1;
for (; * pw; )
{
;
}
#else
volatile char* pw = (volatile char*)&rw + 2;
for (; * pw; )
{
;
}
#endif
}
ILINE ReadLock(volatile int& rw, bool yield)
{
CryReadLock(prw = &rw, yield);
}
~ReadLock()
{
CryReleaseReadLock(prw);
}
private:
volatile int* prw;
};
struct ReadLockCond
{
ILINE ReadLockCond(volatile int& rw, int bActive)
{
if (bActive)
{
CryInterlockedAdd(&rw, 1);
bActivated = 1;
#ifdef NEED_ENDIAN_SWAP
volatile char* pw = (volatile char*)&rw + 1;
for (; * pw; )
{
;
}
#else
volatile char* pw = (volatile char*)&rw + 2;
for (; * pw; )
{
;
}
#endif
}
else
{
bActivated = 0;
}
prw = &rw;
}
void SetActive(int bActive = 1) { bActivated = bActive; }
void Release() { CryInterlockedAdd(prw, -bActivated); }
~ReadLockCond()
{
CryInterlockedAdd(prw, -bActivated);
}
private:
volatile int* prw;
int bActivated;
};
//////////////////////////////////////////////////////////////////////////
struct WriteLock
{
@@ -519,15 +234,6 @@ private:
volatile int* prw;
};
//////////////////////////////////////////////////////////////////////////
struct WriteAfterReadLock
{
ILINE WriteAfterReadLock(volatile int& rw) { CrySpinLock(&rw, 1, WRITE_LOCK_VAL + 1); prw = &rw; }
~WriteAfterReadLock() { CryInterlockedAdd(prw, -WRITE_LOCK_VAL); }
private:
volatile int* prw;
};
//////////////////////////////////////////////////////////////////////////
struct WriteLockCond
{
@@ -562,12 +268,6 @@ ILINE int64 CryInterlockedCompareExchange64(volatile int64* addr, int64 exchange
// This is OK, because long is signed int64 on Linux x86_64
//return CryInterlockedCompareExchange((volatile long*)addr, (long)exchange, (long)comperand);
}
ILINE int64 CryInterlockedExchange64(volatile int64* addr, int64 exchange)
{
__sync_synchronize();
return __sync_lock_test_and_set(addr, exchange);
}
#else
ILINE int64 CryInterlockedCompareExchange64(volatile int64* addr, int64 exchange, int64 compare)
{
@@ -583,21 +283,3 @@ ILINE int64 CryInterlockedCompareExchange64(volatile int64* addr, int64 exchange
#endif
}
#endif
//////////////////////////////////////////////////////////////////////////
#if defined(EXCLUDE_PHYSICS_THREAD)
ILINE void SpinLock(volatile int* pLock, int checkVal, int setVal) { *(int*)pLock = setVal; }
ILINE void AtomicAdd(volatile int* pVal, int iAdd) { *(int*)pVal += iAdd; }
ILINE void AtomicAdd(volatile unsigned int* pVal, int iAdd) { *(unsigned int*)pVal += iAdd; }
ILINE void JobSpinLock(volatile int* pLock, int checkVal, int setVal) { CrySpinLock(pLock, checkVal, setVal); }
#else
ILINE void SpinLock(volatile int* pLock, int checkVal, int setVal) { CrySpinLock(pLock, checkVal, setVal); }
ILINE void AtomicAdd(volatile int* pVal, int iAdd) { CryInterlockedAdd(pVal, iAdd); }
ILINE void AtomicAdd(volatile unsigned int* pVal, int iAdd) { CryInterlockedAdd((volatile int*)pVal, iAdd); }
ILINE void JobSpinLock(volatile int* pLock, int checkVal, int setVal) { SpinLock(pLock, checkVal, setVal); }
#endif
ILINE void JobAtomicAdd(volatile int* pVal, int iAdd) { CryInterlockedAdd(pVal, iAdd); }
ILINE void JobAtomicAdd(volatile unsigned int* pVal, int iAdd) { CryInterlockedAdd((volatile int*)pVal, iAdd); }
@@ -94,325 +94,10 @@ namespace CryMT
container_type v;
mutable CryCriticalSection m_cs;
};
//////////////////////////////////////////////////////////////////////////
// Multi-Thread safe vector container, can be used instead of std::vector.
//////////////////////////////////////////////////////////////////////////
template <class T>
class vector
{
public:
typedef T value_type;
typedef CryAutoCriticalSection AutoLock;
CryCriticalSection& get_lock() const { return m_cs; }
void free_memory() { AutoLock lock(m_cs); stl::free_container(v); }
//////////////////////////////////////////////////////////////////////////
// std::vector interface
//////////////////////////////////////////////////////////////////////////
bool empty() const { AutoLock lock(m_cs); return v.empty(); }
int size() const { AutoLock lock(m_cs); return v.size(); }
void resize(int sz) { AutoLock lock(m_cs); v.resize(sz); }
void reserve(int sz) { AutoLock lock(m_cs); v.reserve(sz); }
size_t capacity() const { AutoLock lock(m_cs); return v.size(); }
void clear() { AutoLock lock(m_cs); v.clear(); }
T& operator[](size_t pos) { AutoLock lock(m_cs); return v[pos]; }
const T& operator[](size_t pos) const { AutoLock lock(m_cs); return v[pos]; }
const T& front() const { AutoLock lock(m_cs); return v.front(); }
const T& back() const { AutoLock lock(m_cs); return v.back(); }
T& back() { AutoLock lock(m_cs); return v.back(); }
void push_back(const T& x) { AutoLock lock(m_cs); return v.push_back(x); }
void pop_back() { AutoLock lock(m_cs); return v.pop_back(); }
//////////////////////////////////////////////////////////////////////////
template <class Func>
void sort(const Func& compare_less) { AutoLock lock(m_cs); std::sort(v.begin(), v.end(), compare_less); }
template <class Iter>
void append(const Iter& startRange, const Iter& endRange) { AutoLock lock(m_cs); v.insert(v.end(), startRange, endRange); }
void swap(std::vector<T>& vec) { AutoLock lock(m_cs); v.swap(vec); }
//////////////////////////////////////////////////////////////////////////
bool try_pop_front(T& returnValue)
{
AutoLock lock(m_cs);
if (!v.empty())
{
returnValue = v.front();
v.erase(v.begin());
return true;
}
return false;
};
bool try_pop_back(T& returnValue)
{
AutoLock lock(m_cs);
if (!v.empty())
{
returnValue = v.back();
v.pop_back();
return true;
}
return false;
};
//////////////////////////////////////////////////////////////////////////
template <typename FindFunction, typename KeyType>
bool find_and_copy(FindFunction findFunc, const KeyType& key, T& foundValue) const
{
AutoLock lock(m_cs);
if (!v.empty())
{
typename std::vector<T>::const_iterator it;
for (it = v.begin(); it != v.end(); ++it)
{
if (findFunc(key, *it))
{
foundValue = *it;
return true;
}
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool try_remove(const T& value)
{
AutoLock lock(m_cs);
if (!v.empty())
{
typename std::vector<T>::iterator it = std::find(v.begin(), v.end(), value);
if (it != v.end())
{
v.erase(it);
return true;
}
}
return false;
};
//////////////////////////////////////////////////////////////////////////
template <typename Predicate>
bool try_remove_and_erase_if(Predicate predicateFunction)
{
AutoLock lock(m_cs);
if (!v.empty())
{
typename std::vector<T>::iterator it = std::remove_if(v.begin(), v.end(), predicateFunction);
if (it != v.end())
{
v.erase(it, v.end());
return true;
}
}
return false;
};
//////////////////////////////////////////////////////////////////////////
bool try_remove_at(size_t idx)
{
AutoLock lock(m_cs);
if (idx < v.size())
{
v.erase(v.begin() + idx);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
//Fast remove - just move last elem over deleted element - order is not preserved
bool try_remove_unordered(const T& value)
{
AutoLock lock(m_cs);
if (!v.empty())
{
typename std::vector<T>::iterator it = std::find(v.begin(), v.end(), value);
if (it != v.end())
{
if (v.size() > 1)
{
typename std::vector<T>::iterator it_back = v.end() - 1;
if (it != it_back)
{
*it = *it_back;
}
v.erase(it_back);
}
else
{
v.erase(it);
}
return true;
}
}
return false;
};
vector() {}
vector(const vector<T>& rOther)
{
AutoLock lock1(m_cs);
AutoLock lock2(rOther.m_cs);
v = rOther.v;
}
vector& operator=(const vector<T>& rOther)
{
if (this == &rOther)
{
return *this;
}
AutoLock lock1(m_cs);
AutoLock lock2(rOther.m_cs);
v = rOther.v;
return *this;
}
private:
std::vector<T> v;
mutable CryCriticalSection m_cs;
};
//////////////////////////////////////////////////////////////////////////
// Multi-Thread safe set container, can be used instead of std::set.
// It has limited functionality, but most of it is there.
//////////////////////////////////////////////////////////////////////////
template <class T>
class set
{
public:
typedef T value_type;
typedef T Key;
typedef typename std::set<T>::size_type size_type;
typedef CryAutoCriticalSection AutoLock;
//////////////////////////////////////////////////////////////////////////
// Methods
//////////////////////////////////////////////////////////////////////////
void clear() { AutoLock lock(m_cs); s.clear(); }
size_type count(const Key& _Key) const { AutoLock lock(m_cs); return s.count(_Key); }
bool empty() const { AutoLock lock(m_cs); return s.empty(); }
size_type erase(const Key& _Key) { AutoLock lock(m_cs); return s.erase(_Key); }
bool find(const Key& _Key) { AutoLock lock(m_cs); return (s.find(_Key) != s.end()); }
bool pop_front(value_type& rFrontElement)
{
AutoLock lock(m_cs);
if (s.empty())
{
return false;
}
rFrontElement = *s.begin();
s.erase(s.begin());
return true;
}
bool pop_front()
{
AutoLock lock(m_cs);
if (s.empty())
{
return false;
}
s.erase(s.begin());
return true;
}
bool front(value_type& rFrontElement)
{
AutoLock lock(m_cs);
if (s.empty())
{
return false;
}
rFrontElement = *s.begin();
return true;
}
bool insert(const value_type& _Val) { AutoLock lock(m_cs); return s.insert(_Val).second; }
size_type max_size() const { AutoLock lock(m_cs); return s.max_size(); }
size_type size() const { AutoLock lock(m_cs); return s.size(); }
void swap(set& _Right) { AutoLock lock(m_cs); s.swap(_Right); }
CryCriticalSection& get_lock() { return m_cs; }
private:
std::set<value_type> s;
mutable CryCriticalSection m_cs;
};
///////////////////////////////////////////////////////////////////////////////
//
// Multi-thread safe lock-less FIFO queue container for passing pointers between threads.
// The queue only stores pointers to T, it does not copy the contents of T.
//
//////////////////////////////////////////////////////////////////////////
template <class T, class Alloc = std::allocator<T> >
class CLocklessPointerQueue
{
public:
explicit CLocklessPointerQueue(size_t reserve = 32) { m_lockFreeQueue.reserve(reserve); };
~CLocklessPointerQueue() {};
// Check's if queue is empty.
bool empty() const;
// Pushes item to the queue, only pointer is stored, T contents are not copied.
void push(T* ptr);
// pop can return NULL, always check for it before use.
T* pop();
private:
queue<T*, typename std::allocator_traits<Alloc>::template rebind_alloc<T*>> m_lockFreeQueue;
};
//////////////////////////////////////////////////////////////////////////
template <class T, class Alloc>
inline bool CLocklessPointerQueue<T, Alloc>::empty() const
{
return m_lockFreeQueue.empty();
}
//////////////////////////////////////////////////////////////////////////
template <class T, class Alloc>
inline void CLocklessPointerQueue<T, Alloc>::push(T* ptr)
{
m_lockFreeQueue.push(ptr);
}
//////////////////////////////////////////////////////////////////////////
template <class T, class Alloc>
inline T* CLocklessPointerQueue<T, Alloc>::pop()
{
T* val = NULL;
m_lockFreeQueue.try_pop(val);
return val;
}
}; // namespace CryMT
namespace stl
{
template <typename T>
void free_container(CryMT::vector<T>& v)
{
v.free_memory();
}
template <typename T>
void free_container(CryMT::queue<T>& v)
{
-19
View File
@@ -1377,25 +1377,6 @@ DLL_EXPORT void* CryInterlockedExchangePointer(void* volatile* dst, void* ex
//return (void*)CryInterlockedCompareExchange((long volatile*)dst, (long)exchange, (long)comperand);
}
#if (defined(LINUX64) && !defined(ANDROID)) || defined(MAC) || defined(IOS_SIMULATOR)
DLL_EXPORT unsigned char _InterlockedCompareExchange128(int64 volatile* dst, int64 exchangehigh, int64 exchangelow, int64* comperand)
{
bool bEquals;
__asm__ __volatile__
(
"lock cmpxchg16b %1\n\t"
"setz %0"
: "=q" (bEquals), "+m" (*dst), "+d" (comperand[1]), "+a" (comperand[0])
: "c" (exchangehigh), "b" (exchangelow)
: "cc"
);
return (char)bEquals;
}
#elif defined(INTERLOCKED_COMPARE_EXCHANGE_128_NOT_SUPPORTED)
// arm64 processors do not provide a cmpxchg16b (or equivalent) instruction,
// so _InterlockedCompareExchange128 is not implemented on arm64 platforms.
#endif
threadID CryGetCurrentThreadId()
{
return GetCurrentThreadId();
-4
View File
@@ -34,10 +34,6 @@
#define PLATFORM_64BIT
#endif
#if defined(_CPU_ARM) && defined(PLATFORM_64BIT)
# define INTERLOCKED_COMPARE_EXCHANGE_128_NOT_SUPPORTED
#endif // defined(_CPU_ARM) && defined(PLATFORM_64BIT)
#ifndef MOBILE
#define MOBILE
#endif