import gc import queue import threading try: from time import monotonic as time except ImportError: from time import time from concurrent.futures import ThreadPoolExecutor def hacked_Queue_get(self, block=True, timeout=None): '''Remove and return an item from the queue. If optional args 'block' is true and 'timeout' is None (the default), block if necessary until an item is available. If 'timeout' is a non-negative number, it blocks at most 'timeout' seconds and raises the Empty exception if no item was available within that time. Otherwise ('block' is false), return an item if one is immediately available, else raise the Empty exception ('timeout' is ignored in that case). ''' with self.not_empty: gc.collect() if not block: if not self._qsize(): raise queue.Empty elif timeout is None: while not self._qsize(): self.not_empty.wait() elif timeout < 0: raise ValueError("'timeout' must be a non-negative number") else: endtime = time() + timeout while not self._qsize(): remaining = endtime - time() if remaining <= 0.0: raise queue.Empty self.not_empty.wait(remaining) item = self._get() self.not_full.notify() return item def run(): queue.Queue.get = hacked_Queue_get executor = ThreadPoolExecutor(max_workers=1) executor._myself = executor # create cycle event = threading.Event() # This will probably run before we can exercise the hang, so we # force synchronization executor.submit(event.wait) # Add a job that will cause a `Queue.get()` after our reference to # the executor is deleted executor.submit(print, "You won't see me") # Remove strong reference, creating cyclic garbage del executor # Allow the executor to continue event.set() # Hang if __name__ == "__main__": run()