- more cleanups to reduce compile complexity & time
This commit is contained in:
parent
94465da48e
commit
81e914d8c6
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue