Version 1.34.2+ | Critical Feature | Production Ready
The Supabase UE5 Plugin implements comprehensive thread safety across all operations to ensure stable, concurrent execution in Unreal Engine’s multi-threaded environment. This page details the thread safety architecture, implementation patterns, and best practices.
Thread safety is a critical requirement for production-grade Unreal Engine plugins. The Supabase plugin ensures that all operations can be safely executed from any thread while maintaining data consistency and preventing race conditions.
// Atomic state management in AsyncRealtime
std::atomic<ERealtimeConnectionState> ConnectionState;
mutable FCriticalSection ConnectionStateMutex;
mutable FCriticalSection DelegateMutex;
// Thread-safe state changes
void SetConnectionState(ERealtimeConnectionState NewState)
{
ERealtimeConnectionState OldState = ConnectionState.exchange(NewState);
// Safe broadcasting handled automatically
}
void SafeBroadcastOnGameThread(TFunction<void()> BroadcastFunction)
{
if (IsInGameThread())
{
BroadcastFunction();
}
else
{
TWeakObjectPtr<UAsyncRealtime> WeakThis = WeakSelf;
AsyncTask(ENamedThreads::GameThread, [WeakThis, BroadcastFunction]() {
if (WeakThis.IsValid() && !WeakThis->bIsShuttingDown) {
BroadcastFunction();
}
});
}
}
// Thread-safe cleanup with proper shutdown handling
void SafeCleanup()
{
bIsShuttingDown = true;
// Clear timers safely
if (UWorld* World = GetWorldFromContext())
{
FTimerManager& TimerManager = World->GetTimerManager();
// Clear all timers with validation
}
// Close WebSocket connection safely
if (WebSocket.IsValid())
{
WebSocket->OnConnected().Clear();
WebSocket->OnConnectionError().Clear();
WebSocket->Close();
WebSocket.Reset();
}
}
All async operations follow this thread-safe pattern:
class SUPABASE_API UAsyncOperation : public UBlueprintAsyncActionBase
{
private:
// Thread-safe state tracking
FThreadSafeBool bIsActive;
FThreadSafeBool bIsShuttingDown;
// Weak self-reference for safe lambda captures
TWeakObjectPtr<UAsyncOperation> WeakSelf;
public:
virtual void Activate() override
{
WeakSelf = this;
bIsActive = true;
// Execute operation on appropriate thread
PerformOperation();
}
virtual void BeginDestroy() override
{
SafeCleanup();
Super::BeginDestroy();
}
};
The subsystem ensures thread-safe access to shared resources:
// Thread-safe subsystem pattern
UCLASS()
class SUPABASE_API USupabaseSubsystem : public UGameInstanceSubsystem
{
private:
mutable FCriticalSection ClientMutex;
public:
USupabaseClient* GetClient()
{
FScopeLock Lock(&ClientMutex);
return Client;
}
void SetConnectionState(ESupabaseConnectionState NewState)
{
FScopeLock Lock(&ClientMutex);
ConnectionState = NewState;
// Broadcast state change safely
}
};
Thread-safe connection handling:
// Multiple connections with thread safety
class FSupabaseConnectionPool
{
private:
FCriticalSection PoolMutex;
TArray<TSharedPtr<USupabaseConnection>> AvailableConnections;
public:
TSharedPtr<USupabaseConnection> AcquireConnection()
{
FScopeLock Lock(&PoolMutex);
// Thread-safe connection acquisition
}
void ReleaseConnection(TSharedPtr<USupabaseConnection> Connection)
{
FScopeLock Lock(&PoolMutex);
// Thread-safe connection release
}
};
The plugin includes comprehensive thread safety tests:
class SUPABASETESTS_API FThreadSafetyTest : public FSupabaseTestBase
{
public:
void TestConcurrentAsyncOperations()
{
const int32 ThreadCount = 10;
const int32 OperationsPerThread = 100;
TAtomic<int32> SuccessCount{0};
TAtomic<int32> ErrorCount{0};
// Launch concurrent operations
ParallelFor(ThreadCount, [&](int32 ThreadIndex) {
for (int32 OpIndex = 0; OpIndex < OperationsPerThread; OpIndex++)
{
UAsyncOperation* Op = UAsyncOperation::OperationAsync(TestWorld, TestParams);
Op->OnSuccess.AddLambda([&](const FString& Result) { SuccessCount++; });
Op->OnError.AddLambda([&](const FString& Error) { ErrorCount++; });
}
});
// Validate thread safety
WaitForAsyncCompletion();
TestTrue("Thread safety violation",
(SuccessCount + ErrorCount) == (ThreadCount * OperationsPerThread));
}
};
The plugin minimizes mutex overhead through:
Recommended concurrent operation limits:
| Operation Type | Max Concurrent |
|---|---|
| Query Operations | 50 |
| Realtime Connections | 10 |
| File Uploads | 5 |
| Authentication | 10 |
// DO: Use weak references in lambdas
TWeakObjectPtr<UMyActor> WeakThis = this;
AsyncOp->OnSuccess.AddLambda([WeakThis](const FString& Result) {
if (WeakThis.IsValid()) {
// Safe to use WeakThis
}
});
// DON'T: Capture raw pointers
AsyncOp->OnSuccess.AddLambda([this](const FString& Result) {
// Potentially unsafe if object is destroyed
});
// DO: Use atomic operations for simple state
std::atomic<bool> bIsConnected{false};
// DO: Use mutex for complex state
FCriticalSection StateMutex;
void UpdateComplexState()
{
FScopeLock Lock(&StateMutex);
// Modify complex state safely
}
// DO: Always broadcast on game thread
SafeBroadcastOnGameThread([this]() {
OnSuccess.Broadcast(Result);
});
// DON'T: Direct broadcast from worker threads
OnSuccess.Broadcast(Result); // Potentially unsafe
// Enable detailed thread safety logging
SUPABASE_LOG(LogSupabaseRealtime, Log,
TEXT("Thread ID: %d, Operation: %s"),
FPlatformTLS::GetCurrentThreadId(),
*OperationName);
#if WITH_EDITOR
checkf(IsInGameThread(),
TEXT("Operation must be called from game thread"));
#endif
Use Unreal’s built-in profiling tools to monitor:
Symptom: Random crashes during event broadcasting
Solution: Always use SafeBroadcastOnGameThread()
Symptom: Connection state out of sync
Solution: Use atomic operations for state changes
Symptom: Memory usage grows with concurrent operations
Solution: Ensure proper cleanup in BeginDestroy()
Symptom: Lost connections under high load
Solution: Implement proper connection pooling
Version 1.34.2+ introduces enhanced thread safety:
// Old pattern (unsafe)
AsyncOp->OnSuccess.AddDynamic(this, &AMyActor::OnSuccess);
// New pattern (thread-safe)
TWeakObjectPtr<AMyActor> WeakThis = this;
AsyncOp->OnSuccess.AddLambda([WeakThis](const FString& Result) {
if (WeakThis.IsValid()) {
WeakThis->OnSuccess(Result);
}
});
For thread safety issues or questions:
This documentation is maintained for Supabase UE5 Plugin v1.34.2+. For the latest information, always refer to the current version documentation.