Releasing ressources on comm close triggered by frontend
AntoinePrv opened this issue · 0 comments
Problem
When one registers a target, one is supposed to give a callback (comm_opened
) for when a comm is opened by the frontend.
The comm_opened
callback takes the xcomm
as rvalue reference. If the backend expects the comm
to be closed by the frontend, the lifetime is unknown and the comm C++ object has to be stored somewhere (in some sort of registry for instance).
When the frontend do close the comm, we want to delete the C++ xcomm
object (and other related resources).
xcomm::on_close
can be used to set a callback on the xcomm
when it is closed by the frontend.
However that callback is called in xcomm::handle_close
(member function), which means that we would be calling ~xcomm
from within the object.
void comm_opened(xeus::xcomm&& comm, const xeus::xmessage&)
{
// This is a very simple registry for comm since their lifetime is managed by the frontend
static std::unordered_map<xeus::xguid, xeus::xcomm> comm_registry{};
auto iter_inserted = comm_registry.emplace(std::make_pair(comm.id(), std::move(comm)));
assert(iter_inserted.second);
auto& registered_comm = iter_inserted.first->second;
registered_comm.on_message(
[&](const ::xeus::xmessage&) { // Does something }
);
registered_comm.on_close(
[&](const ::xeus::xmessage&)
{
// The comm is destructed from within one of its member function.
comm_registry.erase(registered_comm.id());
}
);
}
Current status
The code above currently works because no other instructions are executed after calling the on_close
callback in handle_close
.
Is this well defined behavior though?
Furthermore, it is a bad design because if any of the user or a Xeus developer, unaware of that pattern, decides to add additional work after the on_close
callback, this will break.
Proposed solutions
Same but more explicit
Keep the solution as it is, but make it more explicit and documented.
For instance there could be an additional callback without a xmessage
argument to avoid confusion.
registered_comm.do_destroy(
[&](){ comm_registry.erase(registered_comm.id()); }
);
Manage (some) xcomm from within Xeus
We could imagine having a
xeus::get_interpreter().comm_manager().register_managed_comm_target
Where the comm_opened
callback would take the xcomm
as a lvalue reference and Xeus would manage its lifetime.
For Xwidgets created from the frontend (which hold a xcomm
), it would mean they should only capture a reference to the xcomm
.