1 /***
2     This file is part of snapcast
3     Copyright (C) 2014-2020  Johannes Pohl
4 
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 ***/
18 
19 #ifndef QUEUE_H
20 #define QUEUE_H
21 
22 #include <atomic>
23 #include <condition_variable>
24 #include <deque>
25 #include <mutex>
26 #include <thread>
27 
28 template <typename T>
29 class Queue
30 {
31 public:
pop()32     T pop()
33     {
34         std::unique_lock<std::mutex> mlock(mutex_);
35         while (queue_.empty())
36             cond_.wait(mlock);
37 
38         //		std::lock_guard<std::mutex> lock(mutex_);
39         auto val = queue_.front();
40         queue_.pop_front();
41         return val;
42     }
43 
abort_wait()44     void abort_wait()
45     {
46         {
47             std::lock_guard<std::mutex> mlock(mutex_);
48             abort_ = true;
49         }
50         cond_.notify_one();
51     }
52 
wait_for(const std::chrono::microseconds & timeout)53     bool wait_for(const std::chrono::microseconds& timeout) const
54     {
55         std::unique_lock<std::mutex> mlock(mutex_);
56         abort_ = false;
57         if (!cond_.wait_for(mlock, timeout, [this] { return (!queue_.empty() || abort_); }))
58             return false;
59 
60         return !queue_.empty() && !abort_;
61     }
62 
63     bool try_pop(T& item, const std::chrono::microseconds& timeout = std::chrono::microseconds(0))
64     {
65         std::unique_lock<std::mutex> mlock(mutex_);
66         abort_ = false;
67         if (timeout.count() > 0)
68         {
69             if (!cond_.wait_for(mlock, timeout, [this] { return (!queue_.empty() || abort_); }))
70                 return false;
71         }
72 
73         if (queue_.empty() || abort_)
74             return false;
75 
76         item = std::move(queue_.front());
77         queue_.pop_front();
78 
79         return true;
80     }
81 
pop(T & item)82     void pop(T& item)
83     {
84         std::unique_lock<std::mutex> mlock(mutex_);
85         while (queue_.empty())
86             cond_.wait(mlock);
87 
88         item = queue_.front();
89         queue_.pop_front();
90     }
91 
push_front(const T & item)92     void push_front(const T& item)
93     {
94         {
95             std::lock_guard<std::mutex> mlock(mutex_);
96             queue_.push_front(item);
97         }
98         cond_.notify_one();
99     }
100 
back_copy(T & copy)101     bool back_copy(T& copy)
102     {
103         std::lock_guard<std::mutex> mlock(mutex_);
104         if (queue_.empty())
105             return false;
106         copy = queue_.back();
107         return true;
108     }
109 
front_copy(T & copy)110     bool front_copy(T& copy)
111     {
112         std::lock_guard<std::mutex> mlock(mutex_);
113         if (queue_.empty())
114             return false;
115         copy = queue_.front();
116         return true;
117     }
118 
push_front(T && item)119     void push_front(T&& item)
120     {
121         {
122             std::lock_guard<std::mutex> mlock(mutex_);
123             queue_.push_front(std::move(item));
124         }
125         cond_.notify_one();
126     }
127 
push(const T & item)128     void push(const T& item)
129     {
130         {
131             std::lock_guard<std::mutex> mlock(mutex_);
132             queue_.push_back(item);
133         }
134         cond_.notify_one();
135     }
136 
push(T && item)137     void push(T&& item)
138     {
139         {
140             std::lock_guard<std::mutex> mlock(mutex_);
141             queue_.push_back(std::move(item));
142         }
143         cond_.notify_one();
144     }
145 
size()146     size_t size() const
147     {
148         std::lock_guard<std::mutex> mlock(mutex_);
149         return queue_.size();
150     }
151 
empty()152     bool empty() const
153     {
154         return (size() == 0);
155     }
156 
157     Queue() = default;
158     Queue(const Queue&) = delete;            // disable copying
159     Queue& operator=(const Queue&) = delete; // disable assignment
160 
161 private:
162     std::deque<T> queue_;
163     mutable std::atomic<bool> abort_;
164     mutable std::mutex mutex_;
165     mutable std::condition_variable cond_;
166 };
167 
168 
169 #endif
170