source: trunk/yat/utility/BasicQueue.h @ 3721

Last change on this file since 3721 was 3721, checked in by Peter, 4 years ago

Make assignment thread safe by locking both lhs and rhs. Avoid locking
them independently as that would likely cause deadlocks, instead lock
the with boost::lock - which is designed exactly for this purpose.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 4.6 KB
Line 
1#ifndef theplu_yat_utility_basic_queue
2#define theplu_yat_utility_basic_queue
3
4// $Id: BasicQueue.h 3721 2017-11-21 04:20:51Z peter $
5//
6// Copyright (C) 2017 Peter Johansson
7//
8// This program is free software; you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by
10// the Free Software Foundation; either version 3 of the License, or
11// (at your option) any later version.
12//
13// This program is distributed in the hope that it will be useful, but
14// WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16// General Public License for more details.
17//
18// You should have received a copy of the GNU General Public License
19// along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21#include "config_public.h"
22
23#include <boost/thread.hpp>
24
25namespace theplu {
26namespace yat {
27namespace utility {
28namespace detail {
29
30  /**
31     \internal Base class for Queue and PriorityQueue
32   */
33  template<class Derived, typename T, class Container>
34  class BasicQueue
35  {
36  public:
37    /// Type of object stored
38    typedef typename Container::value_type value_type;
39
40    /**
41       An unsigned integral type. \see size(void)
42    */
43    typedef typename Container::size_type size_type;
44
45    /**
46       Default constructor
47     */
48    BasicQueue(void) {}
49
50    /**
51       Copy constructor
52     */
53    BasicQueue(const BasicQueue& other)
54    {
55      boost::unique_lock<boost::mutex> lock(other.mutex_);
56      q_ = other.q_;
57    } // lock is released here
58
59    /**
60       Construct queue from underlying Container
61     */
62    explicit BasicQueue(const Container& container) : q_(container) {}
63
64    /**
65       \brief clear queue
66
67       \since new in yat 0.15
68     */
69    void clear(void)
70    {
71      boost::unique_lock<boost::mutex> lock(mutex_);
72      return q_.clear();
73    }
74
75
76    /**
77       \return \c true if container's size is zero
78     */
79    bool empty(void) const
80    {
81      boost::unique_lock<boost::mutex> lock(mutex_);
82      return q_.empty();
83    } // lock is released here
84
85
86    /**
87       \brief access next element in queue
88
89       Access the next element is queue. If container is empty,
90       process is waiting until other process is inserting element
91       into container.
92     */
93    void pop(T& value)
94    {
95      boost::unique_lock<boost::mutex> lock(mutex_);
96      while (q_.empty())
97        condition_.wait(lock);
98      // The obvious choice would be to create a temp copy of front,
99      // pop the queue and then return by-value. This is, however,
100      // dangerous becasue if the copy constructor throws, the queue
101      // has been popped and the element is lost. Instead we choose to
102      // pass via passed reference.
103      static_cast<Derived*>(this)->pop_impl(value, lock);
104    } // lock is released here
105
106
107    /**
108       \brief insert an element into container
109     */
110    void push(const T& t)
111    {
112      boost::unique_lock<boost::mutex> lock(mutex_);
113      static_cast<Derived*>(this)->push_impl(t, lock);
114      lock.unlock(); // unlock the mutex
115
116      // Notify others that data is ready after we have unlocked
117      condition_.notify_one();
118    }
119
120
121#ifdef YAT_HAVE_RVALUE
122    /**
123       \brief insert an element into container
124
125       \note only available if configured and built with cxx11 support
126
127       \since New in yat 0.15
128     */
129    void push(T&& t)
130    {
131      boost::unique_lock<boost::mutex> lock(mutex_);
132      static_cast<Derived*>(this)->push_impl(std::move(t), lock);
133      lock.unlock(); // unlock the mutex
134
135      // Notify others that data is ready after we have unlocked
136      condition_.notify_one();
137    }
138#endif
139
140
141    /**
142       \return Number of elements stored in container
143     */
144    size_type size(void) const
145    {
146      boost::unique_lock<boost::mutex> lock(mutex_);
147      return q_.size();
148    } // lock is released here
149
150
151    /**
152       If Queue is empty() do nothing and return \c false, else pop
153       the element into \a value and return \c true
154     */
155    bool try_pop(T& value)
156    {
157      boost::unique_lock<boost::mutex> lock(mutex_);
158      if (q_.empty())
159        return false;
160      static_cast<Derived*>(this)->pop_impl(value, lock);
161      return true;
162    } // lock is released here
163
164  protected:
165    /**
166       assign other to this
167     */
168    void assign(const BasicQueue& other)
169    {
170      if (this != &other) {
171        // boost::lock guarantees that the two mutexes are locked in
172        // the same order regardless of passed order and thereby
173        // avoiding deadlock when two threads are calling
174        // lhs.assign(rhs) and rhs.assign(lhs) simultaneously.
175        boost::lock(mutex_, other.mutex_);
176        boost::unique_lock<boost::mutex> lock(mutex_, boost::adopt_lock_t());
177        boost::unique_lock<boost::mutex> other_lock(other.mutex_,
178                                                    boost::adopt_lock_t());
179        q_ = other.q_;
180      }
181    }
182
183    /// data
184    Container q_;
185  private:
186    mutable boost::mutex mutex_;
187    boost::condition_variable condition_;
188  };
189
190}}}}
191#endif
Note: See TracBrowser for help on using the repository browser.