glenfant / stopit

Raise asynchronous exceptions in other thread, control the timeout of blocks or callables with a context manager or a decorator
MIT License
110 stars 21 forks source link

Trouble Nesting stopit #17

Open 8bit-pixies opened 6 years ago

8bit-pixies commented 6 years ago

Hi,

I'm having a bit of trouble nesting timeouts. Platforms tested:

My shortest reproducible solution is here:

from stopit import ThreadingTimeout as Timeout
import time

def print_stuff(idx):
    while True:
        time.sleep(1)
        print(idx)

def test_this():
    t0 = time.time()
    for idx in range(5):
        with Timeout(5) as timeout_inner:
            print_stuff(idx)
    return time.time() - t0

with Timeout(10) as timeout_outer:
    res = test_this()

assert res < 15

Where the output is:

0
0
0
0
1
1
1
1
2
2
2
2
3
3
3
3
4
4
4
4
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-28-abee5adecac5> in <module>()
     18     res = test_this()
     19 
---> 20 assert res < 15

AssertionError: 
glenfant commented 6 years ago

Please read the README section about the timeout accuracy issue when using thread blocking funcs like time.sleep(xxx) within a Timeout context manager.

8bit-pixies commented 6 years ago

I don't believe this is a timing issue.

My point is that nesting threading blocks literally do not work. Could you provide a pattern or an example where it does work?

I could replace the for loop with an infinite loop and it would never terminate.

As an example this would never terminate

from stopit import ThreadingTimeout as Timeout
import time

def print_stuff(idx):
    while True:
        time.sleep(1)
        print(idx)

def test_this():
    t0 = time.time()
    while True:
        with Timeout(5) as timeout_inner:
            print_stuff(1)
    return time.time() - t0

with Timeout(10) as timeout_outer:
    res = test_this()
lpyparmentier commented 3 years ago

Hi, I had the same issue, and it's related with the fact that the ThreadingTimeout set a timer on the same thread_id (https://github.com/glenfant/stopit/blob/master/src/stopit/threadstop.py#L45). Only way to solve this is to set-up different thread, or find a way to raise different exception per context manager.