I created a C++
wrapper to access my Python
modules. everything is working until I try to use threads in my application.
On my Python
module there is a method which reads from a webcam (so its uses an infinite loop) and I send callbacks from C++
to get the image and other needed information from it.
Since we have a blocking method here, I decided to use threads.
The threading on Python
part seems not to be working on the C++
side that is if I call the async counter part of the webcam_feed
loop, none of my callbacks are actually executed (on python part the routines are all executed however, it seems it doesn't reach to C++ section somehow. I don't get any feedback in C++ side, however, on Python part, those routines responsible for executing the callbacks save the info to the disk so I know for sure they are executed).
I asked a separate question for it here.
Therefore I decided to use the threading inside C++ client. However, whenever I execute the code (given below), I get an access violation whenever I want to use any methods after the thread is started.
Here are the sample callbacks I have for now:
void default_callback(bool status, std::string id, py::array_t<uint8_t>& img)
{
auto rows = img.shape(0);
auto cols = img.shape(1);
auto type = CV_8UC3;
cv::Mat img1(rows, cols, type, img.mutable_data());
cv::imshow("from callback", img1);
cv::waitKey(1);
auto timenow = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::cout << "\narg1: " << status << " arg2: " << id << " arg3: " << typeid(img).name() << " " << ctime(&timenow) << std::endl;
}
void default_c_callback_temporary(bool status, char* message)
{
std::cout << "status is: " << status << " id/name: " << message << " ptr:" << "" << std::endl;
std::ofstream myfile;
myfile.open("example.txt");
myfile << "Writing this to a file: " << status << message << std::endl;
myfile.close();
}
And this is the actual test
void thread_test_start(Core* core)
{
try
{
core->SetCpuAffinity(2);
core->AddCallback(default_callback);
core->AddCallback_C_tmp(default_c_callback_temporary);
//set true to run the async version (implemented in python)
core->Start(false);
}
catch (const std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
}
int main()
{
Core* core = new Core(false);
std::thread t(thread_test_start, core);
py::print(core->GetCallbacks());
std::cout << "\nGet C Callbacks:\n";
py::print(core->GetCallbacks_C_tmp());
std::cout << "\nEverything done. press Enter to Exit";
t.join();
std::getchar();
return 0;
}
The call to core->GetCallbacks()
causes the memory access violation:
Exception thrown at 0x000000006FCC6D80 (python36.dll) in TestDLL.exe: 0xC0000005: Access violation reading location 0x0000000000000010.
And here is a snapshot showing the access violation error inside VS2019:
Doing something like this is also the same :
void thread_test_start2()
{
try
{
Core* core = new Core(false);
core->SetCpuAffinity(2);
core->AddCallback(default_callback);
core->AddCallback_C_tmp(default_c_callback_temporary);
std::thread t(&Core::Start, core, false);
py::print(core->GetCallbacks());
std::cout << "\nGet C Callbacks:\n";
py::print(core->GetCallbacks_C_tmp());
t.join();
}
catch (const std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
}
results in :
Exception thrown at 0x000000006FCC0CDF (python36.dll) in TestDLL.exe: 0xC0000005: Access violation writing location 0x0000000000000020.
like the former one. Why am I getting this error ? Can we not use threading with Pybind11? What am I missing here?
Here is a sample project to re-create this issue : https://workupload.com/file/6LmfRtbztHK
The reason for memory access violations were due to trying to run methods using different threads. That is, all Pybind11 related methods (methods that use Pybind11) need to be executed under the very same thread it seems.
Therefore executing some portion of the code under one thread and trying to execute some other methods in the main thread will result in memory access violation.
In order to get around this, I ended up implementing a simple dispatcher in one callback where any method that needs to be run, first sets a flag, then each time the callback is run, the flag is checked and the corresponding method is run.
int flag=0;
void callback(...)
{
switch(flag)
{
case 1: //e.g. stop
core->stop();
break;
case 2: // e.g. get_callbacks()
core->get_callbacks();
break;
case 3:
//some other op
break;
....
}
//reset flag
flag = 0;
}
User contributions licensed under CC BY-SA 3.0