[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:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user