I’ve been trying to determine how legal it is to create a UObject in a worker thread and pass it (via a task) to be consumed by the main thread.
On the face of things, it actually seems possible. During the creation of an object through NewObject, a check is made to see if the current thread is the game thread; if not, the object is given the flag RF_Async, which is among the flags that inhibit garbage collection. So as long as the main thread made sure to clear the RF_Async flag after consuming the object, it seems that everything would be great.
But!! FAsyncObjectReferencer::VerifyAssumptions() churns through every allocated UObject and verifies that any UObject with RF_Async is in its ReferencedObjects set. The FAsyncLoadingThread seems to be the only execution unit privileged to add objects to this set. So although NewObject won’t make a fuss, it looks somewhat like the only entity that’s supposed to create a UObject off the game thread is the asynchronous loader.
Is that really the case? Should other threads not create objects, or are the checks performed by FAsyncObjectReferencer::VerifyAssumptions() (which only seem to happen if manually invoked) overly restrictive?
My eventual code didn’t need to create UObject-derived classes, so I didn’t delve further. My impression is that creating UObjects outside the main thread and outside the machinery of FAsyncLoading isn’t (or wasn’t at the time) a planned or supported use case. Better to create a non-UObject object in the worker thread to hold the relevant data, then construct a UObject around it in the main thread.
Thanks! I ended up going with the clearing of the RF_Async flag after passing the object to the main thread. Haven’t seen any adverse effects yet. Creating the UObjects on the main thread seems like the right long term solution but would require the full re-write of a system with only days before a deadline.
Hey guys, I want to make a simple virtual texturing mechanism, it requires to create UTexture2D on a worker thread. UTexture2D sadly is an UObject, and when I creating it in a thread it never gets deallocated. Did your removal of rf_async flag worked well?
or is it still not worked well woth GC ?
I wish there was no GC for certain tasks or atleast I could mark UObjects for GC or not for GC when I want…
Sadly UE4 is assumption based sdk
I know the thread is a bit dead but as I received information from UDN I think it’s better to share the information. Hope it will help someone.
I specifically asked the question on UDN and here is the following answer:
We also know that we are in another thread (not the game thread) when we create that new UObject
Only the async loading thread should
create UObjects on another thread.
That code takes great care to manage
concurrency. If you manually
NewObject() on any thread than the
game thread, you can expect a crash at
some point. UObject interaction is not
thread safe and should only be done on
the game thread. There are some very
carefully managed places that the
render thread touches UObjects, but
those are carefully managed
Then I asked more information:
We are creating UObject outside of the game thread:
And received that answer:
It’s never safe to do this. The fact
it crashes later is irrelevant. It’s a
race condition and you are getting
lucky in that it doesn’t crash
immediately. There’s a great chance
you are stomping memory and eventually
stomp it in a way that causes a crash
It is fine for UTexture though the race condition is with GC, but UTexture2D marks itself with async flag - means GC will ignore it - it doesnt tick etc - so it is a special case where it is actually ok.
I will take a look at the code later, ad far as I remember creating UObject has an assert for creation in Background threads. For UTextures - I believe it was a must, to allow creating textures off thread, Id suspect that TextureStreamer does this.
Many thanks for your answer.
So, I can create UObjects, set references if all the objects involved aren’t main-thread-accessible objects, right?
My case is reading from a Database, translate that into Objects, set the reference for those created objects between them (if needed) and once everything is created and assigned switch to Game Thread and set them to main-thread-accessible objects.
Some murmuring on UDN as of a year ago suggests that creating UObjects from worker threads (and not just the async loading thread) is now supported. The Async object flag will be set automatically, and should be manually unset once the object’s referenced from the root set (or once you’ve kept it from being GCable through some other means). Notably, it’s NOT okay for the worker thread to put a reference to that object in a main-thread-accessible object, as the GC might be running and get confused by it.
It is now possible to create UObject outside of the Game thread but there are a couple things to care about. This functionality is used by the streaming system. The most important thing is that UObjects wiil be automatically flagged with EInternalObjectFlags::Async. This prevent the Garbage Collector from destroying the objet during its creation. It’s up to you to clear that flag once you are done with the creation.