Skip to content

fix: Hold pybmq::Session by shared_ptr in _ext.Session - #112

Merged
pniedzielski merged 1 commit into
bloomberg:mainfrom
thecityofguanyu:session-shared-ptr
Sep 28, 2026
Merged

pniedzielski merged 1 commit into
bloomberg:mainfrom
thecityofguanyu:session-shared-ptr

Conversation

@thecityofguanyu

Copy link
Copy Markdown
Contributor

Summary

Session.stop() stops the underlying pybmq::Session but does not destroy it. Destruction is left to _ext.Session.__dealloc__, which runs whenever the garbage collector gets to it, on whichever thread triggers the collection.

Destroying a pybmq::Session blocks until the libbmq FSM thread finishes stopping it, and the FSM thread logs through the Python logging module before it can finish.

If the collection runs on a thread that holds a logging.Handler lock, the destructor waits for the FSM thread and the FSM thread waits for the lock, and the process deadlocks. Any reference cycle hands a stopped Session to the collector. A traceback retained after an exception is a common one.stop().

Proposed Changes

Hold the pybmq::Session via shared_ptr and release it in stop(), so a Session used as a context manager is destroyed when the with block exits.

Each method takes its own copy of the pointer before calling into C++, so a concurrent stop() cannot destroy the session while it is in use. A method called after stop() still raises the same Error as before.

Unit tests have been added to validate this behavior.

`Session.stop()` stops the underlying `pybmq::Session` but does not
destroy it.  Destruction is left to `_ext.Session.__dealloc__`, which
runs whenever the garbage collector gets to it, on whichever thread
triggers the collection.

Destroying a `pybmq::Session` blocks until the libbmq FSM thread
finishes stopping it, and the FSM thread logs through the Python
`logging` module before it can finish.  If the collection runs on a
thread that holds a `logging.Handler` lock, the destructor waits for
the FSM thread and the FSM thread waits for the lock, and the process
deadlocks.  Any reference cycle hands a stopped `Session` to the
collector; a traceback retained after an exception is a common one.

This patch holds the `pybmq::Session` by `shared_ptr` and releases it
in `stop()`, so a `Session` used as a context manager is destroyed
when the `with` block exits.  Each method takes its own copy of the
pointer before calling into C++, so a concurrent `stop()` cannot
destroy the session while it is in use.  Calling a method after
`stop()` now raises `Error`.

Signed-off-by: Chris A. Evans <thecityofguanyu@outlook.com>
@thecityofguanyu
thecityofguanyu requested a review from a team as a code owner September 28, 2026 15:54
@pniedzielski
pniedzielski self-requested a review September 28, 2026 17:21
@pniedzielski pniedzielski self-assigned this Sep 28, 2026
@pniedzielski pniedzielski added the skip news No news entry is required label Sep 28, 2026

@pniedzielski pniedzielski left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks for this!

@pniedzielski
pniedzielski merged commit 1b7e6ea into bloomberg:main Sep 28, 2026
33 of 34 checks passed
@thecityofguanyu
thecityofguanyu deleted the session-shared-ptr branch September 28, 2026 19:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip news No news entry is required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants