Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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:

  • http://opencv.willowgarage.com/documentation/cpp/basic_structures.html
  • https://docs.opencv.org/2.4/modules/core/doc/basic_structures.html

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?
like image 201
Catskul Avatar asked May 18 '12 18:05

Catskul


2 Answers

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;
}
like image 57
Rob Meyers Avatar answered Nov 16 '22 10:11

Rob Meyers


Specific question, short answer: YES.

You can check the cv::Mat implementation details in core/src/matrix.cpp and include/.../core/core.hpp

Some code excerpts from OpenCV sources:

 if( refcount )
        CV_XADD(refcount, 1);

Where CV_XADD is the atomic test-and-increment.

inline void Mat::addref()
{ if( refcount ) CV_XADD(refcount, 1); }

inline void Mat::release()
{
    if( refcount && CV_XADD(refcount, -1) == 1 )
        deallocate();
    data = datastart = dataend = datalimit = 0;
    size.p[0] = 0;
    refcount = 0;
}

Extra

Smart pointers do offer a level of thread safety, but that does not mean they are completely thread-safe in every scenario possible. Specifically, if you try to copy a shared ptr at the same time it is destructed by another thread, you lose. That's not a bug in the implementation but a design trade-off between speed and usefulness.

All major shared ptr implementations (boost, stl) follow this approach.

like image 43
Sam Avatar answered Nov 16 '22 12:11

Sam