Given the following code I would expect the code to run for around 6 seconds as I would expect the calls with argument 0 to terminate almost instantly and the calls with argument 100 to timeout after 3 seconds.
import multiprocessing
import threading
import time
import multiprocessing
def timeout_function_wrapper(function, args, timeout):
# Wrapper function to allow for proper results etc.
def auxiliary(n, out_q):
res = function(n)
out_q.put(res) # to get result back from thread target
result_q = multiprocessing.Queue()
thread = threading.Thread(target=auxiliary, args=(args, result_q))
thread.start()
thread.join(timeout=timeout) # if it times out, thread.is_alive() will be True, no exception is raised
if thread.is_alive():
raise multiprocessing.TimeoutError("Timed out after waiting for {}s".format(timeout))
return result_q.get()
def foo(t):
time.sleep(t)
return t*t
if __name__ == '__main__':
t1 = time.time()
with multiprocessing.Pool(processes=1, maxtasksperchild=1) as pool:
futures = [pool.apply_async(timeout_function_wrapper, [foo, i, 3]) for i in [100, 0, 100,0]]
for i, x in enumerate(futures):
try:
res = x.get() # timeout not needed here, it's in the wrapper
print("Res", i, res)
except multiprocessing.TimeoutError as e:
print("Timeout")
print("Took {}".format(time.time() - t1))
It turns out that this code runs for around 103 seconds. Why is that the case? If I change the the line
thread = threading.Thread(target=auxiliary, args=(args, result_q))
to
thread = threading.Thread(target=auxiliary, args=(args, result_q), daemon=True)
it finishes in 6 seconds.
Questions
Why do threads need to be daemon threads to timeout "in the intended way"?
Why does it timeout "correctly" once and not at all once when they are not daemon threads?
UPDATE So in addition I found that if foo is replaced by:
def foo(t):
while True:
pass
return 42
The whole program does not terminate (even after the sum of timeouts) regardless of it being daemon threads or not.