From b9acbad0f6cf53354af223c3afa811b912d9e0e7 Mon Sep 17 00:00:00 2001 From: Delgan Date: Sun, 19 Mar 2023 18:46:43 +0100 Subject: [PATCH] Fix possible hang when "complete()" called twice and crashed thread See #647. The initial fix didn't prevented "complete()" to hang if it was called a second time. --- loguru/_handler.py | 6 ++++++ tests/test_add_option_enqueue.py | 2 ++ 2 files changed, 8 insertions(+) diff --git a/loguru/_handler.py b/loguru/_handler.py index 45633140..cb99cf98 100644 --- a/loguru/_handler.py +++ b/loguru/_handler.py @@ -72,6 +72,7 @@ def __init__( self._confirmation_lock = None self._owner_process_pid = None self._thread = None + self._is_thread_dead = None if self._is_formatter_dynamic: if self._colorize: @@ -90,6 +91,7 @@ def __init__( self._confirmation_event = multiprocessing.Event() self._confirmation_lock = multiprocessing.Lock() self._owner_process_pid = os.getpid() + self._is_thread_dead = multiprocessing.Event() self._thread = Thread( target=self._queued_writer, daemon=True, name="loguru-writer-%d" % self._id ) @@ -218,6 +220,8 @@ def complete_queue(self): return with self._confirmation_lock: + if self._is_thread_dead.is_set(): + return self._queue.put(True) self._confirmation_event.wait() self._confirmation_event.clear() @@ -292,6 +296,7 @@ def _queued_writer(self): except Exception: with lock: if not self._error_interceptor.should_catch(): + self._is_thread_dead.set() self._confirmation_event.set() raise self._error_interceptor.print(None) @@ -309,6 +314,7 @@ def _queued_writer(self): self._sink.write(message) except Exception: if not self._error_interceptor.should_catch(): + self._is_thread_dead.set() self._confirmation_event.set() raise self._error_interceptor.print(message.record) diff --git a/tests/test_add_option_enqueue.py b/tests/test_add_option_enqueue.py index 57a1d4d9..cd7abae9 100644 --- a/tests/test_add_option_enqueue.py +++ b/tests/test_add_option_enqueue.py @@ -179,6 +179,7 @@ def test_not_caught_exception_sink_write_then_complete(capsys): with default_threading_excepthook(): logger.bind(fail=True).info("Bye bye...") logger.complete() + logger.complete() # Called twice to ensure it's re-usable. logger.remove() out, err = capsys.readouterr() @@ -194,6 +195,7 @@ def test_not_caught_exception_queue_get_then_complete(writer, capsys): with default_threading_excepthook(): logger.bind(broken=NotUnpicklable()).info("Bye bye...") logger.complete() + logger.complete() logger.remove() out, err = capsys.readouterr()