- more cleanups to reduce compile complexity & time

This commit is contained in:
Dmytro Bogovych 2018-12-03 09:46:10 +02:00
parent 94465da48e
commit 81e914d8c6
4 changed files with 205 additions and 160 deletions

View File

@ -2,12 +2,9 @@
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_WAVFILE_H #ifndef __AUDIO_WAVFILE_H
#define __AUDIO_WAVFILE_H #define __AUDIO_WAVFILE_H
#include "helper/HL_Sync.h"
#include "helper/HL_Types.h" #include "helper/HL_Types.h"
#include "Audio_Resampler.h" #include "Audio_Resampler.h"

View File

@ -1,6 +1,7 @@
#include "HL_CrashRpt.h" #include "HL_CrashRpt.h"
#include "HL_String.h" #include "HL_String.h"
// Define this if CrashRpt has to be deployed as .dll // Define this if CrashRpt has to be deployed as .dll
// #define CRASHRPT_DYNAMIC // #define CRASHRPT_DYNAMIC

View File

@ -118,13 +118,13 @@ void BufferQueue::push(const void* data, int bytes)
mSignal.notify_all(); mSignal.notify_all();
} }
BufferQueue::Block BufferQueue::pull(std::chrono::milliseconds timeout) BufferQueue::Block BufferQueue::pull(int milliseconds)
{ {
std::unique_lock<std::mutex> l(mMutex); std::unique_lock<std::mutex> l(mMutex);
std::cv_status status = mBlockList.empty() ? std::cv_status::timeout : std::cv_status::no_timeout; std::cv_status status = mBlockList.empty() ? std::cv_status::timeout : std::cv_status::no_timeout;
if (mBlockList.empty()) if (mBlockList.empty())
status = mSignal.wait_for(l, timeout); status = mSignal.wait_for(l, std::chrono::milliseconds(milliseconds));
Block r; Block r;
if (status == std::cv_status::no_timeout && !mBlockList.empty()) if (status == std::cv_status::no_timeout && !mBlockList.empty())
@ -135,3 +135,186 @@ BufferQueue::Block BufferQueue::pull(std::chrono::milliseconds timeout)
return r; return r;
} }
// ----------------- Semaphore ---------------------
Semaphore::Semaphore(unsigned int count)
: m_count(count)
{}
void Semaphore::notify()
{
std::unique_lock<std::mutex> lock(m_mtx);
m_count++;
m_cv.notify_one();
}
void Semaphore::wait()
{
std::unique_lock<std::mutex> lock(m_mtx);
m_cv.wait(lock, [this]() { return m_count > 0; });
m_count--;
}
bool Semaphore::waitFor(int milliseconds) {
std::unique_lock<std::mutex> 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<void(bool)> handler) {
WorkItem item;
item.end = Clock::now() + std::chrono::milliseconds(milliseconds);
item.handler = std::move(handler);
std::unique_lock<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::chrono::milliseconds>(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<bool, TimerQueue::Clock::time_point> TimerQueue::calcWaitTime()
{
std::lock_guard<std::mutex> 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<std::mutex> 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::WorkItem>& TimerQueue::Queue::getContainer() {
return this->c;
}

View File

@ -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 * 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -6,9 +6,8 @@
#ifndef __HL_SYNC_H #ifndef __HL_SYNC_H
#define __HL_SYNC_H #define __HL_SYNC_H
#include <mutex>
#include <condition_variable> #include <condition_variable>
#include <chrono> #include <mutex>
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <queue> #include <queue>
@ -24,30 +23,14 @@ public:
static long increment(long* value); static long increment(long* value);
}; };
class Semaphore { class Semaphore
{
public: public:
Semaphore(unsigned int count = 0) : m_count(count) {} Semaphore(unsigned int count = 0);
void notify() { void notify();
std::unique_lock<std::mutex> lock(m_mtx); void wait();
m_count++; bool waitFor(int milliseconds);
m_cv.notify_one();
}
void wait() {
std::unique_lock<std::mutex> lock(m_mtx);
m_cv.wait(lock, [this]() { return m_count > 0; });
m_count--;
}
template <class Clock, class Duration>
bool waitUntil(const std::chrono::time_point<Clock, Duration>& point) {
std::unique_lock<std::mutex> lock(m_mtx);
if (!m_cv.wait_until(lock, point, [this]() { return m_count > 0; }))
return false;
m_count--;
return true;
}
private: private:
std::mutex m_mtx; std::mutex m_mtx;
@ -94,7 +77,7 @@ public:
typedef std::shared_ptr<std::vector<unsigned char>> Block; typedef std::shared_ptr<std::vector<unsigned char>> Block;
void push(const void* data, int bytes); void push(const void* data, int bytes);
Block pull(std::chrono::milliseconds timeout); Block pull(int milliseconds);
protected: protected:
std::mutex mMutex; std::mutex mMutex;
@ -115,150 +98,35 @@ protected:
// //
class TimerQueue { class TimerQueue {
public: public:
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 //! Adds a new timer
// \return // \return
// Returns the ID of the new timer. You can use this ID to cancel the // Returns the ID of the new timer. You can use this ID to cancel the
// timer // timer
uint64_t add(int64_t milliseconds, std::function<void(bool)> handler) { uint64_t add(int64_t milliseconds, std::function<void(bool)> handler);
WorkItem item;
item.end = Clock::now() + std::chrono::milliseconds(milliseconds);
item.handler = std::move(handler);
std::unique_lock<std::mutex> 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 //! Cancels the specified timer
// \return // \return
// 1 if the timer was cancelled. // 1 if the timer was cancelled.
// 0 if you were too late to cancel (or the timer ID was never valid to // 0 if you were too late to cancel (or the timer ID was never valid to
// start with) // start with)
size_t cancel(uint64_t id) { 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<std::mutex> 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 //! Cancels all timers
// \return // \return
// The number of timers cancelled // The number of timers cancelled
size_t cancelAll() { size_t cancelAll();
// Setting all "end" to 0 (for immediate execution) is ok,
// since it maintains the heap integrity
std::unique_lock<std::mutex> 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;
}
private: private:
using Clock = std::chrono::steady_clock; using Clock = std::chrono::steady_clock;
TimerQueue(const TimerQueue&) = delete; TimerQueue(const TimerQueue&) = delete;
TimerQueue& operator=(const TimerQueue&) = delete; TimerQueue& operator=(const TimerQueue&) = delete;
void run() { void run();
while (!m_finish) { std::pair<bool, Clock::time_point> calcWaitTime();
auto end = calcWaitTime(); void checkWork();
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<bool, Clock::time_point> calcWaitTime() {
std::lock_guard<std::mutex> 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<std::mutex> 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();
}
}
Semaphore m_checkWork; Semaphore m_checkWork;
std::thread m_th; std::thread m_th;
bool m_finish = false; bool m_finish = false;
@ -268,9 +136,7 @@ private:
Clock::time_point end; Clock::time_point end;
uint64_t id; // id==0 means it was cancelled uint64_t id; // id==0 means it was cancelled
std::function<void(bool)> handler; std::function<void(bool)> handler;
bool operator>(const WorkItem& other) const { bool operator > (const WorkItem& other) const;
return end > other.end;
}
}; };
std::mutex m_mtx; std::mutex m_mtx;
@ -278,9 +144,7 @@ private:
class Queue : public std::priority_queue<WorkItem, std::vector<WorkItem>, class Queue : public std::priority_queue<WorkItem, std::vector<WorkItem>,
std::greater<WorkItem>> { std::greater<WorkItem>> {
public: public:
std::vector<WorkItem>& getContainer() { std::vector<WorkItem>& getContainer();
return this->c;
}
} m_items; } m_items;
}; };