Deadlock/hang is possible from multi-threaded calling
Closed this issue · 5 comments
I'm afraid I only have a statistical reproduction of this, based on an issue I'm seeing in a real world application.
I ran the following code on Python 3.9, WSL2, AMD processor, and after a few hours it stopped printing.
from pyrate_limiter import Duration, RequestRate, Limiter
import random
import datetime
import concurrent
CONCURRENCY = 32
limiter = Limiter(RequestRate(1000, Duration.SECOND))
@limiter.ratelimit('get_feed_page', delay=True)
def test():
if random.randint(0, 999) == 0:
print(datetime.datetime.now())
while True:
with concurrent.futures.ThreadPoolExecutor(max_workers=CONCURRENCY) as executor:
for _ in range(CONCURRENCY):
executor.submit(test)
The stack trace when I CTRL-C this is:
KeyboardInterrupt Traceback (most recent call last)
Cell In [13], line 4
2 with concurrent.futures.ThreadPoolExecutor(max_workers=CONCURRENCY) as executor:
3 for _ in range(CONCURRENCY):
----> 4 executor.submit(test)
File ~/.pyenv/versions/3.9.7/lib/python3.9/concurrent/futures/_base.py:636, in Executor.__exit__(self, exc_type, exc_val, exc_tb)
635 def __exit__(self, exc_type, exc_val, exc_tb):
--> 636 self.shutdown(wait=True)
637 return False
File ~/.pyenv/versions/3.9.7/lib/python3.9/concurrent/futures/thread.py:229, in ThreadPoolExecutor.shutdown(self, wait, cancel_futures)
227 if wait:
228 for t in self._threads:
--> 229 t.join()
File ~/.pyenv/versions/3.9.7/lib/python3.9/threading.py:1053, in Thread.join(self, timeout)
1050 raise RuntimeError("cannot join current thread")
1052 if timeout is None:
-> 1053 self._wait_for_tstate_lock()
1054 else:
1055 # the behavior of a negative timeout isn't documented, but
1056 # historically .join(timeout=x) for x<0 has acted as if timeout=0
1057 self._wait_for_tstate_lock(timeout=max(timeout, 0))
File ~/.pyenv/versions/3.9.7/lib/python3.9/threading.py:1069, in Thread._wait_for_tstate_lock(self, block, timeout)
1067 if lock is None: # already determined that the C code is done
1068 assert self._is_stopped
-> 1069 elif lock.acquire(block, timeout):
1070 lock.release()
1071 self._stop()
When I try and then exit ipython, it hangs, which would point to one of the threads the ThreadPoolExecutor creates being still live (which the stack trace also points to).
Once I'm free, I will see what I can do
@vutran1710 if you're going to close this, you really should remove threaded support entirely. This bug means this library isn't safe to use in any real world multi-threaded scenario.
@vutran1710 if you're going to close this, you really should remove threaded support entirely. This bug means this library isn't safe to use in any real world multi-threaded scenario.
I made some drastic change and it should not happen again.
@vutran1710 if you're going to close this, you really should remove threaded support entirely. This bug means this library isn't safe to use in any real world multi-threaded scenario.
I made some drastic change and it should not happen again.
Oh that's great news. When you closed it as "not planned" I thought that meant the bug is still present. I'll test with my reproduction and confirm we're good.
@vutran1710 I cannot reproduce with my reproduction in the original issue description.
I'll start using the library in the threaded workloads where I encountered this issue, and if I see any problems I'll open another issue. But it looks like you've fixed this bug; thanks!