Is it possible to manually clear all pointers pointing to a specific UObject?

We are designing an Object Pool system, but we have encountered the following problem:

- Suppose a class contains a pointer to a Pooled Object

- When this Object is recycled by the Pool, we want to find all pointers pointing to it and set them to nullptr to prevent that object from holding a pointer to an already recycled Object.

Is there a way to do this?

[Attachment Removed]

Hi there,

Clearing all pointers to an object is not supported by any unreal features or c++ features. You can almost achieve this by utilizing smart pointers however in your case where you will be re-using the objects that’s not applicable.

However, since you are implementing an object pool there are some options to achieve an effect like this. I would recommend using some sort of handle that allows classes outside the pool to get access to one of the objects. Somewhat similar to the FTimerManager. for example, a handle struct with a pointer to the object and a unique identifier that changes every time the object is reused. Classes outside the pool can use the handle to request access to the object in question, (checking against the unique id) until it is reused and the unique id is changed.

I hope that give you an idea on how to proceed,

Louis

[Attachment Removed]

Hi Louis!

Really thanks for your reply.

First, thanks for your handle class idea. And I think this is a really good way to solve this problem.

>Clearing all pointers to an object is not supported by any unreal features or c++ features.

I think UObject-GC system can partially achieve this target? It can clear all UPROPERTY-marked pointer to a destroyed object.

My problem is related to this case, for example, a collision trigger class has a raw AActor pointer:

UPROPERTY()
AActor* actor;

​This may references a normal actor or a pooled actor.

  • If it is a normal actor, destroying this actor will trigger the GC to auto clear ‘actor’ pointer
  • But if it is a pooled actor, I cannot achieve the same thing
  • And if I choose the handler wrapper class, I need two properties like this for every class which want to ref both normal and pooled actors.
UPROPERTY()
AActor* nonPooledActor;
 
UPROPERTY()
FPooledActorHandle pooledActor;

I want to know if there is a better way to solve this problem.

For example, can we reuse the auto (UPROPERTY-marked) pointer clear feature from the GC system?​

Thanks!

[Attachment Removed]

Thank you for your update. I have a clearer picture of what you are looking for. I would like to spend some more time looking into this to get you a better answer regarding the GC system.

In the mean time I think that the handle system is still the best way forward for your pooled objects. The way the GC nulls pointers works is by storing a list of all UPROPERTY refs and iterating through them. If the only way for classes to access the pooled objects is through the pool or a manager class you could essentially replicate that method by invalidating any stale handles to pooled objects.

Cheers,

Louis

[Attachment Removed]

Hi again, I’ve been looking a bit more into the GC’s “Pointer Clear” feature. It utilizes global variable GUObjectArray which contains all active objects that can be tracked by the engine. When an object is being destroyed/garbage collected GUObjectArray is iterated over checking for references to the object. I think it would be a bit overkill to try and leverage this for your purposes. With proper architecture the structure handle method can achieve this without having to walk through every active object.

Alternatively, I think there is actually a cleaner system for achieving your desired outcome by utilizing Weak Pointers:

  1. Store all objects in the pool using TObjectPtr<>.
  2. Store active objects in an array of TSharedRef<>.
  3. Any class that needs access to an object gets a weak pointer to the shared ref.
  4. Once the object is returned to the pool, reset the shared ref.

e.g

class UMyObject : public UObject
{
 
UPROPERTY()
TArray<TObejctPtr<UMyObject> Pool;
 
UPROPERTY()
TArray<TSharedRef<UMyObject>> ActiveObjects;
 
TWeakPtr<UMyObjectType> GetObjectFromPool()
{
      // Get an opbject from the pool (or create a new one).
      // Create a shared ref to it and store it in the active obejcts
      // Create a weak ptr to the shared ref and return it.
};
 
void ReturnObjectToPool(TWeakPtr<UMyObjectType> Object)
{
    TSharedRef<UMyObject> ActiveObject = //--> Find and remove the object from your active objects
     ActiveObject.Reset();
};
 
};

Similar to how UProperty references are nulled when an object is garbage collected, This should null any weak pointers to that shared ref. WeakPtrs do not have ownership over an object so they wont count towards keeping it alive either which is something you will need to be aware of for objects not managed by your pool.

- Louis

[Attachment Removed]

Hello Louis!

First, just about your example:

I’m not sure if I’m right, but based on my test,

	UPROPERTY()
	TArray<TSharedRef<UObject>> ActiveObjects;

Will cause a UHT error:

error : Unable to find ‘class’, ‘delegate’, ‘enum’, or ‘struct’ with name ‘TSharedRef’

I don’t think I can use TSharedRef for UObjects. Although I know how to use TWeakObjectPtr. And so it is impossible to use as a property class in blueprints.

I think I need to recall the design targets, although these are hard to achieve all, so ‘it is impossible’ is also an acceptable answer:

  • We need to unify the reference to both pooled and non-pooled objects. A UPROPERTY-marked pointer is OK and used as an example, but if there is another solution please let me know.
  • We need to null out the pointers when recycle a pooled object, just like the UObject GC system did.

I can totally understand the reason that you recommend the handle struct solution. To be honest, as a programmer I agree with you. And I can design a handle wrapper based pool system so let’s just temporally skip this way.

To let you can get why we prefer a unified reference system, let’s image our designer are creating a BP, they need a way to save a reference to an Actor.

> ‘You need to check if this actor is pooled or not, and prepare two member variables to store the right one’

​I don’t think our designers are like this kind of annoying rule.

That’s why I’m thinking about re-use the GC’s null pointer logic. So my question becomes: can you tell me some functions to check as a start point?

I think the function I’m looking for is:

  • Give one UObject
  • Tell me all the UPROPERTY-marked pointer properties
  • Set these to nullptr

Really thanks for all your help!

Luo

[Attachment Removed]

Hi again,

To respond to the first part of your message:

On the topic of TSharedRef<UObject> you may need to use Shared Pointers instead of Shared references. However in both cases they are not able to be exposed to blueprints which is the cause for the error:

> error : Unable to find ‘class’, ‘delegate’, ‘enum’, or ‘struct’ with name ‘TSharedRef’

If exposing the pointers to blueprint is required and you want to use this method you would need some sort of workaround like providing a getter function to convert the weak pointer to something usable in blueprint.

For a place to start looking into the object nulling you will want to look into CollectGarbageInternal() in GarbageCollection.cpp. But more specifically I think you should look into is the class TFastReferenceCollector which is used by the GC to collect the references for nulling. You should be able to look into how it is used by GC to get an understanding of how to use it in your case.

I hope that helps,

Louis

[Attachment Removed]

Hi Louis!

Really thanks for all your help. I think I got the answer I want from you.

I will mark your answer as the best, and close this issue.

Thanks again!

[Attachment Removed]