Most solutions for timeout context uses signals. But signals get overwritten and only the last signal handler remains. The consequence is that you cannot use a timeout context if the code is already in a timeout context (that could have been started by you, another developer, a library, a framework, who knows?).
So, basically my over-simplified use-case is this:
with Timeout(1): #A
with Timeout(100): #B
sleep(10) #C
print('Fail')
This code should never print "Fail". But every implementation of Timeout() I've seen so far does the same thing:
- Timeout A starts a timeout context
- Timeout B starts a new timeout context (overwriting the Timeout context from A)
- The timeout never occurs, since the computing in C takes less than 100s
- The execution resumes without timeout context and last more than 1s
The previous example seems a little bit stupid, but the real use case is more like:
with Timeout(30):
for a in list:
with Timeout(10):
heavy_computing(a)
I don't know the length of "list", I don't want to take more than 10s per element of the list and no more than 30s in total.
So my question is: What would be the best implementation of the Timeout() context that doesn't use signal and have support for embeded contexts inside contexts?
Edit:
- The solution doesn't have to be thread-safe, just safe from another Timeout context from an higher level of scope.
- If possible, I'd prefer a solution that is the less invasive possible, as I may use it to call a library that I didn't wrote.
Edit2:
Ok, a little clarification concerning how I consider the Timeout. For me Timeout is a non-intrusive solution to return a partial result (or no result at all) if a black-box operation takes too long. The result must be returned fastly but can be incomplete.
def function(argument):
results = []
with Timeout(50):
for a in arguments:
with Timeout(20):
results.append(black_box(a))
return results
If the function takes more than 50s, then the execution is aborted and an incomplet result is returned. And if black_box takes more than 10s, it will be skipped to go on with the next result.
Example: arguments = [a, b, c, d] black_box(a) takes 25s and returns w black_box(b) takes 20s and returns x black_box(c) takes 5s and returns y black_box(d) takes 10s and returns z results = [x, y]
explanations: a took more than 20s and was skipped b took 20s more seconds (40s in total) and was computed successfully c took 5s more seconds (45s in total), still less than 50s d was aborted because of the 50s timeout
The function blackbox is not aware that it is called from a Timeout context and does not contains timeout specific code.
This code is an exemple and may be a little different. Maybe we need some try.. except to catch timeout errors, etc. But black_box can't be modified.