From 81e914d8c62364b99c30d2f305299acb2ec486f9 Mon Sep 17 00:00:00 2001 From: Dmytro Bogovych Date: Mon, 3 Dec 2018 09:46:10 +0200 Subject: [PATCH] - more cleanups to reduce compile complexity & time --- src/engine/audio/Audio_WavFile.h | 3 - src/engine/helper/HL_CrashRpt.cpp | 1 + src/engine/helper/HL_Sync.cpp | 187 +++++++++++++++++++++++++++++- src/engine/helper/HL_Sync.h | 174 +++------------------------ 4 files changed, 205 insertions(+), 160 deletions(-) diff --git a/src/engine/audio/Audio_WavFile.h b/src/engine/audio/Audio_WavFile.h index 86087679..57479d5c 100644 --- a/src/engine/audio/Audio_WavFile.h +++ b/src/engine/audio/Audio_WavFile.h @@ -2,12 +2,9 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - #ifndef __AUDIO_WAVFILE_H #define __AUDIO_WAVFILE_H -#include "helper/HL_Sync.h" #include "helper/HL_Types.h" #include "Audio_Resampler.h" diff --git a/src/engine/helper/HL_CrashRpt.cpp b/src/engine/helper/HL_CrashRpt.cpp index 634101fe..0b819eb5 100644 --- a/src/engine/helper/HL_CrashRpt.cpp +++ b/src/engine/helper/HL_CrashRpt.cpp @@ -1,6 +1,7 @@ #include "HL_CrashRpt.h" #include "HL_String.h" + // Define this if CrashRpt has to be deployed as .dll // #define CRASHRPT_DYNAMIC diff --git a/src/engine/helper/HL_Sync.cpp b/src/engine/helper/HL_Sync.cpp index b11cbbb8..6514a526 100644 --- a/src/engine/helper/HL_Sync.cpp +++ b/src/engine/helper/HL_Sync.cpp @@ -118,13 +118,13 @@ void BufferQueue::push(const void* data, int bytes) mSignal.notify_all(); } -BufferQueue::Block BufferQueue::pull(std::chrono::milliseconds timeout) +BufferQueue::Block BufferQueue::pull(int milliseconds) { std::unique_lock l(mMutex); std::cv_status status = mBlockList.empty() ? std::cv_status::timeout : std::cv_status::no_timeout; if (mBlockList.empty()) - status = mSignal.wait_for(l, timeout); + status = mSignal.wait_for(l, std::chrono::milliseconds(milliseconds)); Block r; if (status == std::cv_status::no_timeout && !mBlockList.empty()) @@ -135,3 +135,186 @@ BufferQueue::Block BufferQueue::pull(std::chrono::milliseconds timeout) return r; } + +// ----------------- Semaphore --------------------- +Semaphore::Semaphore(unsigned int count) + : m_count(count) +{} + +void Semaphore::notify() +{ + std::unique_lock lock(m_mtx); + m_count++; + m_cv.notify_one(); +} + +void Semaphore::wait() +{ + std::unique_lock lock(m_mtx); + m_cv.wait(lock, [this]() { return m_count > 0; }); + m_count--; +} + +bool Semaphore::waitFor(int milliseconds) { + std::unique_lock lock(m_mtx); + + if (!m_cv.wait_for(lock, std::chrono::milliseconds(milliseconds), [this]() { return m_count > 0; })) + return false; + m_count--; + return true; +} + +// ------------------- TimerQueue ------------------- +TimerQueue::TimerQueue() +{ + m_th = std::thread([this] { run(); }); +} + +TimerQueue::~TimerQueue() +{ + cancelAll(); + // Abusing the timer queue to trigger the shutdown. + add(0, [this](bool) { m_finish = true; }); + m_th.join(); +} + +//! Adds a new timer +// \return +// Returns the ID of the new timer. You can use this ID to cancel the +// timer +uint64_t TimerQueue::add(int64_t milliseconds, std::function handler) { + WorkItem item; + item.end = Clock::now() + std::chrono::milliseconds(milliseconds); + item.handler = std::move(handler); + + std::unique_lock lk(m_mtx); + uint64_t id = ++m_idcounter; + item.id = id; + m_items.push(std::move(item)); + lk.unlock(); + + // Something changed, so wake up timer thread + m_checkWork.notify(); + return id; +} + +//! Cancels the specified timer +// \return +// 1 if the timer was cancelled. +// 0 if you were too late to cancel (or the timer ID was never valid to +// start with) +size_t TimerQueue::cancel(uint64_t id) { + // Instead of removing the item from the container (thus breaking the + // heap integrity), we set the item as having no handler, and put + // that handler on a new item at the top for immediate execution + // The timer thread will then ignore the original item, since it has no + // handler. + std::unique_lock lk(m_mtx); + for (auto&& item : m_items.getContainer()) { + if (item.id == id && item.handler) { + WorkItem newItem; + // Zero time, so it stays at the top for immediate execution + newItem.end = Clock::time_point(); + newItem.id = 0; // Means it is a canceled item + // Move the handler from item to newitem. + // Also, we need to manually set the handler to nullptr, since + // the standard does not guarantee moving an std::function will + // empty it. Some STL implementation will empty it, others will + // not. + newItem.handler = std::move(item.handler); + item.handler = nullptr; + m_items.push(std::move(newItem)); + + lk.unlock(); + // Something changed, so wake up timer thread + m_checkWork.notify(); + return 1; + } + } + return 0; +} + +//! Cancels all timers +// \return +// The number of timers cancelled +size_t TimerQueue::cancelAll() { + // Setting all "end" to 0 (for immediate execution) is ok, + // since it maintains the heap integrity + std::unique_lock lk(m_mtx); + for (auto&& item : m_items.getContainer()) { + if (item.id) { + item.end = Clock::time_point(); + item.id = 0; + } + } + auto ret = m_items.size(); + + lk.unlock(); + m_checkWork.notify(); + return ret; +} + +void TimerQueue::run() +{ + while (!m_finish) + { + auto end = calcWaitTime(); + if (end.first) + { + // Timers found, so wait until it expires (or something else + // changes) + int milliseconds = std::chrono::duration_cast(end.second - std::chrono::steady_clock::now()).count(); + m_checkWork.waitFor(milliseconds); + } else { + // No timers exist, so wait forever until something changes + m_checkWork.wait(); + } + + // Check and execute as much work as possible, such as, all expired + // timers + checkWork(); + } + + // If we are shutting down, we should not have any items left, + // since the shutdown cancels all items + assert(m_items.size() == 0); +} + +std::pair TimerQueue::calcWaitTime() +{ + std::lock_guard lk(m_mtx); + while (m_items.size()) { + if (m_items.top().handler) { + // Item present, so return the new wait time + return std::make_pair(true, m_items.top().end); + } else { + // Discard empty handlers (they were cancelled) + m_items.pop(); + } + } + + // No items found, so return no wait time (causes the thread to wait + // indefinitely) + return std::make_pair(false, Clock::time_point()); +} + +void TimerQueue::checkWork() { + std::unique_lock lk(m_mtx); + while (m_items.size() && m_items.top().end <= Clock::now()) { + WorkItem item(std::move(m_items.top())); + m_items.pop(); + + lk.unlock(); + if (item.handler) + item.handler(item.id == 0); + lk.lock(); + } +} + +bool TimerQueue::WorkItem::operator > (const TimerQueue::WorkItem& other) const { + return end > other.end; +} + +std::vector& TimerQueue::Queue::getContainer() { + return this->c; +} diff --git a/src/engine/helper/HL_Sync.h b/src/engine/helper/HL_Sync.h index d66ad3ec..f99ccb7c 100644 --- a/src/engine/helper/HL_Sync.h +++ b/src/engine/helper/HL_Sync.h @@ -1,4 +1,4 @@ -/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com) +/* Copyright(C) 2007-2018 VoIPobjects (voipobjects.com) * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -6,9 +6,8 @@ #ifndef __HL_SYNC_H #define __HL_SYNC_H -#include #include -#include +#include #include #include #include @@ -24,30 +23,14 @@ public: static long increment(long* value); }; -class Semaphore { +class Semaphore +{ public: - Semaphore(unsigned int count = 0) : m_count(count) {} + Semaphore(unsigned int count = 0); - void notify() { - std::unique_lock lock(m_mtx); - m_count++; - m_cv.notify_one(); - } - - void wait() { - std::unique_lock lock(m_mtx); - m_cv.wait(lock, [this]() { return m_count > 0; }); - m_count--; - } - - template - bool waitUntil(const std::chrono::time_point& point) { - std::unique_lock lock(m_mtx); - if (!m_cv.wait_until(lock, point, [this]() { return m_count > 0; })) - return false; - m_count--; - return true; - } + void notify(); + void wait(); + bool waitFor(int milliseconds); private: std::mutex m_mtx; @@ -94,7 +77,7 @@ public: typedef std::shared_ptr> Block; void push(const void* data, int bytes); - Block pull(std::chrono::milliseconds timeout); + Block pull(int milliseconds); protected: std::mutex mMutex; @@ -115,150 +98,35 @@ protected: // class TimerQueue { public: - TimerQueue() { - m_th = std::thread([this] { run(); }); - } - - ~TimerQueue() { - cancelAll(); - // Abusing the timer queue to trigger the shutdown. - add(0, [this](bool) { m_finish = true; }); - m_th.join(); - } + TimerQueue(); + ~TimerQueue(); //! Adds a new timer // \return // Returns the ID of the new timer. You can use this ID to cancel the // timer - uint64_t add(int64_t milliseconds, std::function handler) { - WorkItem item; - item.end = Clock::now() + std::chrono::milliseconds(milliseconds); - item.handler = std::move(handler); - - std::unique_lock lk(m_mtx); - uint64_t id = ++m_idcounter; - item.id = id; - m_items.push(std::move(item)); - lk.unlock(); - - // Something changed, so wake up timer thread - m_checkWork.notify(); - return id; - } + uint64_t add(int64_t milliseconds, std::function handler); //! Cancels the specified timer // \return // 1 if the timer was cancelled. // 0 if you were too late to cancel (or the timer ID was never valid to // start with) - size_t cancel(uint64_t id) { - // Instead of removing the item from the container (thus breaking the - // heap integrity), we set the item as having no handler, and put - // that handler on a new item at the top for immediate execution - // The timer thread will then ignore the original item, since it has no - // handler. - std::unique_lock lk(m_mtx); - for (auto&& item : m_items.getContainer()) { - if (item.id == id && item.handler) { - WorkItem newItem; - // Zero time, so it stays at the top for immediate execution - newItem.end = Clock::time_point(); - newItem.id = 0; // Means it is a canceled item - // Move the handler from item to newitem. - // Also, we need to manually set the handler to nullptr, since - // the standard does not guarantee moving an std::function will - // empty it. Some STL implementation will empty it, others will - // not. - newItem.handler = std::move(item.handler); - item.handler = nullptr; - m_items.push(std::move(newItem)); - - lk.unlock(); - // Something changed, so wake up timer thread - m_checkWork.notify(); - return 1; - } - } - return 0; - } + size_t cancel(uint64_t id); //! Cancels all timers // \return // The number of timers cancelled - size_t cancelAll() { - // Setting all "end" to 0 (for immediate execution) is ok, - // since it maintains the heap integrity - std::unique_lock lk(m_mtx); - for (auto&& item : m_items.getContainer()) { - if (item.id) { - item.end = Clock::time_point(); - item.id = 0; - } - } - auto ret = m_items.size(); - - lk.unlock(); - m_checkWork.notify(); - return ret; - } + size_t cancelAll(); private: using Clock = std::chrono::steady_clock; TimerQueue(const TimerQueue&) = delete; TimerQueue& operator=(const TimerQueue&) = delete; - void run() { - while (!m_finish) { - auto end = calcWaitTime(); - if (end.first) { - // Timers found, so wait until it expires (or something else - // changes) - m_checkWork.waitUntil(end.second); - } else { - // No timers exist, so wait forever until something changes - m_checkWork.wait(); - } - - // Check and execute as much work as possible, such as, all expired - // timers - checkWork(); - } - - // If we are shutting down, we should not have any items left, - // since the shutdown cancels all items - assert(m_items.size() == 0); - } - - std::pair calcWaitTime() { - std::lock_guard lk(m_mtx); - while (m_items.size()) { - if (m_items.top().handler) { - // Item present, so return the new wait time - return std::make_pair(true, m_items.top().end); - } else { - // Discard empty handlers (they were cancelled) - m_items.pop(); - } - } - - // No items found, so return no wait time (causes the thread to wait - // indefinitely) - return std::make_pair(false, Clock::time_point()); - } - - void checkWork() { - std::unique_lock lk(m_mtx); - while (m_items.size() && m_items.top().end <= Clock::now()) { - WorkItem item(std::move(m_items.top())); - m_items.pop(); - - lk.unlock(); - if (item.handler) - item.handler(item.id == 0); - lk.lock(); - } - } - + void run(); + std::pair calcWaitTime(); + void checkWork(); Semaphore m_checkWork; std::thread m_th; bool m_finish = false; @@ -268,9 +136,7 @@ private: Clock::time_point end; uint64_t id; // id==0 means it was cancelled std::function handler; - bool operator>(const WorkItem& other) const { - return end > other.end; - } + bool operator > (const WorkItem& other) const; }; std::mutex m_mtx; @@ -278,9 +144,7 @@ private: class Queue : public std::priority_queue, std::greater> { public: - std::vector& getContainer() { - return this->c; - } + std::vector& getContainer(); } m_items; };