I'm attempting to share an image, that is only being used read-only, across threads. Typically I do this sort of thing with boost::shared_ptrs but since cv::Mat is already a reference counting container underneath, I've been attempting to use it in the same manner assuming that it is thread safe based on references to thread safety in reference counting here:
However I've been having having issues that might possibly indicate that they are infact not thread safe; that assignment is non-atomic. Occasionally I'll get a seg-fault inside a reference count increment that implies that the original object has already been destroyed.
So the specific question is:
No, the assignment is not perfectly thread safe.
I wrote a test program that creates two threads. They both contain a shared_ptr to an object that contains a cv::Mat. One thread assigns that cv::Mat to a randomly generated image while the other thread makes a local copy of that cv::Mat.
This crashes immediately with a double-free. If the writing thread overwrites the previous as the copying thread begins copying, it'll copy a cv::Mat who's internal data ptr has just been deleted. When the copying thread's local copy goes out of scope, it attempts to free it again.
volatile bool g_done = false;
struct Object
{
cv::Mat cvMask;
};
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
void thread1(boost::shared_ptr<Object> sharedObj)
{
while(!g_done)
{
sharedObj->cvMask = cv::Mat::ones(1 + (rand()% 1024), 1+(rand()%768), CV_8UC1);
}
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
void thread2(boost::shared_ptr<Object> sharedObj)
{
while(!g_done)
{
cv::Mat localCopy = sharedObj->cvMask;
}
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
void sigHandler(int signum)
{
fprintf(stderr, "Quitting...\n");
g_done = true;
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv)
{
signal(SIGINT, sigHandler);
boost::shared_ptr<Object> sharedObj(new Object);
sharedObj->cvMask = cv::Mat::ones(1024,768, CV_8UC1);
boost::thread* t1 = new boost::thread(boost::bind(&thread1, _1), sharedObj);
boost::thread* t2 = new boost::thread(boost::bind(&thread2, _1), sharedObj);
while(!g_done)
{
usleep(1e6);
}
t1->join();
t2->join();
delete t1;
delete t2;
return 0;
}