/* * This file is open source software, licensed to you under the terms * of the Apache License, Version 2.0 (the "License"). See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. You may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #ifndef QUEUE_HH_ #define QUEUE_HH_ #include "circular_buffer.hh" #include "future.hh" #include #include template class queue { std::queue> _q; size_t _max; std::experimental::optional> _not_empty; std::experimental::optional> _not_full; private: void notify_not_empty(); void notify_not_full(); public: explicit queue(size_t size); // Push an item. // // Returns false if the queue was full and the item was not pushed. bool push(T&& a); // pops an item. T pop(); // Consumes items from the queue, passing them to @func, until @func // returns false or the queue it empty // // Returns false if func returned false. template bool consume(Func&& func); // Returns true when the queue is empty. bool empty() const; // Returns true when the queue is full. bool full() const; // Returns a future<> that becomes available when pop() or consume() // can be called. future<> not_empty(); // Returns a future<> that becomes available when push() can be called. future<> not_full(); // Pops element now or when ther is some. Returns a future that becomes // available when some element is available. future pop_eventually(); // Pushes the element now or when there is room. Returns a future<> which // resolves when data was pushed. future<> push_eventually(T&& data); size_t size() const { return _q.size(); } // Destroy any items in the queue, and pass the provided exception to any // waiting readers or writers. void abort(std::exception_ptr ex) { while (!_q.empty()) { _q.pop(); } if (_not_full) { _not_full->set_exception(ex); _not_full= std::experimental::nullopt; } if (_not_empty) { _not_empty->set_exception(std::move(ex)); _not_empty = std::experimental::nullopt; } } }; template inline queue::queue(size_t size) : _max(size) { } template inline void queue::notify_not_empty() { if (_not_empty) { _not_empty->set_value(); _not_empty = std::experimental::optional>(); } } template inline void queue::notify_not_full() { if (_not_full) { _not_full->set_value(); _not_full = std::experimental::optional>(); } } template inline bool queue::push(T&& data) { if (_q.size() < _max) { _q.push(std::move(data)); notify_not_empty(); return true; } else { return false; } } template inline T queue::pop() { if (_q.size() == _max) { notify_not_full(); } T data = std::move(_q.front()); _q.pop(); return data; } template inline future queue::pop_eventually() { if (empty()) { return not_empty().then([this] { return make_ready_future(pop()); }); } else { return make_ready_future(pop()); } } template inline future<> queue::push_eventually(T&& data) { if (full()) { return not_full().then([this, data = std::move(data)] () mutable { _q.push(std::move(data)); notify_not_empty(); }); } else { _q.push(std::move(data)); notify_not_empty(); return make_ready_future<>(); } } template template inline bool queue::consume(Func&& func) { if (_q.size() == _max) { notify_not_full(); } bool running = true; while (!_q.empty() && running) { running = func(std::move(_q.front())); _q.pop(); } return running; } template inline bool queue::empty() const { return _q.empty(); } template inline bool queue::full() const { return _q.size() == _max; } template inline future<> queue::not_empty() { if (!empty()) { return make_ready_future<>(); } else { _not_empty = promise<>(); return _not_empty->get_future(); } } template inline future<> queue::not_full() { if (!full()) { return make_ready_future<>(); } else { _not_full = promise<>(); return _not_full->get_future(); } } #endif /* QUEUE_HH_ */