Is cv::Mat thread-safe (atomic assignment + refcounting)?

Catskul picture Catskul · May 18, 2012 · Viewed 7.8k times · Source

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:

  • Is cv::Mat assignment atomic?

Answer

Rob Meyers picture Rob Meyers · Oct 28, 2016

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;
}