Skip to content

Commit

Permalink
Fix possible hang when "complete()" called twice and crashed thread
Browse files Browse the repository at this point in the history
See #647. The initial fix didn't prevented "complete()" to hang
if it was called a second time.
  • Loading branch information
Delgan committed Mar 26, 2023
1 parent c926fd0 commit b9acbad
Show file tree
Hide file tree
Showing 2 changed files with 8 additions and 0 deletions.
6 changes: 6 additions & 0 deletions loguru/_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions tests/test_add_option_enqueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand Down

0 comments on commit b9acbad

Please sign in to comment.