Hello, My Panel application uses a library that creates threads to perform periodic tasks in the background. Is there a way to register a shutdown hook in Panel so I can stop the background tasks before exiting? Currently I have to kill -9 the application to exit, which is not very nice. Thanks in advance for any suggestions.
Hi @szampier
I don’t have the answer, but I would see if there is an answer in
pn.state.on_...
methods- Set Up Manual Threading — Panel v1.6.2 how-to guide.
Thank you @Marc!
The link you sent on setting up manual threading is very useful but it also shows the lack of a global on_exit
callback. The Session Thread example uses pn.state.on_session_destroyed
callback to terminate the threads, but there is no such callback in the Global Thread case. Interestingly, the GlobalTaskRunner
class defines a remove_all
class method which is never invoked. The workaround is to use daemon
threads which are abruptly stopped at shutdown but this is not always desirable if one wants to gracefully terminate ongoing tasks or release resources when the application exits.
@szampier I think you understanding threading better than I do . I wrote the guide to at least have some starting point for users.
It would benefit Panel and its users greatly if you created a Feature Request with the needs you have including callback and documentation. If you are up for it a PR to improve the how-to guide, it would be greatly appreciated.
For example daemon threads was the workaround I could find to make the server shut down when I wanted to stop it. But I have never understood what they are.
Hi @Marc, I’m not sure if I can improve the how-to guide which is already very well done and super useful like the rest of the Panel documentation. However, I would like to suggest adding a new on_exit
method to the state
class to register custom callbacks to be called when the server exits. Below is a sketch of a possible implementation that I have tested locally and it seems to work fine.
# module state.py
class _state(param.Parameterized):
...
_on_exit: ClassVar[list[Callable[[], None]]] = []
...
def on_exit(self, callback):
self._on_exit.append(callback)
# module server.py
class Server(BokehServer):
...
def stop(self, wait: bool = True) -> None:
...
if state._on_exit:
for callback in state._on_exit:
callback()
super().stop(wait=wait)
# application
pn.state.on_exit(GlobalTaskRunner.remove_all)
That seems really useful. Could you create a Feature Request on Github? And reference this discussion? Thanks.
Curious, could you use atexit for this?
Hi @Hoxbro, atexit
waits for all non-daemon threads to finish before calling the cleanup functions, so unfortunately it won’t work in the situation described in this topic. Also note that the Bokeh server used by Panel already registers a cleanup function with atexit
, which calls the stop()
method mentioned above.