I have a ConcurrentQueue
class that is based around a user provided container with a constructor like this...
ConcurrentQueue(const ConcurrentQueue& other) : m_Queue(other.m_Queue) {}
But, I need to lock other
's mutex while it's being copied.
Option 1:
So I could not use the copy constructor at all, and do...
ConcurrentQueue(const ConcurrentQueue& other) : m_Queue(other.m_Queue)
{
std::lock_guard<std::mutex> lock(other.m_Mutex);
m_Queue = other.m_Queue;
}
But I can't guarantee that copy assignment and copy construction are equivalent functionality.
Option 2:
I could have a private method...
std::queue<T, Container> GetQueue() const
{
std::lock_guard<std::mutex> lock(other.m_Mutex);
return m_Queue;
}
And then in the constructor do this...
ConcurrentQueue(const ConcurrentQueue& other) : m_Queue(other.GetQueue()) {}
But this potentially (depending on optimizations) uses m_Queue's copy constructor once and it's move constructor once. And I also can't guarantee that a copy and a move is equivalent to just a copy. Additionally the user provided container could be bizarre and be copyable but unmoveable, which would also cause this approach to have problems.
So, what do I do?
Mutexes are used to protect shared data structures being concurrently accessed. If a mutex is destroyed while a thread is blocked waiting for that mutex, critical sections and shared data are no longer protected. The mtx_destroy function releases any resources used by the mutex pointed to by mtx .
A mutex can also be recursive or non-recursive: Recursive mutexes can be locked several times by the same thread.
Only one thread may have the mutex locked at any given time. Threads attempting to lock an already locked mutex will block until the thread that owns the mutex unlocks it.
std::mutex is neither copyable nor movable.
ConcurrrentQueue::ConcurrrentQueue(
ConcurrrentQueue const& other )
: m_Queue( (std::lock_guard<std::mutex>( other.m_Mutex ),
other.m_Queue ) )
{
}
should work.
Lock, create a copy of the content and then swap it with the member. At least that's the easiest and IMHO cleanest way. Another less clean way is to use the comma operator: (a, b)
yields b
, but if a
is a scoped lock, the temporary will live until the next sequence point, i.e. until you have used b
to initialize your local copy.
That said, there are two things to consider:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With