diff --git a/python/revng/api/_capi.py b/python/revng/api/_capi.py index f616ef4d0..7a8b0073c 100644 --- a/python/revng/api/_capi.py +++ b/python/revng/api/_capi.py @@ -16,6 +16,12 @@ from revng.support import AnyPaths, get_root, to_iterable from revng.support.collect import collect_files, collect_libraries, collect_one +# This counter is used to count the pointers released by PipelineC, since +# calling rp_shutdown before all pointers are freed leads to a crash +# This will count (atomically) the pointers created by PipelineC and decrements +# once the pointer is freed by `ffi.gc`. Once the API signals that it will no +# longer do anything (by calling `mark_end`) then once the counter reaches 0 +# the callback can be invoked which will safely call rp_shutdown. class AtomicCounterWithCallback: """Simple atomic counter, will call callback once mark_end has been called and the counter reaches zero""" @@ -39,6 +45,8 @@ class AtomicCounterWithCallback: def mark_end(self): with self.lock: self.ending = True + if self.counter == 0: + self.callback() class ApiWrapper: diff --git a/python/revng/api/analysis.py b/python/revng/api/analysis.py index a8129fa52..66e42ccc8 100644 --- a/python/revng/api/analysis.py +++ b/python/revng/api/analysis.py @@ -75,7 +75,7 @@ class AnalysesList(Sequence[Analysis]): return _api.rp_analyses_list_count(self._analyses_list) def _get_analysis(self, index: int) -> Analysis: - _analysis = _api.rp_manager_get_analysis(self._manager, self._analyses_list, index) + _analysis = _api.rp_manager_get_analysis(self._manager._manager, self._analyses_list, index) return Analysis(_analysis) @overload diff --git a/python/revng/api/manager.py b/python/revng/api/manager.py index 867c5d030..d4c51a050 100644 --- a/python/revng/api/manager.py +++ b/python/revng/api/manager.py @@ -427,7 +427,7 @@ class Manager: def _analyses_list_get(self, index: int) -> AnalysesList: _analyses_list = _api.rp_manager_get_analyses_list(self._manager, index) - return AnalysesList(_analyses_list, self._manager) + return AnalysesList(_analyses_list, self) def analyses_lists(self) -> Generator[AnalysesList, None, None]: return make_generator(self._analyses_list_count(), self._analyses_list_get) diff --git a/python/revng/daemon/__init__.py b/python/revng/daemon/__init__.py index 412adb6be..11bd1e26a 100644 --- a/python/revng/daemon/__init__.py +++ b/python/revng/daemon/__init__.py @@ -105,7 +105,7 @@ def make_startlette() -> Starlette: } routes = [ - Route("/", generate_demo_page(manager, DEBUG), methods=["GET"]), + Route("/", generate_demo_page(manager.workdir, DEBUG), methods=["GET"]), Route("/status", status, methods=["GET"]), Mount( "/graphql", @@ -129,6 +129,7 @@ def make_startlette() -> Starlette: store_result = manager.save() if not store_result: logging.warning("Failed to store manager's containers") + manager._manager = None capi_shutdown() return Starlette( diff --git a/python/revng/daemon/demo_webpage.py b/python/revng/daemon/demo_webpage.py index 57f5611ca..dbbd65853 100644 --- a/python/revng/daemon/demo_webpage.py +++ b/python/revng/daemon/demo_webpage.py @@ -8,18 +8,16 @@ from starlette.requests import Request from starlette.responses import PlainTextResponse from starlette.templating import Jinja2Templates -from revng.api import Manager - module_dir = Path(__file__).parent.resolve() templates = Jinja2Templates(directory=module_dir / "templates") -def generate_demo_page(manager: Manager, debug: bool): +def generate_demo_page(workdir: Path, debug: bool): if debug: async def dev_demo_page(request: Request): return templates.TemplateResponse( - "index.html", {"request": request, "manager": manager} + "index.html", {"request": request, "workdir": workdir} ) return dev_demo_page diff --git a/python/revng/daemon/templates/index.html b/python/revng/daemon/templates/index.html index 2c11dc5e2..03e7542b5 100644 --- a/python/revng/daemon/templates/index.html +++ b/python/revng/daemon/templates/index.html @@ -9,7 +9,7 @@
Welcome to revng!
- Working directory is {{ manager.workdir }} + Working directory is {{ workdir }}