Boost::thread, glut and data sharing

409 views Asked by At

I think I have a problem in my program. I must create an object that continuosly communicate with an external tracking system and get coordinates of point from it. I wrapped this class inside a boost::thread and before the first calls to my Glut Application I create the thread object and I detach it

The code for the salient methods of the class is the following

boost::mutex resourceMutex;

void Tracker::init()
{  
  boost::mutex::scoped_lock lock(resourceMutex);

  try
  {
    // some initializations
  }
  catch (std::bad_alloc const&)
  {  
     cerr << "Memory allocation fail during init!" << endl;
  }

  try
  {  
     p3dData = (Position3d*)calloc( NUM_MARKERS , sizeof( Position3d ) );
     if ( p3dData==NULL )
       throw std::bad_alloc();
  }
  catch ( std::bad_alloc const&)
  {  
     cerr << "Memory allocation fail during memory allocation!" << endl;
  }

}

void Tracker::update()
{
  boost::mutex::scoped_lock lock(optotrakResourceMutex);
  //... operations on vector< Eigen::Vector3d > points
}

vector<Eigen::Vector3d> &Tracker::getAllPoints()
{
  return points;
}

My glutTimerFunc makes a call to an update function that every frame picks the points with the method getAllPoints, while the tracker thread continuosly update them (in fact the frequencies of access to data are different, the thread calls to is faster than the glut update functions calls.

Now when the program exit, I first delete the Tracker object allocated with new then interrupt the thread containing it, but sometimes I get strange behaviours I think they are memory leak

Is the way of getting data with different frequencies of access and the use of scoped_lock correct or should I put some guard in the getAllPoints method?

1

There are 1 answers

0
Julien-L On BEST ANSWER

I understand your dedicated tracker thread continuously calls Tracker::update() to acquire the localization data from your device (NDI Optotrak?)

Then, your OpenGL application accesses the latest points at regular interval from the main thread using Tracker::getAllPoints().

In this case, the vector of 3D points Tracker::points is a shared resource between these two threads.

To prevent concurrent access, both the writing operation in update() and the reading with getAllPoints() must be protected by the mutex, not only the writing as in your current code. The reading code in the main thread must also lock the mutex:

// In your main application:
void timerFunc()
{
    Tracker* tracker = ...;            // Obtain a pointer to the tracker object
    tracker->LockResourceMutex();      // Enter critical section
    vector< Eigen::Vector3d >& pointsRef = tracker->getAllPoints();
    //... operations on points, protected by the mutex
    tracker->UnlockResourceMutex();    // Leave critical section
}

// In class Tracker:
void Tracker::LockResourceMutex() { optotrakResourceMutex.lock(); }
void Tracker::UnlockResourceMutex() { optotrakResourceMutex.unlock(); }

Caveat: If your operations on points in the timerFunc() are slow, then the mutex will remain locked for a long time and your tracker thread will block on it when calling Tracker::update().

A better design would be to change Tracker::getAllPoints() to return a copy of the 3D points vector instead of a reference:

// In class Tracker:
vector<Eigen::Vector3d> Tracker::getAllPoints()
{
    boost::mutex::scoped_lock lock(optotrakResourceMutex);
    return points; // Will call the std::vector() copy constructor
}

// In your main application:
void timerFunc()
{
    Tracker* tracker = ...;            // Obtain a pointer to the tracker object
    vector< Eigen::Vector3d > myPoints = tracker->getAllPoints();
    //... operations on your own copy if points
}

Note how the mutex is encapsulated in the Tracker class and how the timerFunc() does not need to worry about it.

Also note how the mutex is locked only during the copy. The copy of a list of 3D vectors is certainly going to be faster than mathematical operations on them.