def test_run_more_than_queue_size(self): """ Makes sure that the executor will run more jobs that the queue size, which ensures that the semaphore gets released, even if an exception is thrown. """ raw_executor = futures.ThreadPoolExecutor(1) bounded_executor = BoundedQueueExecutor(raw_executor, 5) class Counter(object): """ Counts how many times run() is called. """ def __init__(self): self.counter = 0 def run(self): """ Always increments the counter. Sometimes raises an exception. """ self.counter += 1 if self.counter % 2 == 0: raise Exception('test') counter = Counter() for _ in six.moves.range(10): bounded_executor.submit(counter.run) bounded_executor.shutdown() self.assertEqual(10, counter.counter)
def test_wait_for_running_jobs(self): """ Makes sure that no more than queue_limit workers are running at once, which checks that the semaphore is acquired before submitting an action. """ raw_executor = futures.ThreadPoolExecutor(2) bounded_executor = BoundedQueueExecutor(raw_executor, 1) assert_equal = self.assertEqual class CountAtOnce(object): """ Counts how many threads are running at once. There should never be more than 1 because that's the limit on the bounded executor. """ def __init__(self): self.running_at_once = 0 self.lock = threading.Lock() def run(self): with self.lock: self.running_at_once += 1 assert_equal(1, self.running_at_once) # While we are sleeping here, no other actions should start # running. If they do, they will increment the counter and # fail the above assertion. time.sleep(0.05) with self.lock: self.running_at_once -= 1 self.counter += 1 if self.counter % 2 == 0: raise Exception('test') count_at_once = CountAtOnce() for _ in six.moves.range(5): bounded_executor.submit(count_at_once.run) bounded_executor.shutdown()