UObjects can be multithreaded in a limited way. They can be constructed on worker threads and StaticFindObject
(and all FindObject
helper functions) can be called on any thread. In all cases any interaction with UObjects outside of the Game (main) Thread should be encapsulated in FGCScopeGuard
scope:
UPackage* Package = nullptr;
{
SCOPED_LOADTIMER(CreateLinker_CreatePackage);
**FGCScopeGuard GCGuard;
Package = CreatePackage(*Desc.Name.ToString());**
…
}
FGCScopeGuard
ensures that within its scope Garbage Collection is not running. It is important because in scenarios when a new object is constructed or an object is returned from StaticFindObject
call which is not otherwise reachable it can be destroyed before it can be made referenced by a reachable object. It is also dangerous to change UObject
state while GC is running (setting flags that may affect GC).
Since FGCScopeGuard
prevents GC from running it’s important to keep these scopes as small and fast to execute as possible since they may affect performance of the Game Thread if GC attempts to run at the same time. Note that if Garbage Collector attempts to run while another thread is in FGCScopeGuard
scope, GC will be skipped until the next frame. This only applies to incremental GC during gameplay (non incremental GC will just block the Game Thread) and GC can be skipped up to 10 times. If GC is unsuccessful after 10 attempts it will block the Game Thread until it can be executed. If worker threads need to lock FGCScopeGuard
for a significant amount of time they can periodically check if GC wants to run by calling IsGarbageCollectionWaiting()
function and if it returns true they can release the lock and allow GC to run. AsyncLoading.cpp
code is a good example of how this is achieved.