How do i clear a SetTimer from within the SetTimer?

int32 counter = 0;
FTimerHandle TimerHandle; 
GetWorld()->GetTimerManager().SetTimer(TimerHandle, [counter]() { 
    counter++;
    if (counter> 20)
    GetWorld()->GetTimerManager().ClearTimer(TimerHandle);
} }, 0.1, true);

Till this day im still not able to make a SetTimer clear from within itself.
I tried a lot of things. Like passing the FTimerHandle by reference. But it doesnt work.
Are SetTimers never supposed to be cleared from inside?

In this case, what you can try is clearing the timer before you set it, then invalidate it afterwards.

1 Like

I dont understand it.
Clearing then invalidate it?
If i clear it it will stop it?
Could you give an example?

Your lambda is not capturing counter by reference, and is not capturing TimerHandle at all.
Do you also have a member named TimerHandle? Else I don’t see how it would even compile.

Also, with proper compiler warnings, you should be told about this:

jwatte@jwatte-M2 /tmp % clang++ -o foo foo.cpp -std=c++14
foo.cpp:7:30: error: cannot assign to a variable captured by copy in a non-mutable lambda
    doit([counter](){ counter++; });
                      ~~~~~~~^

1 Like

working Lambda with timer

inside of header

	UPROPERTY()
		FTimerHandle TimerHandle;

	UPROPERTY()
	int32 myVar = 0;
	

cpp in your function

AMyActor::StartTimer(){
	myVar = 0;
	
	GetWorld()->GetTimerManager().SetTimer(TimerHandle, FTimerDelegate::CreateLambda([this]() {						
		GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Green, FString::FromInt(myVar));
		
		myVar++;
		if (myVar > 20) {
			this->GetWorld()->GetTimerManager().ClearTimer(TimerHandle);
			GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Green, "COMPLETE");			
		}
		}), 0.1, true,-1.0f);
}

I tried creating the handle and number variable as a local variable but you can’t get it out of the internal scope and even if you pass the handle by reference in the square brackets it would not cancel the timer.

If the number and handle are kept inside the actor all works well.

1 Like

So that needs a global variable for each timer i create, right?
There’s no way to just pop timers as you go.
Like the way i actually got it to work, is by doing it like recursively:

void ADummy::RecursivePoney(int32& counter) {
FTimerHandle TimerHandle; 
GetWorld()->GetTimerManager().SetTimer(TimerHandle, [counter]() { 
    counter++;
    if (counter> 20)
    return;
    RecursivePoney(counter);
} }, 0.1, false);

}

So basically. When i call the function, will make it timer to call it again. Then if the counter is above 20, will return and not call it again.
Though this still needs a global function of RecursivePoney.
But it can be called many times. So its not creating an FTimerHandle for each time you need a bunch of RecursivePoney’s.

Here’s a timer lambda which will removes itself from the timer manager:

TSharedPtr<FTimerHandle> TimerHandle = MakeShared<FTimerHandle>();
int32 Counter = 0;
World->GetTimerManager().SetTimer(*TimerHandle, [TimerHandle, Counter, World] () mutable
{
	UE_LOG(LogTemp, Display, TEXT("Counter=%d"), Counter);
	if (++Counter > 20)
	{
		UE_LOG(LogTemp, Display, TEXT("ClearTimer"));
		World->GetTimerManager().ClearTimer(*TimerHandle);
	}
}, 0.1f, true);

First thing is to handle the “autodelete” part, in order to remove a timer you need access to the FTimerHandle at the time you want to remove it (which here is in the lambda). Therefor you need to capture the handle.
The trick is you can’t just capture a reference on a stack variable as it will be go out of scope at the end of the function setting the timer. So you need something with a greater lifespan which leaves : global variable, class member and heap allocated.
I guess from the previous answers you don’t want to polute other scopes so we will just go with heap allocation and the options are :

  • Raw pointer : always works but we are in 2023 :slight_smile:
  • TUniquePtr : would be my go to choice, but it would make the lambda non-copyable
  • TSharedPtr : heavier than TUniquePtr but in this case it seems to be the only option

We will then allocate a TSharedPtr and copy it in the lambda this way it can be used in the calling function to setup the timer and from the lambda to clear it, the pointer will be deleted when the timer is cleared and the lambda destroyed.

The second part is the counter which we need to increment when the timer tick and is used to determine when to clear the timer. Again we can’t just capture a reference on it, so we need to copy it in the lambda. By default lambda are const, in order to be able to increment the counter you need to change this behavior by specifing the mutable keyword in the lambda definition.

2 Likes

this is great.
Very complex.
But so far the only thing we have.
I’ve been trying to figure this one out for a while.
Thanks a lot.