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

Last change on this file since 3999 was 3999, checked in by Peter, 14 months ago

update copyright years

  • 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 3999 2020-10-08 23:22:32Z peter $
5//
6// Copyright (C) 2017, 2018, 2020 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 yat. If not, see <http://www.gnu.org/licenses/>.
20
21#include "config_public.h"
22
23#include <condition_variable>
24#include <mutex>
25
26namespace theplu {
27namespace yat {
28namespace utility {
29namespace detail {
30
31  /**
32     \internal Base class for Queue and PriorityQueue
33   */
34  template<class Derived, typename T, class Container>
35  class BasicQueue
36  {
37  public:
38    /// Type of object stored
39    typedef typename Container::value_type value_type;
40
41    /**
42       An unsigned integral type. \see size(void)
43    */
44    typedef typename Container::size_type size_type;
45
46    /**
47       Default constructor
48     */
49    BasicQueue(void) {}
50
51    /**
52       Copy constructor
53     */
54    BasicQueue(const BasicQueue& other)
55    {
56      std::unique_lock<std::mutex> lock(other.mutex_);
57      q_ = other.q_;
58    } // lock is released here
59
60    /**
61       Construct queue from underlying Container
62     */
63    explicit BasicQueue(const Container& container) : q_(container) {}
64
65    /**
66       \brief clear queue
67
68       \since new in yat 0.15
69     */
70    void clear(void)
71    {
72      std::unique_lock<std::mutex> lock(mutex_);
73      return q_.clear();
74    }
75
76
77    /**
78       \return \c true if container's size is zero
79     */
80    bool empty(void) const
81    {
82      std::unique_lock<std::mutex> lock(mutex_);
83      return q_.empty();
84    } // lock is released here
85
86
87    /**
88       \brief access next element in queue
89
90       Access the next element is queue. If container is empty,
91       process is waiting until other process is inserting element
92       into container.
93     */
94    void pop(T& value)
95    {
96      std::unique_lock<std::mutex> lock(mutex_);
97      while (q_.empty())
98        condition_.wait(lock);
99      // The obvious choice would be to create a temp copy of front,
100      // pop the queue and then return by-value. This is, however,
101      // dangerous becasue if the copy constructor throws, the queue
102      // has been popped and the element is lost. Instead we choose to
103      // pass via passed reference.
104      static_cast<Derived*>(this)->pop_impl(value, lock);
105    } // lock is released here
106
107
108    /**
109       \brief insert an element into container
110     */
111    void push(const T& t)
112    {
113      std::unique_lock<std::mutex> lock(mutex_);
114      static_cast<Derived*>(this)->push_impl(t, lock);
115      lock.unlock(); // unlock the mutex
116
117      // Notify others that data is ready after we have unlocked
118      condition_.notify_one();
119    }
120
121
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      std::unique_lock<std::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
139
140    /**
141       \return Number of elements stored in container
142     */
143    size_type size(void) const
144    {
145      std::unique_lock<std::mutex> lock(mutex_);
146      return q_.size();
147    } // lock is released here
148
149
150    /**
151       If Queue is empty() do nothing and return \c false, else pop
152       the element into \a value and return \c true
153     */
154    bool try_pop(T& value)
155    {
156      std::unique_lock<std::mutex> lock(mutex_);
157      if (q_.empty())
158        return false;
159      static_cast<Derived*>(this)->pop_impl(value, lock);
160      return true;
161    } // lock is released here
162
163  protected:
164    /**
165       assign other to this
166     */
167    void assign(const BasicQueue& other)
168    {
169      if (this != &other) {
170        // std::lock guarantees that the two mutexes are locked in
171        // the same order regardless of passed order and thereby
172        // avoiding deadlock when two threads are calling
173        // lhs.assign(rhs) and rhs.assign(lhs) simultaneously.
174        std::lock(mutex_, other.mutex_);
175        std::unique_lock<std::mutex> lock(mutex_, std::adopt_lock_t());
176        std::unique_lock<std::mutex> other_lock(other.mutex_,
177                                                std::adopt_lock_t());
178        q_ = other.q_;
179      }
180    }
181
182    /// data
183    Container q_;
184  private:
185    mutable std::mutex mutex_;
186    std::condition_variable condition_;
187  };
188
189}}}}
190#endif
Note: See TracBrowser for help on using the repository browser.