Skip to content

Commit c6b6605

Browse files
automatically call shutdown with atexit hook
1 parent ff90e78 commit c6b6605

File tree

1 file changed

+16
-8
lines changed

1 file changed

+16
-8
lines changed

src/geophires_x_client/__init__.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1+
import atexit
12
import os
23
import sys
34
import threading
45
from multiprocessing import Manager
56
from pathlib import Path
67

8+
# noinspection PyPep8Naming
79
from geophires_x import GEOPHIRESv3 as geophires
810

9-
# Assuming these are in a sibling file or accessible path
1011
from .common import _get_logger
1112
from .geophires_input_parameters import GeophiresInputParameters
1213
from .geophires_input_parameters import ImmutableGeophiresInputParameters
@@ -16,16 +17,17 @@
1617
class GeophiresXClient:
1718
"""
1819
A thread-safe and process-safe client for running GEOPHIRES simulations.
19-
Relies on an explicit shutdown() call to clean up background processes.
20+
It automatically manages a background process via atexit and provides an
21+
explicit shutdown() method for advanced use cases like testing.
2022
"""
2123

2224
# --- Class-level shared resources ---
2325
_manager = None
2426
_cache = None
2527
_lock = None
2628

27-
# A standard threading lock to make the one-time initialization thread-safe.
2829
_init_lock = threading.Lock()
30+
"""A standard threading lock to make the one-time initialization thread-safe."""
2931

3032
def __init__(self, enable_caching=True, logger_name=None):
3133
if logger_name is None:
@@ -41,25 +43,31 @@ def __init__(self, enable_caching=True, logger_name=None):
4143
@classmethod
4244
def _initialize_shared_resources(cls):
4345
"""
44-
Initializes the multiprocessing Manager and shared resources (cache, lock)
45-
in a thread-safe and process-safe manner.
46+
Initializes the multiprocessing Manager and shared resources in a
47+
thread-safe manner. It also registers the shutdown hook to ensure
48+
automatic cleanup on application exit.
4649
"""
4750
with cls._init_lock:
4851
if cls._manager is None:
4952
cls._manager = Manager()
5053
cls._cache = cls._manager.dict()
5154
cls._lock = cls._manager.RLock()
55+
# Register the shutdown method to be called automatically on exit.
56+
atexit.register(cls.shutdown)
5257

5358
@classmethod
5459
def shutdown(cls):
5560
"""
56-
Explicitly shuts down the background manager process.
57-
This MUST be called when the application is finished with the client
58-
to prevent orphaned processes.
61+
Explicitly shuts down the background manager process and de-registers
62+
the atexit hook to prevent errors if called multiple times.
63+
This is useful for test suites or applications that need to precisely
64+
control the resource lifecycle.
5965
"""
6066
with cls._init_lock:
6167
if cls._manager is not None:
6268
cls._manager.shutdown()
69+
# De-register the hook to avoid trying to shut down twice.
70+
atexit.unregister(cls.shutdown)
6371
cls._manager = None
6472
cls._cache = None
6573
cls._lock = None

0 commit comments

Comments
 (0)