I am trying to make an application using python code and C++, but I need some kind of protection against infinite loops or too long executions. I tried following some threads and seeing how other people solved their issues, but couldn't find a working solution. I have this example code:
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include <atomic>
#include <chrono>
#include <cstdlib>
#include "pybind11/pybind11.h"
#include "pybind11/embed.h"
namespace py = pybind11;
std::atomic<bool> stopped(false);
std::atomic<bool> executed(false);
void python_executor(const std::string &code)
{
py::gil_scoped_acquire acquire;
try
{
std::cout << "+ Executing python script..." << std::endl;
py::exec(code);
std::cout << "+ Finished python script" << std::endl;
}
catch (const std::exception &e)
{
std::cout << "@ " << e.what() << std::endl;
}
executed = true;
std::cout << "+ Terminated normal" << std::endl;
}
int main()
{
std::cout << "+ Starting..." << std::endl;
std::string code = R"(
# infinite_writer.py
import time
file_path = r"C:\Temp\loop.txt"
counter = 1
while True:
with open(file_path, "a") as f:
f.write(f"Line {counter}\n")
counter += 1
time.sleep(1) # optional: wait 1 second between writes
)";
py::scoped_interpreter interpreterGuard{};
py::gil_scoped_release release;
std::thread th(python_executor, code);
auto threadId = th.get_id();
std::cout << "+ Thread: " << threadId << std::endl;
// stopped = true;
int maxExecutionTime = 10;
auto start = std::chrono::steady_clock::now();
while (!executed)
{
auto elapsed = std::chrono::steady_clock::now() - start;
if (elapsed > std::chrono::seconds(maxExecutionTime)) {
std::cout << "Interrupting...";
PyErr_SetInterrupt();
std::this_thread::sleep_for(std::chrono::seconds(1));
if (th.joinable()) {
th.join();
executed = true;
break;
}
}
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "+ Waiting..." << std::endl;
}
// Make sure to join the thread if it's still running
if (th.joinable()) {
th.join();
}
std::cout << "+ Finished" << std::endl;
return EXIT_SUCCESS;
}
It's purposely an infinite loop in python, to try and stop it when timeout hits, but it never finishes, I got the following console response:
+ Starting...
+ Thread: 28596
+ Executing python script...
+ Waiting...
+ Waiting...
+ Waiting...
+ Waiting...
+ Waiting...
+ Waiting...
+ Waiting...
+ Waiting...
+ Waiting...
+ Waiting...
Interrupting...
It stays at this part infinitely, proving that it didn't finish execution of the python code. What can I do to correctly end the execution?
I tried exactly like the example code, with PyErr_SetInterrupt, and also tried using PyEval_SetTrace lie said in this thread on github: https://github.com/pybind/pybind11/issues/2677 with the same result, code still runs after trying to stop it.
PyThreadState_SetAsyncExc(python_executor_thread_id, PyExc_KeyboardInterrupt);while holding GIL instead ofPyErr_SetInterrupt.python_executor_thread_idshould be set bypython_executorthread after acquiring gil.