timer not emtting signal when processor busy in for-loop

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Drewton8

I am trying to get a Timer to fire at a set interval, to be used in a progress bar whilst rescaling large images. All the other code works, but the Timer does not seem to fire whilst the CPU is busy processing an image.

As an example, in the sample code below, I was expecting the Timer callback to be executed whilst the for-loops are busy.

extends Node
var time_waster: float
var duration: float = 0.0
var timer: Timer

func _ready():
	timer = Timer.new()
	timer.set_wait_time(0.1)
	timer.autostart = false
	add_child(timer)

	var _error = timer.connect("timeout", self, "_timeout")

	print("Start")
	timer.start()
	for j in range(1,20):
		print("J Loop----------------- ", j)
		for i in range(1,2000000):
			time_waster = log(i*j) # Waste some time

	timer.stop()
	print("End")

func _timeout():
	duration += timer.wait_time 
	print("Timer call back: ",duration)

But the output when running the above code is …

Start
J Loop----------------- 1
J Loop----------------- 2
etc. until
End

The Timer does not fire at all whilst the loops are running ?

I do not see what I am doing incorrectly. Any help appreciated.

Thanks

:bust_in_silhouette: Reply From: timothybrentwood

I think because of all the calculation you are doing you’re not hitting an idle frame allowing the timer to increment. From the docs:

enum  TimerProcessMode:
TIMER_PROCESS_PHYSICS = 0
Update the timer during the physics step at each frame (fixed framerate processing).

TIMER_PROCESS_IDLE = 1
Update the timer during the idle time at each frame.

For what you’re doing you should probably just use a TextureProgress node and update it at the top of your for loop.

var progress_bar = TextureProgress.new()
self.add_child(progress_bar)
progress_bar.max_value = num_things_to_load
for i in range(num_things_to_load):
    progress_bar.value = i
    do_work_here()

Thank you for your answer. I had misunderstood when timers were called, thinking that they were events that ocurred independently of the process load.

I solved my problem by implementing the compute intensive part in a separate thread. The code below works as I originally was expecting, with an output of the form …

Starting thread
Start loop
1
2
Timer: 0.1
Timer: 0.2


9
Timer: 1.9
Timer: 2
Timer: 2.1
End loop
Timer: 2.2
Thread complete
END

 extends Node
    
    var load_time: float
    var timer: Timer
    var thread: Thread
    var compute_complete: bool
    signal thread_complete
    
    func _ready():
    	load_time = 0.0
    
    	timer = Timer.new()
    	timer.set_wait_time(0.1) 
    	add_child(timer)	
    	var _error = timer.connect("timeout", self, "_on_Timer_timeout")
    	timer.start()
    
    	print("Starting thread")
    	thread = Thread.new()
    	thread.start(self,"_compute_function")
    	yield(self,"thread_complete")
    	print("Thread complete")
    
    	timer.stop()
    	print("END")
    
    func _compute_function(userdata):
    	compute_complete = false
    	print("Start loop")
    	for j in range(1,10):
    		print(j)
    		for i in range(1,6000000):
    			var result = log(i*j)
    	print("End loop")
    	compute_complete = true
    
    func _on_Timer_timeout():
    	load_time += timer.wait_time 
    	print("Timer: ",load_time)
    	if compute_complete:
    		thread.wait_to_finish()
    		emit_signal("thread_complete")

Drewton8 | 2021-05-09 16:24