Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 1 addition & 13 deletions AutoML.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@
from tools.splash_launcher import SplashLauncher
from tools.trash_eater import manager_eater
from mainappsrc.version import VERSION
from mainappsrc.services import service_manager
from mainappsrc.core.automl_core import (
AutoMLApp,
FaultTreeNode,
Expand Down Expand Up @@ -300,18 +299,7 @@ def _loader() -> Any:
if module is None: # pragma: no cover - defensive
return

class _AutoMLCoreService:
def __init__(self, mod: Any) -> None:
self._module = mod

def run(self) -> None:
self._module.main()

service_manager.request(
"automl_core", lambda: _AutoMLCoreService(module), recoverable=False, daemon=False
)
service_manager.join("automl_core")
service_manager.release("automl_core")
module.main()

memory_manager.cleanup()
if _diagnostics_manager:
Expand Down
12 changes: 12 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
-->

# Version History
- 0.2.213 - Introduce pausable service manager to prevent premature thread
termination while diagrams remain open.
- 0.2.212 - Remove service thread manager and invoke application core directly.
- 0.2.211 - Pause service threads on release and add explicit shutdown API so
threads persist until manually terminated.
- 0.2.210 - Restart paused service threads if they exited and clamp reference
counts so services remain available while diagrams are open.
- 0.2.209 - Wait for service threads to terminate on release, preventing
``Tcl_AsyncDelete`` errors when reopening saved diagrams.
- 0.2.208 - Suspend unused service threads instead of killing them so diagram
tabs can reopen without crashes. Threads are terminated only after
remaining idle beyond a timeout.
- 0.2.207 - Skip joining the current thread during thread manager shutdown
to prevent runtime errors and ``Tcl_AsyncDelete`` warnings.
- 0.2.206 - Track stop events in the service thread manager so threads
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 0.2.207
version: 0.2.213
Author: Miguel Marina <karel.capek.robotics@gmail.com> - [LinkedIn](https://www.linkedin.com/in/progman32/)
# AutoML

Expand Down
2 changes: 0 additions & 2 deletions mainappsrc/services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,4 @@ def __getattr__(name: str) -> Any: # pragma: no cover - simple delegation
return getattr(module, attr_name)


from .service_manager import ServiceManager, manager as service_manager

__all__.extend(["ServiceManager", "service_manager"])
125 changes: 0 additions & 125 deletions mainappsrc/services/service_manager.py

This file was deleted.

2 changes: 1 addition & 1 deletion mainappsrc/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@

"""Project version information."""

VERSION = "0.2.207"
VERSION = "0.2.213"

__all__ = ["VERSION"]
104 changes: 0 additions & 104 deletions tests/services/test_service_manager.py

This file was deleted.

70 changes: 70 additions & 0 deletions tests/thread_manager/test_service_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Author: Miguel Marina <karel.capek.robotics@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Copyright (C) 2025 Capek System Safety & Robotic Solutions
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Grouped regression tests for :mod:`tools.service_manager`."""

from __future__ import annotations

import time

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[2]))

from tools.service_manager import ServiceManager


class TestServiceManagerLifecycle:
"""Lifecycle and pause/resume behaviour."""

def test_reuses_paused_service(self) -> None:
runs: list[int] = []

def worker(stop, resume) -> None: # type: ignore[no-untyped-def]
runs.append(1)
resume.clear()
while not stop.is_set() and not resume.wait(0.01):
pass

mgr = ServiceManager()
svc = mgr.acquire("demo", worker)
time.sleep(0.05)
assert runs
thread = svc.thread
mgr.release("demo")
time.sleep(0.05)
assert thread.is_alive()
mgr.acquire("demo", worker)
time.sleep(0.05)
assert len(runs) > 1
assert svc.thread is thread
mgr.release("demo")
mgr.shutdown_all()

def test_shutdown_all_terminates_services(self) -> None:
stop_called: list[bool] = []

def worker(stop, resume) -> None: # type: ignore[no-untyped-def]
if stop.wait(0.01):
stop_called.append(True)

mgr = ServiceManager()
mgr.acquire("demo", worker)
mgr.release("demo")
mgr.shutdown_all()
assert stop_called
Loading