Noticing Module Shutdown

Afternoon, folks.

I find myself writing my first C extension module, which is a fun experience mostly. Sadly, Baby’s First Module is complicated by starting up a background thread to run I²C communications (no, running the I²C comms in foreground is not an option). So far I have things communicating back and forth safely across the thread divide, reference counting is only normally nightmarish, the usual stuff.

However I have a problem when exiting test scripts. I would very much like some kind of module finalization hook so that I can shut down the comms thread before the Python interpreter tears down the infrastructure that the thread relies on for communicating with the foreground. As my module stands, it is possible for a badly timed message from the far end to invoke code that is no longer expecting to be called; this appears to happen in practise since my colleague can get SIGSEGV about 50% of the time with a simple test script.

Aside from mentioning that dynamically loaded modules are not unloaded, the fulsome documentation on writing extension modules drops no hints about module finalization. Is there any hook I can tap into, or am I doomed to need an explicit shutdown function?

(I practice I don’t think this is a big deal; the tests are artificially engineering bad timing which is much less likely to happen in real life. It does however offend my soul to end with a segfault if I don’t need to.)

1 Like

There’s PyModuleDef.m_free.
It might be called pretty late during interpreter shutdown, though, so I still wouldn’t be sure that everything you need for the thread finalization will still be around.

That’s what I’d recommend – a context with both an explicit startup function, and an explicit teardown function that’s called well before the interpreter starts exiting.

1 Like

Meh. That’s annoying. I might get away with inventing a context manager in future versions, but not right now given that I am attempting to replicate existing code in a different setting. It looks like the first version is just going to have to accept that quitting the interpreter runs a (small) risk of segfaulting.

1 Like

I see.
Look into atexit as well – these hooks run before interpreter shutdown rather than when a module is unloaded, but for most applications those will be the same time. And atexit hooks are run while the interpreter is still entirely intact.

2 Likes

To my huge surprise, I managed to persuade Beloved Client that a minor variation in the interface was acceptable (i.e. starting with from hub import hub instead of import hub). Making the problem code part of an object rather than a module means I have a tp_finalize hook to hang the shutdown code on, and my problems have all gone away.

Thanks anyway for all the advice. Hopefully it will be useful to someone else in the future.

4 Likes