diff options
Diffstat (limited to 'indra/llcommon/tests/llprocess_test.cpp')
| -rw-r--r-- | indra/llcommon/tests/llprocess_test.cpp | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index 2988d30629..65d797947f 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -17,7 +17,9 @@ #include <vector> #include <list> // std headers +#include <chrono> #include <fstream> +#include <thread> // external library headers #include "llapr.h" #include "apr_thread_proc.h" @@ -1720,4 +1722,75 @@ namespace tut check_eof(history, "stderr"); }); } + + template<> template<> + void object::test<34>() + { + set_test_name("tick() completes quickly after kill()"); + // Regression test: tick() must not block after kill() is called. + // The old Windows code called WaitForSingleObject(..., 100) in tick(), + // causing a 100 ms stall on the main thread. This test ensures that a + // mainloop tick completes well under that threshold even when the child + // process has been killed and may still be exiting. + PythonProcessLauncher py(get_test_name(), + "import time\n" + "time.sleep(120)\n"); + py.launch(); + + // Wait for the process to start up + yield(); + ensure("process started", py.mPy->isRunning()); + + // Send the kill signal + py.mPy->kill(); + + // Sleep longer than the tick threshold to ensure the child has had + // time to exit at the OS level, so the next tick is likely to enter + // the "process just exited" code path that used to block for 100 ms. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + // Time a single mainloop tick: it must not block + auto start = std::chrono::steady_clock::now(); + LLEventPumps::instance().obtain("mainloop").post(LLSD()); + auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - start).count(); + + ensure(STRINGIZE("tick() took " << elapsed_ms << " ms, expected < 75 ms"), + elapsed_ms < 75); + + // Let the process fully exit so cleanup is orderly + waitfor(*py.mPy); + } + + template<> template<> + void object::test<35>() + { + set_test_name("LLProcess destructor completes quickly after kill()"); + // Regression test: after an explicit kill() call the destructor must + // not perform a blocking wait. The old Windows code called + // WaitForSingleObject(..., 100) in the destructor, causing a 100 ms + // stall on the main thread. With mKillCalled set to true, the + // destructor skips the termination/wait block entirely. + PythonProcessLauncher py(get_test_name(), + "import time\n" + "time.sleep(120)\n"); + py.launch(); + + // Wait for the process to start up + yield(); + ensure("process started", py.mPy->isRunning()); + + // Kill the process (sets mKillCalled = true) + py.mPy->kill(); + + // Time how long the destructor takes + auto start = std::chrono::steady_clock::now(); + py.mPy.reset(); // explicit destruction + auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - start).count(); + + ensure(STRINGIZE("destructor took " << elapsed_ms << " ms, expected < 75 ms"), + elapsed_ms < 75); + } + } // namespace tut |
