From 20045029d969b2fb419420628d9c4fa4821ab444 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Wed, 26 Aug 2026 10:05:25 -0700 Subject: [PATCH] Initialize/Finalize: Pop Hooks Before Calling Them `amrex::Initialize` and `amrex::Finalize` drained their hook stacks with `top()()` followed by `pop()`. A hook that registers another one with `ExecOnInitialize`/`ExecOnFinalize` therefore left the *calling* hook on top of the stack: it ran a second time, and the following `pop()` silently discarded the newly registered hook without ever running it. Move the hook off the stack before calling it, so that a hook which registers another one runs exactly once and the newly registered hook runs as well. Found while making the ABLASTR warn manager state per AMReX cycle in https://github.com/BLAST-WarpX/warpx/pull/7186: a warning recorded from a finalize hook resurrects the warn manager singleton and loses the teardown hook that would have released it again. --- Src/Base/AMReX.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Src/Base/AMReX.cpp b/Src/Base/AMReX.cpp index 62952546698..40da5ce7097 100644 --- a/Src/Base/AMReX.cpp +++ b/Src/Base/AMReX.cpp @@ -87,6 +87,7 @@ #include #include #include +#include #include #include #include @@ -441,13 +442,13 @@ amrex::Initialize (int& argc, char**& argv, bool build_parm_parse, while ( ! The_Initialize_Function_Stack.empty()) { // - // Call the registered function. - // - The_Initialize_Function_Stack.top()(); - // - // And then remove it from the stack. + // Remove the function from the stack before calling it, so that a function + // that calls ExecOnInitialize itself neither runs twice nor drops the newly + // registered function. // + auto f = std::move(The_Initialize_Function_Stack.top()); The_Initialize_Function_Stack.pop(); + f(); } BL_PROFILE_INITIALIZE(); @@ -854,13 +855,13 @@ amrex::Finalize (amrex::AMReX* pamrex) while (!The_Finalize_Function_Stack.empty()) { // - // Call the registered function. - // - The_Finalize_Function_Stack.top()(); - // - // And then remove it from the stack. + // Remove the function from the stack before calling it, so that a function + // that calls ExecOnFinalize itself neither runs twice nor drops the newly + // registered function. // + auto f = std::move(The_Finalize_Function_Stack.top()); The_Finalize_Function_Stack.pop(); + f(); } // The MemPool stuff is not using The_Finalize_Function_Stack so that