Remove ConcurrentSystemLocker and use ReadWriteLock instead of better threading synchronization

This commit is contained in:
Wojtek Figat
2025-09-04 14:48:52 +02:00
parent c44d939c08
commit 3e363c8275
16 changed files with 107 additions and 194 deletions

View File

@@ -1,75 +0,0 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#include "ConcurrentSystemLocker.h"
#include "Engine/Platform/Platform.h"
#if !BUILD_RELEASE
#include "Engine/Core/Log.h"
#endif
ConcurrentSystemLocker::ConcurrentSystemLocker()
{
_counters[0] = _counters[1] = 0;
}
void ConcurrentSystemLocker::Begin(bool write, bool exclusively)
{
volatile int64* thisCounter = &_counters[write];
volatile int64* otherCounter = &_counters[!write];
#if !BUILD_RELEASE
int32 retries = 0;
double startTime = Platform::GetTimeSeconds();
#endif
RETRY:
#if !BUILD_RELEASE
retries++;
if (retries > 1000)
{
double endTime = Platform::GetTimeSeconds();
if (endTime - startTime > 0.5f)
{
LOG(Error, "Deadlock detected in ConcurrentSystemLocker! Thread 0x{0:x} waits for {1} ms...", Platform::GetCurrentThreadID(), (int32)((endTime - startTime) * 1000.0));
retries = 0;
}
}
#endif
// Check if we can enter (cannot read while someone else is writing and vice versa)
if (Platform::AtomicRead(otherCounter) != 0)
{
// Someone else is doing opposite operation so wait for it's end
// TODO: use ConditionVariable+CriticalSection to prevent active-waiting
Platform::Yield();
goto RETRY;
}
// Writers might want to check themselves for a single writer at the same time - just like a mutex
if (exclusively && Platform::AtomicRead(thisCounter) != 0)
{
// Someone else is doing opposite operation so wait for it's end
Platform::Yield();
goto RETRY;
}
// Mark that we entered this section
Platform::InterlockedIncrement(thisCounter);
// Double-check if we're safe to go
if (Platform::InterlockedCompareExchange(otherCounter, 0, 0))
{
// Someone else is doing opposite operation while this thread was doing counter increment so retry
Platform::InterlockedDecrement(thisCounter);
goto RETRY;
}
}
void ConcurrentSystemLocker::End(bool write)
{
// Mark that we left this section
Platform::InterlockedDecrement(&_counters[write]);
}
bool ConcurrentSystemLocker::HasLock(bool write) const
{
return Platform::AtomicRead(&_counters[write]) != 0;
}

View File

@@ -1,47 +0,0 @@
// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Core.h"
#include "Engine/Core/Types/BaseTypes.h"
/// <summary>
/// Utility for guarding system data access from different threads depending on the resources usage (eg. block read on write).
/// </summary>
struct ConcurrentSystemLocker
{
private:
volatile int64 _counters[2];
public:
NON_COPYABLE(ConcurrentSystemLocker);
ConcurrentSystemLocker();
void Begin(bool write, bool exclusively = false);
void End(bool write);
bool HasLock(bool write) const;
public:
template<bool Write>
struct Scope
{
NON_COPYABLE(Scope);
Scope(ConcurrentSystemLocker& locker, bool exclusively = false)
: _locker(locker)
{
_locker.Begin(Write, exclusively);
}
~Scope()
{
_locker.End(Write);
}
private:
ConcurrentSystemLocker& _locker;
};
typedef Scope<false> ReadScope;
typedef Scope<true> WriteScope;
};

View File

@@ -3,6 +3,7 @@
#pragma once
#include "Engine/Platform/CriticalSection.h"
#include "Engine/Platform/ReadWriteLock.h"
/// <summary>
/// Checks if current execution in on the main thread.
@@ -10,35 +11,70 @@
FLAXENGINE_API bool IsInMainThread();
/// <summary>
/// Scope locker for critical section.
/// Scope lock for critical section (mutex). Ensures no other thread can enter scope.
/// </summary>
class ScopeLock
{
private:
const CriticalSection* _section;
ScopeLock() = default;
ScopeLock(const ScopeLock&) = delete;
ScopeLock& operator=(const ScopeLock&) = delete;
ScopeLock() = delete;
NON_COPYABLE(ScopeLock);
public:
/// <summary>
/// Init, enters critical section.
/// </summary>
/// <param name="section">The synchronization object to lock.</param>
ScopeLock(const CriticalSection& section)
FORCE_INLINE ScopeLock(const CriticalSection& section)
: _section(&section)
{
_section->Lock();
}
/// <summary>
/// Destructor, releases critical section.
/// </summary>
~ScopeLock()
FORCE_INLINE ~ScopeLock()
{
_section->Unlock();
}
};
/// <summary>
/// Scope lock for read/write lock that allows for shared reading by multiple threads (no writers allowed).
/// </summary>
class ScopeReadLock
{
private:
const ReadWriteLock* _lock;
ScopeReadLock() = delete;
NON_COPYABLE(ScopeReadLock);
public:
FORCE_INLINE ScopeReadLock(const ReadWriteLock& lock)
: _lock(&lock)
{
_lock->ReadLock();
}
FORCE_INLINE ~ScopeReadLock()
{
_lock->ReadUnlock();
}
};
/// <summary>
/// Scope lock for read/write lock that allows for exclusive writing by a single thread (no readers allowed).
/// </summary>
class ScopeWriteLock
{
private:
const ReadWriteLock* _lock;
ScopeWriteLock() = delete;
NON_COPYABLE(ScopeWriteLock);
public:
FORCE_INLINE ScopeWriteLock(const ReadWriteLock& lock)
: _lock(&lock)
{
_lock->WriteLock();
}
FORCE_INLINE ~ScopeWriteLock()
{
_lock->WriteUnlock();
}
};