0

I face the following situation (which I have to admit I'm too noob to trust myself in solving alone..): I have thread A which occasionally creates new cv::Mat objects for thread B to consume. I need a thread-safe container C (a boost::circular_buffer in this case) which will hold the cv::Mat objects thread A generates. Then, thread B needs to constantly iterate through all items in C to produce an animation. Therefore I need a thread-safe C which will disallow data-races but also cause no (ideally) or very small (if not possible otherwise) lag to thread B's animation -> I dot want thread B to freeze when thread A updates C. The best I could come up with is this:

#include <boost/circular_buffer.hpp>
#include <opencv2/core.hpp>
#include <boost/core/noncopyable.hpp>
#include <memory>
#include <type_traits>
#include <algorithm>

using im_buf = boost::circular_buffer<cv::Mat>;
class ImageBuffer : private boost::noncopyable { 
private:
  im_buf buffer;
  std::mutex mtx;
  std::unique_lock<std::mutex> lock;
public:
  // operator<< accepting cv::Mat, cv::Mat& and cv::Mat&&
  template <class T,
    class = typename std::enable_if
    <std::is_same<cv::Mat, typename std::decay<T>::type>
     ::value>::type>
   void operator<<(T&& mat) {
      lock.lock();
      buffer.push_back(std::forward<T>(mat));
      lock.unlock();
    }
    template <typename Func> // excpect callable objects only
    inline void iterate(Func func) {
      lock.lock();
      std::for_each(buffer.begin(),buffer.end(),func);
      lock.unlock();
    }
    inline ImageBuffer():
      buffer {settings::max_images_in_loop},
      mtx {},
      lock {mtx} {}
    ~ImageBuffer()=default;
    ImageBuffer(const ImageBuffer&&)=delete; 
    ImageBuffer& operator=(const ImageBuffer&&)=delete;  
};

(Note that even if this is not an invariant, thread B is not suppose to mutate C or any of its contents (I'd love to use a const_iterator here but boost::circular_buffer does not provide one..)

Yet, with this code thread B will freeze the entire animation for some time each time thread A adds new elements.. so, a. isn't there some better approach?? b. is implementation truly thread safe?

Marinos K
  • 1,779
  • 16
  • 39
  • 2
    Your `lock` member variable should actually be a local variable in each function. If you do that you also don't need to unlock explicitly, it happens when the scope is left. Have a look at the usage examples for [`std::unique_lock`](http://en.cppreference.com/w/cpp/thread/unique_lock) and [`std::lock_guard`](http://en.cppreference.com/w/cpp/thread/lock_guard). – mindriot Jan 29 '16 at 10:39
  • you could check the [std::unique_lock::owns_lock](http://en.cppreference.com/w/cpp/thread/unique_lock/owns_lock) to see if the mutex is locked. In case the mutex is locked B continue its process. If the mutex is not locked B iterate through all items to produce the animation. – drosam Jan 29 '16 at 10:47
  • you're not getting an error for `std::mutex mtx;` without `#include "`? – lciamp Jan 29 '16 at 11:03
  • no, weird - it might be implicitly included in some other header – Marinos K Jan 29 '16 at 11:19

1 Answers1

2

If you problem is animation, you are worrying about the wrong thing. Mutexes are fine for your purpose - don't worry about stalling.

Ideally you need to produce frames at 60fps. You may get away with as little as 20fps depending on your application. Until the advent of digital cinema, cinemas only showed 24fps.

60fps means you have 16 milliseconds to render the frame. On a 1GHz processor that's 16 million clock cycles. To avoid stuttering due to other processes, you need to use less than half of that, say 4-8 million cycles, so 50% to 75% of processor time is idle, and you use only 4-8 milliseconds of processor time rendering the frame.

Waiting for other threads, mutexes or other resources only affects your animation if it causes you to miss the deadline for the next frame.

If your thread has to wait once every few frames, for a few tens of thousand clock cycles due to a mutex, this will not affect your rendering because you still have plenty of time before the next frame is due to be shown on the screen.

Ben
  • 34,935
  • 6
  • 74
  • 113