How do thread and wait_to_finish() work?

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

Hello,
i have a custom made path finding algorithm, and since it takes a lot of resources i want it to run in background with a thread.
To avoid the units to stand still too much time the algorithm interrupt himself after a few seconds, it tells what´s the best path up to a certain point, and then evaluate if it is necessary to start another path-finding research to reach the actual target. And so on in a loop.
This is the basic code:

var target
func find_path(start, end):
    target=end
	if not thread.is_active():
		thread.start(self, "calculate", start)
        evaluate_path(thread.wait_to_finish())
    else:
        print("thread still active")
	

func evaluate_path(p):
	if p[p.size()-1]==target:
        unit.start_moving_along(p)
        print("path found")
	elif 0.5*(p[0]-target).lenght()>(p[p.size()-1]-target).length()
        unit.start_moving_along(p)
        print("getting close, continue from the last point of path")
		find_path(p[p.size()-1],target)
	else:
        print("There is no available path")

func calculate(start):
   var path=PoolVector2Array() 
   path=do_the real_calculations(start, target)
   return path

It works fine if the target is reached in the first loop (meaning the sequence of find_path=> calculate=>evaluate).
But for some reason it freeze in the following ones: basically every time it prints path not found yet or getting close it looks like it´s not working on the second thread anymore, because I can no more move the camera and do other in game stuff until the calculation is finished.
Can you help me understand why?
The thread is correctly closed because it doesnt print thread still active


EDIT:
I believe the wait_to_finish() function push another function into the thread: writing evaluate_path(thread.wait_to_finish())mean that the evaluate function is started into the secondary thread as soon as the pathfinding is finished, and since the evaluate_function is linked to the unit movement, and the unit movement is linked to all the game, all the game start running on the secondary thread, therefore it freeze because the next loop of pathfinding is being processed in the same thread of the game.

Can somebody confirm this?
If yes, how the hell can i make this simple sequence to work?
1-get the start and ending point
2-start a thread
3-run path finding algorithm on secondary thread
4-background calculation reach an end, close the thread
5-examine results in main thread,decide if to start again from point 1

:bust_in_silhouette: Reply From: hilfazer

You start a thread and immediately call thread.wait_to_finish(). It lock your main thread until thread is done with its job.

Have a look at thread example in Godot Asset Lib:

You need to do something like this in your worker thread

call_deferred("_bg_load_done")
return tex # return it

The trick is: _bg_load_done will be done in main thread, not worker thread. It’s pretty important.

In _bg_load_done (or whatever name you use) main thread will wait_to_finish

func _bg_load_done():
	# Wait for the thread to complete, get the returned value
	var tex = thread.wait_to_finish()

THANK YOU!
I finally understood how the wait_to_finish() function works, it´s quite tricky!
For anybody that is looking for the same problem, you basically have to call the function that finishes the thread at the end of the thread function using call_deferred, and in that ending function immediatly call wait_to_finish() on the thread.
Something like this:

var thread=Thread.new()

func starting_function()
 thread.start(self, "thread_function", any_value)

func thread_function(value):
 var r=do_your_stuff()
 call_deferred("ending_function")
 return r  #this value will be passed to ending_function

func ending_function():
 var result=thread.wait_to_finish()  #result=r
 do_stuff_with_result()

Andrea | 2018-09-14 16:10

If you don’t need the return value, a quick way to do the same thing would be to call thread.call_deferred("wait_to_finish")

Nicholas Pipitone | 2019-10-22 02:28