DarioP
DarioP

Reputation: 5465

How could I avoid this raw pointer with this OpenMP critical section?

I have a std::deque<std::reference_wrapper<MyType>> mydeque. I need a function that returns the front value (as a plain reference) and pops it from the queue. As std::deque are not thread safe, access should be protected (I'm using OpenMP).

I came up with the ugly code below. It looks very bad having such advanced structures and then falling back to a raw pointer.

MyType & retrieve() {
  MyType* b;
  #pragma omp critical(access_mydeque)
  {
    b = &(mydeque.front().get());
    mydeque.pop_front();
  }
  return *b;
}

The problem is that I cannot return within the critical section, but I also cannot declare a reference(_wrapper) before the critical section (because it must be assigned to something)... Is there a way to solve this?

Upvotes: 2

Views: 265

Answers (2)

Jim Cownie
Jim Cownie

Reputation: 2869

You could simply use TBB's parallel data structures https://software.intel.com/en-us/node/506076 (though since there is no concurrent_deque they may not be perfect for you :-( ).

They do not require that you also use TBB to describe the parallelism aspects of your code, so can be mixed into an OpenMP code. (Of course, since you're using C++ you might find TBB's approach to scalable, composable, parallelism more friendly than OpenMP's, but that's a separable decision).

Upvotes: 1

Massimiliano
Massimiliano

Reputation: 8032

Any solution I can think of involves using an omp_lock_t instead of the critical construct and a RAII class managing the omp_lock_t ownership:

class LockGuard {
public:  
    explicit LockGuard(omp_lock_t& lock) : m_lock(lock){
        omp_set_lock(&m_lock);
    }

    ~LockGuard() {
        omp_unset_lock(&m_lock);
    }

private:
  omp_lock_t& m_lock;
};

Then you can either modify the code you already have into something like:

MyType & retrieve() {
    LockGuard guard(mydeque_lock);
    auto b = mydeque.front();
    mydeque.pop_front();
    return b;
}

or better, write your own thread-safe container that aggregates the lock and the std::deque:

template<class T>
class MtLifo {
public:
    MtLifo() {
       omp_init_lock(&m_lock);
    }

    typename std::deque<T>::reference front_and_pop() {
        LockGuard guard(m_lock);
        auto b = m_stack.front();
        m_stack.pop_front();
        return b;
    }

    void push_front(const T& value) {
        LockGuard guard(m_lock);
        m_stack.push_front(value);
    }


    ~MtLifo() {
        omp_destroy_lock(&m_lock);
    }

private:
   std::deque<T> m_stack;
   omp_lock_t m_lock;
}

Upvotes: 2

Related Questions