diff --git a/lib/mu-async-queue.hh b/lib/mu-async-queue.hh new file mode 100644 index 00000000..af4773ed --- /dev/null +++ b/lib/mu-async-queue.hh @@ -0,0 +1,185 @@ +/* +** Copyright (C) 2019 Dirk-Jan C. Binnema +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 3, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#ifndef __MU_ASYNC_QUEUE_HH__ +#define __MU_ASYNC_QUEUE_HH__ + +#include +#include +#include +#include + +namespace Mu { + +constexpr std::size_t UnlimitedAsyncQueueSize{0}; + +template > /**< allocator the items */ + +class AsyncQueue { +public: + using value_type = ItemType; + using allocator_type = Allocator; + using size_type = std::size_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using Timeout = std::chrono::steady_clock::duration; + + bool push (const value_type& item, Timeout timeout = {}) { + return push(std::move(value_type(item))); + } + + /** + * Push an item to the end of the queue by moving it. If the queue is unlimited, + * the timeout is ignored. + * + * @param item the item to move to the end of the queue + * @param timeout and optional timeout + * + * @return true if the item was pushed; false otherwise. + */ + bool push (value_type&& item, Timeout timeout={}) { + + std::lock_guard lock{m_}; + + if constexpr (!unlimited()) { + const auto rv = cv_full_.wait_for(lock, timeout,[&](){ + return !full_unlocked();}) && !full_unlocked(); + if (!rv) + return false; + } + + q_.emplace_back(std::move(item)); + lock.unlock(); + + cv_empty_.notify_one(); + return true; + } + + /** + * Pop and item from the queue + * + * @param item receives the popped item (or nothing) + * @param timeout optional time to wait for an item to become available + * + * @return true if item was set; false otherwise. + */ + bool pop (value_type& item, Timeout timeout = Timeout{}) { + + std::lock_guard lock{m_}; + + if (timeout != Timeout{}) { + const auto rv = cv_empty_.wait_for(lock, timeout,[&]() { + return !q_.empty(); }) && !q_.empty(); + if (!rv) + return false; + + } else if (q_.empty()) + return false; + + item = std::move(q_.front()); + q_.pop_front(); + + lock.unlock(); + cv_full_.notify_one(); + + return true; + } + + /** + * Clear the queue + * + */ + void clear() { + LOCKED; + q_.clear(); + lock.unlock(); + cv_full_.notify_one(); + } + + /** + * Size of the queue + * + * + * @return the size + */ + size_type size() const { + std::lock_guard lock{m_}; + return q_.size(); + } + + /** + * Maximum size of the queue if specified through the template + * parameter; otherwise the (theoretical) max_size of the inner + * container. + * + * @return the maximum size + */ + size_type max_size() const { + if constexpr (unlimited()) + return q_.max_size(); + else + return MaxSize; + } + + /** + * Is the queue empty? + * + * @return true or false + */ + bool empty() const { + std::lock_guard lock{m_}; + return q_.empty(); + } + + /** + * Is the queue full? Returns false unless a maximum size was specified + * (as a template argument) + * + * @return true or false. + */ + bool full() const { + if constexpr (unlimited()) + return false; + + std::lock_guard lock{m_}; + return full_unlocked(); + } + + /** + * Is this queue (theoretically) unlimited in size? + * + * @return true or false + */ + constexpr static bool unlimited() { return MaxSize == UnlimitedAsyncQueueSize; } + +private: + bool full_unlocked() const { return q_.size() >= max_size(); } + + std::deque q_; + mutable std::mutex m_; + std::condition_variable cv_full_, cv_empty_; +}; + +} // namespace mu + +#endif /* __MU_ASYNC_QUEUE_HH__ */ diff --git a/lib/utils/Makefile.am b/lib/utils/Makefile.am index 04f616d7..7bfb6990 100644 --- a/lib/utils/Makefile.am +++ b/lib/utils/Makefile.am @@ -46,6 +46,7 @@ noinst_LTLIBRARIES= \ libmu-utils.la libmu_utils_la_SOURCES= \ + mu-async-queue.hh \ mu-date.c \ mu-date.h \ mu-error.hh \ diff --git a/lib/utils/mu-logger.cc b/lib/utils/mu-logger.cc index 12466d60..2956148b 100644 --- a/lib/utils/mu-logger.cc +++ b/lib/utils/mu-logger.cc @@ -163,10 +163,8 @@ Mu::log_init (const std::string& path, Mu::LogOptions opts) void Mu::log_uninit () { - if (!MuLogInitialized) { - g_warning ("logging was not initialized"); + if (!MuLogInitialized) return; - } if (MuStream.is_open()) MuStream.close(); diff --git a/lib/utils/mu-utils.hh b/lib/utils/mu-utils.hh index 4f011ce4..a5e55f0f 100644 --- a/lib/utils/mu-utils.hh +++ b/lib/utils/mu-utils.hh @@ -23,9 +23,11 @@ #include #include #include +#include #include #include #include +#include namespace Mu { @@ -117,7 +119,14 @@ std::string date_to_time_t_string (const std::string& date, bool first); */ std::string date_to_time_t_string (int64_t t); +using SteadyClock = std::chrono::steady_clock; +static inline int64_t to_ms (SteadyClock::duration dur) { + return std::chrono::duration_cast(dur).count(); +} +static inline int64_t to_us (SteadyClock::duration dur) { + return std::chrono::duration_cast(dur).count(); +} /** * Convert a size string to a size in bytes @@ -157,6 +166,44 @@ static inline std::string to_string (const T& val) } +struct MaybeAnsi { + explicit MaybeAnsi(bool use_color): color_{use_color} {} + + enum struct Color { + Black = 30, + Red = 31, + Green = 32, + Yellow = 33, + Blue = 34, + Magenta = 35, + Cyan = 36, + White = 37, + + BrightBlack = 90, + BrightRed = 91, + BrightGreen = 92, + BrightYellow = 93, + BrightBlue = 94, + BrightMagenta = 95, + BrightCyan = 96, + BrightWhite = 97, + }; + + std::string fg(Color c) const { return ansi(c, true); } + std::string bg(Color c) const { return ansi(c, false); } + + std::string reset() const { return color_ ? "\x1b[0m" : ""; } + +private: + std::string ansi(Color c, bool fg=true) const { + return color_ ? format("\x1b[%dm", static_cast(c) + (fg ? 0 : 10)) : ""; + } + + const bool color_; +}; + + + /** * * don't repeat these catch blocks everywhere...