Notes:

"init" is an overloaded term. Try not to use it. Instead, have hooks:

- startup / shutdown. executed once each per program lifecycle.
- load / unload. executed once per module load cycle.

So the flow is:

- startup. prepares long-lived state.
- load. deserializes and prepares short-lived state.
- unload. serializes and releases short-lived state.
- shutdown. releases long-lived state.

So when the hotplug system reloads the module, it performs an unload/load cycle.
Normal operation without any reloads includes all four stages.

Some considerations:

- a module should be able to indicate specific failure:
    - startup (preparing state)
    - load (deserialization or preparing state)
    - unload (serialization)
- a module should be able to indicate it cannot be reloaded.
- serialization data might come from disk, or from a reload, or be missing.

On reloading and rollback:

- when loading a module, copy the dynlib to a versioned temporary file, and load that temporary file.
- procedure for reloading a module:
    - current old version unload (and serialize)
    - attempt new version load (and deserialize)
    - if success: release old version and delete lib
    - if failure:
        - release new version and delete lib.
        - attempt old version load (and deserialize) (catch fatal error)
- Configure signal handlers to always clean up the temporary files on errors like segfault?
    - temp files needs to gracefully handle multiple instances of the app running at once with shared hotloading.

Things should be done in a thread-safe manner. Signal to a module that it should unload gracefully, and the
module signal when it can safely be unloaded.

