Merge branch 'master' into mqtt_client

This commit is contained in:
2026-03-26 12:57:03 +03:00
58 changed files with 887 additions and 1393 deletions

View File

@@ -103,7 +103,7 @@ public:
//! \~english Sets the factory used to create accepted client objects.
//! \~russian Устанавливает фабрику, создающую объекты принятых клиентов.
void setClientFactory(std::function<ServerClient *()> f) { client_factory = f; }
void setClientFactory(std::function<ServerClient *()> f) { client_factory = std::move(f); }
private:
void newClient(ServerClient * c);

View File

@@ -204,7 +204,7 @@ public:
//! \~english Sets the callback receiving both key event and user data.
//! \~russian Устанавливает обратный вызов, получающий событие клавиши и пользовательские данные.
void setSlot(KBFunc slot) { ret_func = slot; }
void setSlot(KBFunc slot) { ret_func = std::move(slot); }
//! \~english Sets the callback that only receives the key event and ignores user data.
//! \~russian Устанавливает обратный вызов, получающий только событие клавиши и игнорирующий пользовательские данные.

View File

@@ -184,7 +184,7 @@ public:
//! \endcode
inline PIDeque(size_t piv_size, std::function<T(size_t i)> f) {
PIINTROSPECTION_CONTAINER_NEW(T, sizeof(T))
expand(piv_size, f);
expand(piv_size, std::move(f));
}
//! \~english Move constructor.
@@ -729,7 +729,7 @@ public:
//! заданному в передаваемой функции `test`, или `def` если такого элемента нет.
//! \~\sa \a indexWhere()
inline const T & atWhere(std::function<bool(const T & e)> test, ssize_t start = 0, const T & def = T()) const {
const ssize_t i = indexWhere(test, start);
const ssize_t i = indexWhere(std::move(test), start);
if (i < 0)
return def;
else
@@ -743,7 +743,7 @@ public:
//! заданному в передаваемой функции `test`, или `def` если такого элемента нет.
//! \~\sa \a lastIndexWhere()
inline const T & lastAtWhere(std::function<bool(const T & e)> test, ssize_t start = -1, const T & def = T()) const {
const ssize_t i = lastIndexWhere(test, start);
const ssize_t i = lastIndexWhere(std::move(test), start);
if (i < 0)
return def;
else
@@ -1265,7 +1265,7 @@ public:
deleteT(pid_data + pid_start + new_size, pid_size - new_size);
pid_size = new_size;
} else if (new_size > pid_size) {
expand(new_size, f);
expand(new_size, std::move(f));
}
return *this;
}
@@ -1328,15 +1328,15 @@ public:
if (index < pid_size - 1) {
const size_t os = pid_size - index - 1;
memmove(reinterpret_cast<void *>(pid_data + pid_start + index + 1),
reinterpret_cast<const void *>(pid_data + pid_start + index),
os * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start + index),
os * sizeof(T));
}
} else {
alloc_backward(pid_size + 1, -1);
if (index > 0) {
memmove(reinterpret_cast<void *>(pid_data + pid_start),
reinterpret_cast<const void *>(pid_data + pid_start + 1),
index * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start + 1),
index * sizeof(T));
}
}
elementNew(pid_data + pid_start + index, e);
@@ -1358,15 +1358,15 @@ public:
if (index < pid_size - 1) {
const size_t os = pid_size - index - 1;
memmove(reinterpret_cast<void *>(pid_data + pid_start + index + 1),
reinterpret_cast<const void *>(pid_data + pid_start + index),
os * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start + index),
os * sizeof(T));
}
} else {
alloc_backward(pid_size + 1, -1);
if (index > 0) {
memmove(reinterpret_cast<void *>(pid_data + pid_start),
reinterpret_cast<const void *>(pid_data + pid_start + 1),
index * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start + 1),
index * sizeof(T));
}
}
elementNew(pid_data + pid_start + index, std::move(e));
@@ -1393,15 +1393,15 @@ public:
alloc_forward(pid_size + v.pid_size);
if (os > 0) {
memmove(reinterpret_cast<void *>(pid_data + pid_start + index + v.pid_size),
reinterpret_cast<const void *>(pid_data + pid_start + index),
os * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start + index),
os * sizeof(T));
}
} else {
alloc_backward(pid_size + v.pid_size, -v.pid_size);
if (index > 0) {
memmove(reinterpret_cast<void *>(pid_data + pid_start),
reinterpret_cast<const void *>(pid_data + pid_start + v.pid_size),
index * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start + v.pid_size),
index * sizeof(T));
}
}
newT(pid_data + pid_start + index, v.pid_data + v.pid_start, v.pid_size);
@@ -1426,15 +1426,15 @@ public:
alloc_forward(pid_size + init_list.size());
if (os > 0) {
memmove(reinterpret_cast<void *>(pid_data + pid_start + index + init_list.size()),
reinterpret_cast<const void *>(pid_data + pid_start + index),
reinterpret_cast<const void *>(pid_data + pid_start + index),
os * sizeof(T));
}
} else {
alloc_backward(pid_size + init_list.size(), -init_list.size());
if (index > 0) {
memmove(reinterpret_cast<void *>(pid_data + pid_start),
reinterpret_cast<const void *>(pid_data + pid_start + init_list.size()),
index * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start + init_list.size()),
index * sizeof(T));
}
}
newT(pid_data + pid_start + index, init_list.begin(), init_list.size());
@@ -1462,13 +1462,13 @@ public:
deleteT(pid_data + pid_start + index, count);
if (os <= index) {
memmove(reinterpret_cast<void *>(pid_data + pid_start + index),
reinterpret_cast<const void *>(pid_data + pid_start + index + count),
os * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start + index + count),
os * sizeof(T));
} else {
if (index > 0) {
memmove(reinterpret_cast<void *>(pid_data + pid_start + count),
reinterpret_cast<const void *>(pid_data + pid_start),
index * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start),
index * sizeof(T));
}
pid_start += count;
}
@@ -1540,7 +1540,7 @@ public:
//! \endcode
//! \~\sa \a sort()
inline PIDeque<T> & sort(std::function<bool(const T & a, const T & b)> comp) {
std::stable_sort(begin(), end(), comp);
std::stable_sort(begin(), end(), std::move(comp));
return *this;
}
@@ -1654,7 +1654,13 @@ public:
//! \endcode
//! \~\sa \a remove(), \a removeOne(), \a removeWhere()
inline PIDeque<T> & removeWhere(std::function<bool(const T & e)> test) {
ssize_t j = indexWhere(test);
ssize_t j = -1;
for (size_t i = pid_start; i < pid_start + pid_size; ++i) {
if (test(pid_data[i])) {
j = ssize_t(i) - pid_start;
break;
}
}
if (j != -1) {
for (size_t i = j + 1; i < pid_size; ++i) {
if (!test(pid_data[i + pid_start])) {
@@ -2545,21 +2551,21 @@ public:
if (index + count > pid_size) count = pid_size - index;
ret.alloc_forward(count);
memcpy(reinterpret_cast<void *>(ret.pid_data + ret.pid_start),
reinterpret_cast<const void *>(pid_data + pid_start + index),
count * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start + index),
count * sizeof(T));
const size_t os = pid_size - index - count;
if (os <= index) {
if (os > 0) {
memmove(reinterpret_cast<void *>(pid_data + pid_start + index),
reinterpret_cast<const void *>(pid_data + pid_start + index + count),
os * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start + index + count),
os * sizeof(T));
}
} else {
if (index > 0) {
memmove(reinterpret_cast<void *>(pid_data + pid_start + count),
reinterpret_cast<const void *>(pid_data + pid_start),
index * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start),
index * sizeof(T));
}
pid_start += count;
}
@@ -2646,8 +2652,8 @@ private:
size_t ns = (pid_rsize - pid_size) / 2;
if (pid_start != ns) {
memmove(reinterpret_cast<void *>(pid_data + ns),
reinterpret_cast<const void *>(pid_data + pid_start),
pid_size * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start),
pid_size * sizeof(T));
pid_start = ns;
}
}
@@ -2656,8 +2662,8 @@ private:
const size_t ns = (pid_rsize - pid_size) / 2;
if (pid_start != ns) {
memmove(reinterpret_cast<void *>(pid_data + ns),
reinterpret_cast<const void *>(pid_data + pid_start),
pid_size * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start),
pid_size * sizeof(T));
pid_start = ns;
}
}
@@ -2694,8 +2700,8 @@ private:
PIINTROSPECTION_CONTAINER_ALLOC(T, (as - pid_rsize))
if (pid_rsize > 0 && pid_data) {
memcpy(reinterpret_cast<void *>(tmp_data + new_start),
reinterpret_cast<const void *>(pid_data + pid_start),
pid_size * sizeof(T));
reinterpret_cast<const void *>(pid_data + pid_start),
pid_size * sizeof(T));
dealloc();
}
pid_data = tmp_data;

View File

@@ -184,7 +184,7 @@ public:
//! \endcode
inline PIVector(size_t size, std::function<T(size_t i)> f) {
PIINTROSPECTION_CONTAINER_NEW(T, sizeof(T))
expand(size, f);
expand(size, std::move(f));
}
//! \~english Move constructor.
@@ -725,7 +725,7 @@ public:
//! заданному в передаваемой функции `test`, или `def` если такого элемента нет.
//! \~\sa \a indexWhere()
inline const T & atWhere(std::function<bool(const T & e)> test, ssize_t start = 0, const T & def = T()) const {
const ssize_t i = indexWhere(test, start);
const ssize_t i = indexWhere(std::move(test), start);
if (i < 0)
return def;
else
@@ -739,7 +739,7 @@ public:
//! заданному в передаваемой функции `test`, или `def` если такого элемента нет.
//! \~\sa \a lastIndexWhere()
inline const T & lastAtWhere(std::function<bool(const T & e)> test, ssize_t start = -1, const T & def = T()) const {
const ssize_t i = lastIndexWhere(test, start);
const ssize_t i = lastIndexWhere(std::move(test), start);
if (i < 0)
return def;
else
@@ -1267,7 +1267,7 @@ public:
deleteT(piv_data + new_size, piv_size - new_size);
piv_size = new_size;
} else if (new_size > piv_size) {
expand(new_size, f);
expand(new_size, std::move(f));
}
return *this;
}
@@ -1486,7 +1486,7 @@ public:
//! \endcode
//! \~\sa \a sort()
inline PIVector<T> & sort(std::function<bool(const T & a, const T & b)> comp) {
std::stable_sort(begin(), end(), comp);
std::stable_sort(begin(), end(), std::move(comp));
return *this;
}
@@ -1599,7 +1599,13 @@ public:
//! \endcode
//! \~\sa \a remove(), \a removeOne(), \a removeWhere()
inline PIVector<T> & removeWhere(std::function<bool(const T & e)> test) {
ssize_t j = indexWhere(test);
ssize_t j = -1;
for (size_t i = 0; i < piv_size; ++i) {
if (test(piv_data[i])) {
j = i;
break;
}
}
if (j != -1) {
for (size_t i = j + 1; i < piv_size; ++i) {
if (!test(piv_data[i])) {

View File

@@ -1070,7 +1070,7 @@ public:
//! \~english Counts elements in the flat vector that pass the `test`.
//! \~russian Подсчитывает элементы в плоском векторе, проходящие `test`.
//! \~\sa PIVector::entries(std::function)
inline int entries(std::function<bool(const T & e)> test) const { return mat.entries(test); }
inline int entries(std::function<bool(const T & e)> test) const { return mat.entries(std::move(test)); }
//! \~english Returns the first index (row, col) of `e` in the 2D array.
@@ -1086,7 +1086,7 @@ public:
//! \~russian Возвращает первый индекс (строка, столбец) в двумерном массиве, проходящий `test`.
//! \~\sa PIVector::indexWhere()
inline Index indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
ssize_t flat = mat.indexWhere(test, start);
ssize_t flat = mat.indexWhere(std::move(test), start);
if (flat < 0 || cols_ == 0) return Index{-1, -1};
return Index{flat / static_cast<ssize_t>(cols_), flat % static_cast<ssize_t>(cols_)};
}
@@ -1104,7 +1104,7 @@ public:
//! \~russian Возвращает последний индекс (строка, столбец) в двумерном массиве, проходящий `test`.
//! \~\sa PIVector::lastIndexWhere()
inline Index lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
ssize_t flat = mat.lastIndexWhere(test, start);
ssize_t flat = mat.lastIndexWhere(std::move(test), start);
if (flat < 0 || cols_ == 0) return Index{-1, -1};
return Index{flat / static_cast<ssize_t>(cols_), flat % static_cast<ssize_t>(cols_)};
}
@@ -1113,12 +1113,12 @@ public:
//! \~english Tests if any element in the flat vector passes the `test`.
//! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`.
//! \~\sa PIVector::any()
inline bool any(std::function<bool(const T & e)> test) const { return mat.any(test); }
inline bool any(std::function<bool(const T & e)> test) const { return mat.any(std::move(test)); }
//! \~english Tests if all elements in the flat vector pass the `test`.
//! \~russian Проверяет, проходят ли все элементы в плоском векторе `test`.
//! \~\sa PIVector::every()
inline bool every(std::function<bool(const T & e)> test) const { return mat.every(test); }
inline bool every(std::function<bool(const T & e)> test) const { return mat.every(std::move(test)); }
//! \~english Fills the entire 2D array with copies of `e`.
//! \~russian Заполняет весь двумерный массив копиями `e`.
@@ -1132,7 +1132,7 @@ public:
//! \~russian Заполняет весь двумерный массив, используя функцию-генератор `f` на основе плоского индекса.
//! \~\sa PIVector::fill(std::function)
inline PIVector2D<T> & fill(std::function<T(size_t i)> f) {
mat.fill(f);
mat.fill(std::move(f));
return *this;
}
@@ -1228,7 +1228,7 @@ public:
//! \~\sa PIVector::map()
template<typename ST>
inline PIVector2D<ST> map(std::function<ST(const T & e)> f) const {
return PIVector2D<ST>(rows_, cols_, mat.template map<ST>(f));
return PIVector2D<ST>(rows_, cols_, mat.template map<ST>(std::move(f)));
}
//! \~english Applies a function (with row and col indices) to each element and returns a new 2D array.
@@ -1250,23 +1250,26 @@ public:
//! \~russian Применяет функцию к каждой строке (с возможностью изменения).
//! \~\sa forEachRow() const, PIVector::forEach()
inline PIVector2D<T> & forEachRow(std::function<void(Row)> f) {
for (size_t r = 0; r < rows_; ++r)
for (size_t r = 0; r < rows_; ++r) {
f(row(r));
}
return *this;
}
//! \~english Applies a function to each row (read-only).
//! \~russian Применяет функцию к каждой строке (только чтение).
inline void forEachRow(std::function<void(RowConst)> f) const {
for (size_t r = 0; r < rows_; ++r)
for (size_t r = 0; r < rows_; ++r) {
f(row(r));
}
}
//! \~english Applies a function to each column (modifiable).
//! \~russian Применяет функцию к каждому столбцу (с возможностью изменения).
inline PIVector2D<T> & forEachColumn(std::function<void(Col)> f) {
for (size_t c = 0; c < cols_; ++c)
for (size_t c = 0; c < cols_; ++c) {
f(col(c));
}
return *this;
}
@@ -1274,8 +1277,9 @@ public:
//! \~russian Применяет функцию к каждому столбцу (только чтение).
//! \param f Function taking a \a ColConst.
inline void forEachColumn(std::function<void(ColConst)> f) const {
for (size_t c = 0; c < cols_; ++c)
for (size_t c = 0; c < cols_; ++c) {
f(col(c));
}
}
//! \~english Accumulates a value across all elements.
@@ -1283,7 +1287,7 @@ public:
//! \~\sa PIVector::reduce()
template<typename ST>
inline ST reduce(std::function<ST(const T & e, const ST & acc)> f, const ST & initial = ST()) const {
return mat.template reduce<ST>(f, initial);
return mat.template reduce<ST>(std::move(f), initial);
}
//! \~english Accumulates a value across all elements with indices.

View File

@@ -726,11 +726,12 @@ inline bool piDeleteSafety(T *& pointer) {
//! \~english In this example "Error!" will be printed on every \b false function return.
//! \~russian В данном примере будет выведен "Error!" при каждом \b false возврате из функции.
class PIP_EXPORT PIScopeExitCall {
NO_COPY_CLASS(PIScopeExitCall)
public:
//! \~\brief
//! \~english Constructor that takes a function to execute
//! \~russian Конструктор, который принимает функцию для выполнения
explicit PIScopeExitCall(std::function<void()> f): func(f) {}
explicit PIScopeExitCall(std::function<void()> f): func(std::move(f)) {}
//! \~\brief
//! \~english Destructor that executes the function if it exists
@@ -758,8 +759,6 @@ public:
}
private:
NO_COPY_CLASS(PIScopeExitCall)
std::function<void()> func;
};
@@ -768,14 +767,14 @@ private:
//! \~english Inherit from this class to make your class non-trivially copyable.
//! \~russian Наследуйтесь от этого класса чтобы сделать свой класс нетривиально копируемым.
struct PIP_EXPORT PINonTriviallyCopyable {
PINonTriviallyCopyable() noexcept = default;
PINonTriviallyCopyable(const PINonTriviallyCopyable &) noexcept = default;
PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept;
PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) noexcept = default;
PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) noexcept = default;
PINonTriviallyCopyable() = default;
PINonTriviallyCopyable(const PINonTriviallyCopyable &) = default;
PINonTriviallyCopyable(PINonTriviallyCopyable &&) ;
PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) = default;
PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) = default;
~PINonTriviallyCopyable() = default;
};
inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept = default;
inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) = default;
//! \~\brief

View File

@@ -113,11 +113,11 @@ public:
//! \~english Sets the callback that receives parsed requests and returns replies.
//! \~russian Устанавливает callback, который получает разобранные запросы и возвращает ответы.
void setRequestCallback(std::function<PIHTTP::MessageMutable(const PIHTTP::MessageConst &)> c) { callback = c; }
void setRequestCallback(std::function<PIHTTP::MessageMutable(const PIHTTP::MessageConst &)> c) { callback = std::move(c); }
//! \~english Sets the credential validator used when HTTP Basic authentication is enabled.
//! \~russian Устанавливает валидатор учетных данных, используемый при включенной HTTP Basic-аутентификации.
void setBasicAuthCallback(std::function<bool(const PIString &, const PIString &)> c) { callback_auth = c; }
void setBasicAuthCallback(std::function<bool(const PIString &, const PIString &)> c) { callback_auth = std::move(c); }
private:
static void addFixedHeaders(PIHTTP::MessageMutable & msg);

View File

@@ -419,7 +419,7 @@ public:
//! \~\param f
//! \~english The callback function returning the next file path, or nullptr to use the internal generator.
//! \~russian Функция обратного вызова, возвращающая путь к следующему файлу, или nullptr для использования внутреннего генератора.
void setFuncGetNewFilePath(std::function<PIString()> f) { f_new_path = f; }
void setFuncGetNewFilePath(std::function<PIString()> f) { f_new_path = std::move(f); }
//! \~english Writes one record with explicit ID and payload.
//! \~russian Записывает одну запись с явным идентификатором и данными.

View File

@@ -527,6 +527,7 @@ bool PIEthernet::listen(bool threaded) {
listen_threaded = true;
server_bounded = false;
server_thread_.start(server_func);
server_thread_.waitForStart();
return true;
}
listen_threaded = server_bounded = false;

View File

@@ -177,7 +177,7 @@ void PIIODevice::setReopenTimeout(PISystemTime timeout) {
//! после каждого успешного потокового чтения. Метод должен быть
//! в формате "bool func(void * data, uchar * readed, int size)"
void PIIODevice::setThreadedReadSlot(ReadRetFunc func) {
func_read = func;
func_read = std::move(func);
}

View File

@@ -103,15 +103,15 @@ public:
//! \~english Sets custom header validation callback.
//! \~russian Устанавливает пользовательский callback проверки заголовка.
void setHeaderCheckSlot(PacketExtractorHeaderFunc f) { func_header = f; }
void setHeaderCheckSlot(PacketExtractorHeaderFunc f) { func_header = std::move(f); }
//! \~english Sets custom payload validation callback.
//! \~russian Устанавливает пользовательский callback проверки полезной нагрузки.
void setPayloadCheckSlot(PacketExtractorPayloadFunc f) { func_payload = f; }
void setPayloadCheckSlot(PacketExtractorPayloadFunc f) { func_payload = std::move(f); }
//! \~english Sets custom footer validation callback.
//! \~russian Устанавливает пользовательский callback проверки окончания пакета.
void setFooterCheckSlot(PacketExtractorFooterFunc f) { func_footer = f; }
void setFooterCheckSlot(PacketExtractorFooterFunc f) { func_footer = std::move(f); }
//! \~english Switches packet extraction to mode "mode".

View File

@@ -62,7 +62,7 @@ public:
//! \~russian Связывает ключ "key" с callback-функцией "func" без аргументов полезной нагрузки.
void assign(Key key, std::function<void()> func) {
auto lf = [func](PIByteArray) { func(); };
functions[key] << lf;
functions[key] << std::move(lf);
}
@@ -77,7 +77,7 @@ public:
func(v);
}
};
functions[key] << lf;
functions[key] << std::move(lf);
}

View File

@@ -175,7 +175,7 @@ struct PIP_EXPORT Function {
identifier = name;
arguments = args;
type = bfCustom;
handler = h;
handler = std::move(h);
}
PIString identifier;

View File

@@ -5,7 +5,7 @@
//! \~russian Математический вектор
/*
PIP - Platform Independent Primitives
Math vector
Math vector
Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru
This program is free software: you can redistribute it and/or modify
@@ -805,11 +805,12 @@ public:
//! \~english Applies \a f to every coordinate without modifying the vector.
//! \~russian Применяет \a f к каждой координате без изменения вектора.
void forEach(std::function<void(const Type &)> f) const { c.forEach(f); }
void forEach(std::function<void(const Type &)> f) const { c.forEach(std::move(f)); }
//! \~english Applies \a f to every coordinate and returns this vector.
//! \~russian Применяет \a f к каждой координате и возвращает этот вектор.
_CVector & forEach(std::function<void(Type &)> f) {
c.forEach(f);
c.forEach(std::move(f));
return *this;
}

View File

@@ -1,255 +0,0 @@
/*
PIP - Platform Independent Primitives
MQTT Client
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pimqttclient.h"
#include "MQTTClient.h"
STATIC_INITIALIZER_BEGIN
MQTTClient_init_options opts = MQTTClient_init_options_initializer;
MQTTClient_global_init(&opts);
STATIC_INITIALIZER_END
PRIVATE_DEFINITION_START(PIMQTT::Client)
MQTTClient client = nullptr;
bool connected = false;
static void connectionLost_callback(void * context, char *) {
((PIMQTT::Client *)context)->mqtt_connectionLost();
}
static void deliveryComplete_callback(void * context, MQTTClient_deliveryToken dt) {
((PIMQTT::Client *)context)->mqtt_deliveryComplete(dt);
}
static int messageArrived_callback(void * context, char * topicName, int topicLen, MQTTClient_message * message) {
PIMQTT::Message msg;
msg.topic = PIString::fromUTF8(topicName);
msg.payload = PIByteArray(message->payload, message->payloadlen);
msg.msg_id = message->msgid;
msg.is_duplicate = message->dup != 0;
msg.qos = static_cast<PIMQTT::QoS>(message->qos);
MQTTClient_freeMessage(&message);
MQTTClient_free(topicName);
((PIMQTT::Client *)context)->mqtt_messageArrived(msg);
return 1;
}
PRIVATE_DEFINITION_END(PIMQTT::Client)
PIMQTT::Client::Client() {
thread = new PIThread([this] { run(); });
thread->start();
}
PIMQTT::Client::~Client() {
thread->stop();
disconnect();
thread->waitForFinish();
piDeleteSafety(thread);
}
void PIMQTT::Client::connect(const PIString & address, const PIString & client, const PIString & username, const PIString & password) {
auto ci = connect_info.getRef();
ci->address = address;
ci->clientID = client;
ci->username = username;
ci->password = password;
changeStatus(Connecting);
}
void PIMQTT::Client::disconnect() {
changeStatus(Disconnecting);
}
void PIMQTT::Client::subscribe(const PIString & topic, QoS qos) {
sub_queue.getRef()->enqueue({topic, qos});
notifier.notify();
}
void PIMQTT::Client::unsubscribe(const PIString & topic) {
unsub_queue.getRef()->enqueue(topic);
notifier.notify();
}
void PIMQTT::Client::publish(const PIString & topic, const PIByteArray & msg, QoS qos) {
Message m;
m.topic = topic;
m.payload = msg;
m.qos = qos;
pub_queue.getRef()->enqueue(m);
notifier.notify();
}
void PIMQTT::Client::mqtt_connectionLost() {
piCoutObj << "mqtt_connectionLost";
PRIVATE->connected = false;
changeStatus(Idle);
disconnected(Error::ServerUnavailable);
}
void PIMQTT::Client::mqtt_deliveryComplete(int token) {}
void PIMQTT::Client::mqtt_messageArrived(const Message & msg) {
piCoutObj << "mqtt_messageArrived";
received(msg);
}
void PIMQTT::Client::connectInternal() {
destroy();
PRIVATE->connected = false;
auto ci = connect_info.get();
PINetworkAddress net_addr = PINetworkAddress::resolve(ci.address);
if (net_addr.port() == 0) net_addr.setPort(1883);
PIString server_uri = "tcp://" + net_addr.toString();
MQTTClient_create(&PRIVATE->client, server_uri.dataAscii(), ci.clientID.dataUTF8(), MQTTCLIENT_PERSISTENCE_NONE, nullptr);
MQTTClient_setCallbacks(PRIVATE->client,
this,
PRIVATE->connectionLost_callback,
PRIVATE->messageArrived_callback,
PRIVATE->deliveryComplete_callback);
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
conn_opts.keepAliveInterval = 9;
conn_opts.cleansession = 1;
conn_opts.connectTimeout = piRound(connect_timeout.toSeconds());
if (ci.username.isNotEmpty()) conn_opts.username = ci.username.dataAscii();
if (ci.password.isNotEmpty()) conn_opts.password = ci.password.dataAscii();
int ret = MQTTClient_connect(PRIVATE->client, &conn_opts);
if (ret != MQTTCLIENT_SUCCESS) {
piCoutObj << "Failed to connect to " + server_uri << ", code" << ret;
changeStatus(Idle);
if (ret < 0) ret = static_cast<int>(Error::ServerUnavailable);
disconnected(static_cast<Error>(ret));
return;
}
PRIVATE->connected = true;
changeStatus(Connected);
connected();
}
void PIMQTT::Client::publishInternal(const Message & m) {
if (!PRIVATE->client) return;
MQTTClient_message pubmsg = MQTTClient_message_initializer;
pubmsg.payload = const_cast<void *>(static_cast<const void *>(m.payload.data()));
pubmsg.payloadlen = m.payload.size_s();
pubmsg.qos = static_cast<int>(m.qos);
int ret = MQTTClient_publishMessage(PRIVATE->client, m.topic.dataAscii(), &pubmsg, nullptr);
if (ret != MQTTCLIENT_SUCCESS) {
piCoutObj << "Failed to publishMessage, code" << ret;
}
}
void PIMQTT::Client::subscribeInternal(const Subscribe & sub) {
if (!PRIVATE->client) return;
int ret = MQTTClient_subscribe(PRIVATE->client, sub.topic.dataUTF8(), static_cast<int>(sub.qos));
if (ret != MQTTCLIENT_SUCCESS) {
piCoutObj << "Failed to subscribe" << sub.topic << ", code" << ret;
}
}
void PIMQTT::Client::unsubscribeInternal(const PIString & topic) {
if (!PRIVATE->client) return;
int ret = MQTTClient_unsubscribe(PRIVATE->client, topic.dataUTF8());
if (ret != MQTTCLIENT_SUCCESS) {
piCoutObj << "Failed to unsubscribe" << topic << ", code" << ret;
}
}
void PIMQTT::Client::destroy() {
if (!PRIVATE->client) return;
if (PRIVATE->connected) MQTTClient_disconnect(PRIVATE->client, 1000);
MQTTClient_destroy(&PRIVATE->client);
PRIVATE->connected = false;
PRIVATE->client = nullptr;
}
void PIMQTT::Client::changeStatus(Status s) {
m_status = s;
notifier.notify();
}
void PIMQTT::Client::run() {
notifier.wait();
piCoutObj << "run" << (int)m_status;
if (thread->isStopping() || m_status == Disconnecting) {
bool was_connected = PRIVATE->connected;
changeStatus(Idle);
destroy();
if (was_connected) disconnected(Error::Unknown);
PRIVATE->connected = false;
return;
}
if (m_status == Connecting) {
connectInternal();
return;
}
if (m_status == Connected) {
Subscribe sub;
{
auto ref = sub_queue.getRef();
if (!ref->isEmpty()) sub = ref->dequeue();
}
if (sub.topic.isNotEmpty()) {
subscribeInternal(sub);
notifier.notify();
return;
}
PIString topic;
{
auto ref = unsub_queue.getRef();
if (!ref->isEmpty()) topic = ref->dequeue();
}
if (topic.isNotEmpty()) {
unsubscribeInternal(topic);
notifier.notify();
return;
}
Message msg;
{
auto ref = pub_queue.getRef();
if (!ref->isEmpty()) msg = ref->dequeue();
}
if (msg.isValid()) {
publishInternal(msg);
notifier.notify();
return;
}
}
}

View File

@@ -27,14 +27,16 @@
#include "piliterals_time.h"
#include "pimqtttypes.h"
#include "pip_mqtt_client_export.h"
#include "piprotectedvariable.h"
#include "pithread.h"
#include "pithreadpoolworker.h"
namespace PIMQTT {
class PIP_EXPORT Client: public PIObject {
class PIP_MQTT_CLIENT_EXPORT Client: public PIObject {
PIOBJECT(PIMQTT::Client)
public:
@@ -103,6 +105,7 @@ private:
std::atomic_int m_status = {Idle};
PISystemTime connect_timeout = 10_s;
PIThread * thread = nullptr;
// PIThreadPoolWorker * worker = nullptr;
};

View File

@@ -57,7 +57,7 @@ public:
//! \~english Sets a callback invoked when the machine finishes.
//! \~russian Задает callback-функцию, вызываемую при завершении машины.
void setOnFinish(std::function<void()> f) { on_finish = f; }
void setOnFinish(std::function<void()> f) { on_finish = std::move(f); }
//! \~english Posts an event to active states and triggers the first matching transition.

View File

@@ -51,7 +51,7 @@ public:
template<typename Ret, typename... Args>
FunctionBase * makeFunction(std::function<Ret(Args...)> func) {
auto * ret = new Function<Args...>();
ret->func = func;
ret->func = std::move(func);
return ret;
}

View File

@@ -5,8 +5,8 @@
//! \~russian Объявляет состояния, используемые в PIStateMachine
/*
PIP - Platform Independent Primitives
State machine node
Ivan Pelipenko peri4ko@yandex.ru
State machine node
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -196,10 +196,10 @@ class PIP_EXPORT PIStateLambda: public PIStateBase {
public:
//! \~english Creates a state backed by enter and exit callbacks.
//! \~russian Создает состояние с callback-функциями входа и выхода.
PIStateLambda(std::function<void()> on_enter, std::function<void()> on_exit = nullptr, const PIString & n = {}): PIStateBase(n) {
enter = on_enter;
exit = on_exit;
}
PIStateLambda(std::function<void()> on_enter, std::function<void()> on_exit = nullptr, const PIString & n = {})
: PIStateBase(n)
, enter(std::move(on_enter))
, exit(std::move(on_exit)) {}
//! \~english Executes the enter callback.
@@ -228,9 +228,8 @@ class PIP_EXPORT PIStateFinal: public PIStateBase {
public:
//! \~english Creates a final state with an optional callback executed on entry.
//! \~russian Создает финальное состояние с необязательной callback-функцией, выполняемой при входе.
PIStateFinal(std::function<void()> on_finish = nullptr, const PIString & n = {}): PIStateBase(n) {
PIStateFinal(std::function<void()> on_finish = nullptr, const PIString & n = {}): PIStateBase(n), enter(std::move(on_finish)) {
is_final = true;
enter = on_finish;
}

View File

@@ -35,7 +35,7 @@ PITransitionBase::~PITransitionBase() {
PITransitionBase * PITransitionBase::addAction(std::function<void()> a) {
action = a;
action = std::move(a);
return this;
}

View File

@@ -68,7 +68,7 @@ public:
//! \~english Installs callback that receives grabbed signals.
//! \~russian Устанавливает обратный вызов, получающий перехваченные сигналы.
static void setSlot(SignalEvent slot) { ret_func = slot; }
static void setSlot(SignalEvent slot) { ret_func = std::move(slot); }
//! \~english Redirects selected signals to the slot set by \a setSlot().
//! \~russian Перенаправляет выбранные сигналы в обработчик, заданный через \a setSlot().

View File

@@ -86,11 +86,9 @@ void PIConditionVariable::wait(PIMutex & lk) {
}
void PIConditionVariable::wait(PIMutex & lk, const std::function<bool()> & condition) {
bool isCondition;
void PIConditionVariable::wait(PIMutex & lk, std::function<bool()> condition) {
while (true) {
isCondition = condition();
if (isCondition) break;
if (condition()) break;
#if defined(WINDOWS)
SleepConditionVariableCS(&PRIVATE->nativeHandle, (PCRITICAL_SECTION)lk.handle(), INFINITE);
#elif defined(FREERTOS)
@@ -122,8 +120,7 @@ bool PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout) {
}
bool PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout, const std::function<bool()> & condition) {
bool isCondition;
bool PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout, std::function<bool()> condition) {
#if defined(WINDOWS) || defined(FREERTOS)
PITimeMeasurer measurer;
#else
@@ -135,8 +132,7 @@ bool PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout, const std:
xEventGroupClearBits(PRIVATE->nativeHandle, 1);
#endif
while (true) {
isCondition = condition();
if (isCondition) break;
if (condition()) break;
bool isTimeout;
#if defined(WINDOWS)
isTimeout = SleepConditionVariableCS(&PRIVATE->nativeHandle,

View File

@@ -106,7 +106,7 @@ public:
//! \param condition вызываемый объект или функция, не принимающая аргументов и возвращающая значение, которое может быть оценено как
//! bool. Вызывается повторно, пока не примет значение true
//!
virtual void wait(PIMutex & lk, const std::function<bool()> & condition);
virtual void wait(PIMutex & lk, std::function<bool ()> condition);
//! \~english Waits for at most \a timeout and returns \c true if awakened before it expires.
@@ -167,7 +167,7 @@ public:
//! bool. Вызывается повторно, пока не примет значение true
//! \return false если достигнут таймаут, или true если условие пробуждения истинно
//!
virtual bool waitFor(PIMutex & lk, PISystemTime timeout, const std::function<bool()> & condition);
virtual bool waitFor(PIMutex & lk, PISystemTime timeout, std::function<bool()> condition);
private:
PRIVATE_DECLARATION(PIP_EXPORT)

View File

@@ -4,22 +4,22 @@
//! \~english Thread-safe variable
//! \~russian Потокобезопасная переменная
/*
PIP - Platform Independent Primitives
Thread-safe variable
Ivan Pelipenko peri4ko@yandex.ru
PIP - Platform Independent Primitives
Thread-safe variable
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, 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 Lesser General Public License for more details.
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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PIPROTECTEDVARIABLE_H
@@ -34,21 +34,29 @@
template<typename T>
class PIP_EXPORT PIProtectedVariable {
public:
//! \~english Constructs %PIProtectedVariable and initialize variable by value `v`.
//! \~russian Создает %PIProtectedVariable и инициализирует переменную значением `v`.
PIProtectedVariable(T v = T()): var(std::move(v)) {}
//! \~\brief
//! \~english Pointer-like wrapper returned by \a getRef() while the protected value remains locked.
//! \~russian Указателеподобная обертка, возвращаемая \a getRef(), пока защищенное значение остается заблокированным.
class PIP_EXPORT Pointer {
friend class PIProtectedVariable<T>;
NO_COPY_CLASS(Pointer);
Pointer & operator=(Pointer && other) = delete;
public:
//! \~english Copies wrapper state for access to the same protected value.
//! \~russian Копирует состояние обертки для доступа к тому же защищенному значению.
Pointer(const Pointer & v): pv(v.pv), counter(v.counter + 1) {}
//! \~english Move constructor - transfers ownership of the lock.
//! \~russian Конструктор перемещения - передает владение блокировкой.
Pointer(Pointer && other): pv(other.pv) { other.can_unlock = false; };
//! \~english Destroys wrapper and releases the mutex when it owns the original lock.
//! \~russian Уничтожает обертку и освобождает мьютекс, когда она владеет исходной блокировкой.
//! \~english Destroys wrapper and releases the mutex.
//! \~russian Уничтожает обертку и освобождает мьютекс.
~Pointer() {
if (counter == 0) pv.mutex.unlock();
if (can_unlock) {
pv.mutex.unlock();
}
}
//! \~english Returns pointer access to the protected value.
@@ -60,11 +68,11 @@ public:
T & operator*() { return pv.var; }
private:
Pointer() = delete;
Pointer(PIProtectedVariable<T> & v): pv(v) {}
explicit Pointer() = delete;
explicit Pointer(PIProtectedVariable<T> & v): pv(v) { pv.mutex.lock(); }
PIProtectedVariable<T> & pv;
int counter = 0;
bool can_unlock = true;
};
//! \~english Replaces the protected value with \a v.
@@ -76,10 +84,7 @@ public:
//! \~english Locks the value and returns wrapper-based access to it.
//! \~russian Блокирует значение и возвращает обертку для доступа к нему.
Pointer getRef() {
mutex.lock();
return Pointer(*this);
}
Pointer getRef() { return Pointer(*this); }
//! \~english Returns a copy of the protected value.
//! \~russian Возвращает копию защищенного значения.
@@ -98,7 +103,7 @@ public:
private:
mutable PIMutex mutex;
T var;
T var = {};
};

View File

@@ -540,7 +540,7 @@ PRIVATE_DEFINITION_END(PIThread)
PIThread::PIThread(void * data, ThreadFunc func, bool startNow, PISystemTime loop_delay): PIObject() {
PIINTROSPECTION_THREAD_NEW(this);
data_ = data;
ret_func = func;
ret_func = std::move(func);
terminating = running_ = lockRun = false;
priority_ = piNormal;
delay_ = loop_delay;
@@ -609,13 +609,13 @@ bool PIThread::start(PISystemTime loop_delay) {
bool PIThread::start(ThreadFunc func) {
ret_func = func;
ret_func = std::move(func);
return start();
}
bool PIThread::start(ThreadFunc func, PISystemTime loop_delay) {
ret_func = func;
ret_func = std::move(func);
delay_ = loop_delay;
return start();
}
@@ -641,7 +641,7 @@ bool PIThread::startOnce() {
bool PIThread::startOnce(ThreadFunc func) {
ret_func = func;
ret_func = std::move(func);
return startOnce();
}
@@ -738,12 +738,12 @@ bool PIThread::_startThread(void * func) {
#ifdef FREERTOS
auto name_ba = createThreadName();
if (xTaskCreate((__THREAD_FUNC_RET__ (*)(void *))func,
(const char *)name_ba.data(), // A name just for humans
128, // This stack size can be checked & adjusted by reading the Stack Highwater
this,
priority_,
&PRIVATE->thread) == pdPASS) {
if (xTaskCreate((__THREAD_FUNC_RET__(*)(void *))func,
(const char *)name_ba.data(), // A name just for humans
128, // This stack size can be checked & adjusted by reading the Stack Highwater
this,
priority_,
&PRIVATE->thread) == pdPASS) {
tid_ = (llong)PRIVATE->thread;
return true;
}
@@ -752,7 +752,7 @@ bool PIThread::_startThread(void * func) {
if (PRIVATE->thread) CloseHandle(PRIVATE->thread);
# ifdef CC_GCC
PRIVATE->thread = (void *)_beginthreadex(0, 0, (__THREAD_FUNC_RET__ (*)(void *))func, this, CREATE_SUSPENDED, 0);
PRIVATE->thread = (void *)_beginthreadex(0, 0, (__THREAD_FUNC_RET__(*)(void *))func, this, CREATE_SUSPENDED, 0);
# else
PRIVATE->thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)func, this, CREATE_SUSPENDED, 0);
# endif
@@ -766,7 +766,7 @@ bool PIThread::_startThread(void * func) {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int ret = pthread_create(&PRIVATE->thread, &attr, (__THREAD_FUNC_RET__ (*)(void *))func, this);
int ret = pthread_create(&PRIVATE->thread, &attr, (__THREAD_FUNC_RET__(*)(void *))func, this);
pthread_attr_destroy(&attr);
// PICout(PICoutManipulators::DefaultControls) << "pthread_create" << PRIVATE->thread;
// piCout << "started" << PRIVATE->thread;
@@ -884,8 +884,8 @@ void PIThread::_runThread() {
PIINTROSPECTION_THREAD_RUN(this);
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "lock" << "...";
if (lockRun) thread_mutex.lock();
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "lock" << "ok";
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "run" << "...";
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "lock" << "ok";
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "run" << "...";
#ifdef PIP_INTROSPECTION
PITimeMeasurer _tm;
#endif
@@ -1042,7 +1042,7 @@ void PIThread::runOnce(PIObject * object, const char * handler, const PIString &
void PIThread::runOnce(std::function<void()> func, const PIString & name) {
PIThread * t = new PIThread();
t->setName(name);
t->setSlot(func);
t->setSlot(std::move(func));
#ifndef MICRO_PIP
__PIThreadCollection::instance()->startedAuto(t);
CONNECT0(void, t, stopped, __PIThreadCollection::instance(), stoppedAuto);

View File

@@ -217,6 +217,7 @@ public:
//! \~english Waits until the thread starts. Returns \b false if the timeout expires first.
//! \~russian Ожидает запуска потока. Возвращает \b false, если таймаут истек раньше.
bool waitForStart(PISystemTime timeout = {});
//! \~english Deprecated overload of \a waitForStart() that accepts milliseconds.
//! \~russian Устаревшая перегрузка \a waitForStart(), принимающая миллисекунды.
bool waitForStart(int timeout_msecs) DEPRECATEDM("use waitForStart(PISystemTime)") {
@@ -226,6 +227,7 @@ public:
//! \~english Waits for thread completion. Returns \b false if the timeout expires first.
//! \~russian Ожидает завершения потока. Возвращает \b false, если таймаут истек раньше.
bool waitForFinish(PISystemTime timeout = {});
//! \~english Deprecated overload of \a waitForFinish() that accepts milliseconds.
//! \~russian Устаревшая перегрузка \a waitForFinish(), принимающая миллисекунды.
bool waitForFinish(int timeout_msecs) DEPRECATEDM("use waitForFinish(PISystemTime)") {

View File

@@ -71,8 +71,8 @@
#include "pispinlock.h"
#include "pithread.h"
#include "pithreadnotifier.h"
#include "pithreadpoolexecutor.h"
#include "pithreadpoolloop.h"
#include "pithreadpoolworker.h"
#include "pitimer.h"
#endif // PITHREADMODULE_H

View File

@@ -1,83 +0,0 @@
/*
PIP - Platform Independent Primitives
Stephan Fomenko
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pithreadpoolexecutor.h"
#include "piliterals_time.h"
/*! \class PIThreadPoolExecutor
* \brief Thread pools address two different problems: they usually provide improved performance when executing large
* numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and
* managing the resources, including threads, consumed when executing a collection of tasks.
*/
PIThreadPoolExecutor::PIThreadPoolExecutor(int corePoolSize): isShutdown_(false) {
for (int i = 0; i < corePoolSize; ++i) {
PIThread * thread = new PIThread([&, i]() {
auto runnable = taskQueue.poll(100_ms, std::function<void()>());
if (runnable) {
runnable();
}
if (isShutdown_ && taskQueue.size() == 0) threadPool[i]->stop();
});
threadPool.push_back(thread);
thread->start();
}
}
bool PIThreadPoolExecutor::awaitTermination(PISystemTime timeout) {
PITimeMeasurer measurer;
for (size_t i = 0; i < threadPool.size(); ++i) {
auto dif = timeout - measurer.elapsed();
if (dif.isNegative()) return false;
if (!threadPool[i]->waitForFinish(dif)) return false;
}
return true;
}
void PIThreadPoolExecutor::shutdownNow() {
isShutdown_ = true;
for (size_t i = 0; i < threadPool.size(); ++i)
threadPool[i]->stop();
}
PIThreadPoolExecutor::~PIThreadPoolExecutor() {
shutdownNow();
while (threadPool.size() > 0)
delete threadPool.take_back();
}
void PIThreadPoolExecutor::execute(const std::function<void()> & runnable) {
if (!isShutdown_) taskQueue.offer(runnable);
}
bool PIThreadPoolExecutor::isShutdown() const {
return isShutdown_;
}
void PIThreadPoolExecutor::shutdown() {
isShutdown_ = true;
}

View File

@@ -1,95 +1,23 @@
//! \~\file pithreadpoolexecutor.h
//! \~\ingroup Thread
//! \brief
//! \~english Thread pool executor
//! \~russian Исполнитель пула потоков
//!
//! \details
//! \~english Executes tasks in a pool of worker threads.
//! \~russian Выполняет задачи в пуле рабочих потоков.
/*
PIP - Platform Independent Primitives
Stephan Fomenko
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PITHREADPOOLEXECUTOR_H
#define PITHREADPOOLEXECUTOR_H
#include "piblockingqueue.h"
#include "pithread.h"
#include "pithreadpoolworker.h"
#include <atomic>
namespace {
DEPRECATEDM("Use pithreadpoolworker.h") const bool deprecated_header_is_used = true;
}
//! \~\ingroup Thread
//! \~\brief
//! \~english Fixed-size pool of worker threads for fire-and-forget tasks.
//! \~russian Фиксированный пул рабочих потоков для задач без ожидания результата.
class PIP_EXPORT PIThreadPoolExecutor {
class DEPRECATEDM("Use PIThreadPoolWorker") PIThreadPoolExecutor: public PIThreadPoolWorker {
public:
//! \~english Constructs executor with \a corePoolSize worker threads.
//! \~russian Создает исполнитель с \a corePoolSize рабочими потоками.
explicit PIThreadPoolExecutor(int corePoolSize);
PIThreadPoolExecutor(int threads_count): PIThreadPoolWorker(threads_count) { start(); }
~PIThreadPoolExecutor() { stopAndWait(); }
//! \~english Stops worker threads and destroys executor resources.
//! \~russian Останавливает рабочие потоки и уничтожает ресурсы исполнителя.
virtual ~PIThreadPoolExecutor();
//! \~\brief
//! \~english Submits \a runnable for asynchronous execution by a worker thread.
//! \~russian Передает \a runnable на асинхронное выполнение рабочим потоком.
//! \details
//! \~english
//! This is a best-effort fire-and-forget call and does not report whether the task was accepted.
//! \After shutdown requests new tasks are ignored.
//! \~russian
//! Это вызов по принципу best-effort без ожидания результата и без сообщения о том, была ли задача принята.
//! \После запроса на завершение новые задачи игнорируются.
void execute(const std::function<void()> & runnable);
//! \~english Requests immediate shutdown and stops worker threads without waiting for queued tasks to finish.
//! \~russian Запрашивает немедленное завершение и останавливает рабочие потоки без ожидания завершения задач в очереди.
void shutdownNow();
//! \~\brief
//! \~english Requests orderly shutdown: new tasks are rejected and workers stop after the current queue is drained.
//! \~russian Запрашивает упорядоченное завершение: новые задачи отклоняются, а рабочие потоки останавливаются после опустошения текущей
//! очереди.
//! \details
//! \~english This method does not wait for worker termination.
//! \~russian Этот метод не ожидает завершения рабочих потоков.
void shutdown();
//! \~english Returns \c true after \a shutdown() or \a shutdownNow() has been requested.
//! \~russian Возвращает \c true после запроса \a shutdown() или \a shutdownNow().
bool isShutdown() const;
//! \~\brief
//! \~english Waits up to \a timeout for all worker threads to finish.
//! \~russian Ожидает до \a timeout завершения всех рабочих потоков.
//! \return
//! \~english \c false if the timeout expires first.
//! \~russian \c false, если таймаут истек раньше.
bool awaitTermination(PISystemTime timeout);
private:
std::atomic_bool isShutdown_;
PIBlockingQueue<std::function<void()>> taskQueue;
PIVector<PIThread *> threadPool;
void execute(std::function<void()> runnable) DEPRECATEDM("Use enqueueTask()") { enqueueTask(runnable); }
void shutdownNow() DEPRECATEDM("Use stopAndWait()") { stopAndWait(); }
void shutdown() DEPRECATEDM("Use stop()") { stop(); }
bool isShutdown() const DEPRECATEDM("Use !isRunning()") { return !isRunning(); }
bool awaitTermination(PISystemTime timeout) DEPRECATEDM("Use waitForFinish()") { return waitForFinish(timeout); }
};
#endif // PITHREADPOOLEXECUTOR_H
#endif

View File

@@ -136,7 +136,7 @@ PIThreadPoolLoop::~PIThreadPoolLoop() {
void PIThreadPoolLoop::setFunction(std::function<void(int)> f) {
func = f;
func = std::move(f);
}
@@ -163,6 +163,6 @@ void PIThreadPoolLoop::exec(int index_start, int index_count) {
void PIThreadPoolLoop::exec(int index_start, int index_count, std::function<void(int)> f) {
setFunction(f);
setFunction(std::move(f));
exec(index_start, index_count);
}

View File

@@ -0,0 +1,238 @@
/*
PIP - Platform Independent Primitives
Ivan Pelipenko, Stephan Fomenko
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pithreadpoolworker.h"
#include "pisysteminfo.h"
//! \addtogroup Thread
//! \{
//! \class PIThreadPoolWorker pithreadpoolworker.h
//! \~\details
//! \~english
//! \PIThreadPoolWorker is a class that implements a fixed-size pool of worker threads for general-purpose task execution. It
//! allows starting threads, enqueuing tasks as functors or class member methods, and managing their execution. The class provides methods
//! to wait for all tasks or specific tasks by ID to complete, as well as to gracefully stop the pool with timeouts. The lifecycle of each
//! task can be monitored using the taskStarted and taskFinished events. It is a versatile tool for multithreaded asynchronous operations.
//! \~russian
//! PIThreadPoolWorker — это класс, реализующий фиксированный пул рабочих потоков для выполнения задач общего назначения. Он
//! позволяет запускать потоки, добавлять в очередь задачи в виде функторов или методов класса, а также управлять их выполнением. Класс
//! предоставляет методы для ожидания завершения всех задач или отдельных задач по их идентификатору, а также для корректной остановки пула
//! с таймаутами. С помощью событий taskStarted и taskFinished можно отслеживать жизненный цикл каждой задачи. Это универсальный инструмент
//! для многопоточного асинхронного выполнения операций.
//!
//! \}
PIThreadPoolWorker::PIThreadPoolWorker(int threads_count) {
if (threads_count < 0) threads_count = PISystemInfo::instance()->processorsCount;
assertm(threads_count > 0, "Invalid threads count!");
for (int i = 0; i < threads_count; ++i) {
Worker * w = new Worker();
w->thread.setSlot([this, w]() { threadFunc(w); });
workers << w;
}
}
PIThreadPoolWorker::~PIThreadPoolWorker() {
piDeleteAllAndClear(workers);
}
void PIThreadPoolWorker::start() {
for (auto w: workers) {
w->thread.start();
w->notifier.notify();
}
}
void PIThreadPoolWorker::stop() {
for (auto w: workers) {
w->thread.stop();
w->notifier.notify();
}
}
bool PIThreadPoolWorker::stopAndWait(PISystemTime timeout) {
stop();
return waitForFinish(timeout);
}
bool PIThreadPoolWorker::waitForStart(PISystemTime timeout) {
PITimeMeasurer tm;
for (auto w: workers) {
if (timeout.isNull())
w->thread.waitForStart();
else {
auto remains = timeout - tm.elapsed();
if (remains.isNegative()) return false;
if (!w->thread.waitForStart(remains)) return false;
}
}
return true;
}
bool PIThreadPoolWorker::waitForFinish(PISystemTime timeout) {
PITimeMeasurer tm;
for (auto w: workers) {
if (timeout.isNull())
w->thread.waitForFinish();
else {
auto remains = timeout - tm.elapsed();
if (remains.isNegative()) return false;
if (!w->thread.waitForFinish(remains)) return false;
}
}
return true;
}
bool PIThreadPoolWorker::isRunning() const {
return workers.every([](Worker * w) { return w->thread.isRunning(); });
}
bool PIThreadPoolWorker::waitForTasks(PISystemTime timeout) {
if (!isRunning()) return tasks_queue.getRef()->isEmpty();
auto checkWorking = [this] {
if (tasks_queue.getRef()->isNotEmpty()) return true;
for (auto * w: workers)
if (w->in_work > 0) return true;
return false;
};
if (timeout.isNull()) {
for (;;) {
if (!checkWorking()) break;
piMinSleep();
}
return true;
}
PITimeMeasurer tm;
while (tm.elapsed() < timeout) {
if (!checkWorking()) return true;
piMinSleep();
}
return tm.elapsed() < timeout;
}
bool PIThreadPoolWorker::waitForTask(int64_t id, PISystemTime timeout) {
if (!isRunning()) return tasks_queue.getRef()->every([id](const Task & t) { return t.id != id; });
auto checkWorking = [this, id] {
if (tasks_queue.getRef()->any([id](const Task & t) { return t.id == id; })) return true;
for (auto * w: workers)
if (w->in_work == id) return true;
return false;
};
if (timeout.isNull()) {
for (;;) {
if (!checkWorking()) break;
piMinSleep();
}
return true;
}
PITimeMeasurer tm;
while (tm.elapsed() < timeout) {
if (!checkWorking()) return true;
piMinSleep();
}
return tm.elapsed() < timeout;
}
void PIThreadPoolWorker::exec() {
start();
waitForStart();
waitForTasks();
stopAndWait();
}
int64_t PIThreadPoolWorker::enqueueTask(std::function<void(int64_t)> func, PIObject * context) {
if (context) {
if (!contexts[context]) {
contexts << context;
CONNECTL(context, deleted, ([this, context](PIObject *) {
contexts.remove(context);
auto qref = tasks_queue.getRef();
// auto prev_size = qref->size();
// piCout << "deleted" << (void *)context << qref->map<void *>([](const Task & t) { return t.context; });
qref->removeWhere([context](const Task & t) { return t.context == context; });
// piCout << prev_size << qref->size() << qref->map<void *>([](const Task & t) { return t.context; });
}));
}
}
int64_t id = ++next_task_id;
Task task;
task.id = id;
task.func = std::move(func);
task.context = context;
tasks_queue.getRef()->enqueue(std::move(task));
for (auto * w: workers)
w->notifier.notify();
return id;
}
bool PIThreadPoolWorker::removeTask(int64_t id) {
auto qref = tasks_queue.getRef();
auto prev_size = qref->size();
qref->removeWhere([id](const Task & t) { return t.id == id; });
return prev_size != qref->size();
}
void PIThreadPoolWorker::clearTasks() {
tasks_queue.getRef()->clear();
}
PIThreadPoolWorker::TaskStatus PIThreadPoolWorker::taskStatus(int64_t id) const {
if (id <= 0) return TaskStatus::Unknown;
auto qref = tasks_queue.getRef();
for (auto w: workers)
if (w->in_work == id) return TaskStatus::InProgress;
for (const auto & t: *qref)
if (t.id == id) return TaskStatus::Enqueued;
return id <= next_task_id ? TaskStatus::DoneOrCancelled : TaskStatus::Unknown;
}
void PIThreadPoolWorker::threadFunc(Worker * w) {
w->notifier.wait();
if (w->thread.isStopping()) return;
Task task;
{
auto ref = tasks_queue.getRef();
if (ref->isEmpty()) return;
task = ref->dequeue();
}
if (!task.isValid()) return;
w->in_work = task.id;
taskStarted(task.id);
task.func(task.id);
w->in_work = -1;
taskFinished(task.id);
w->notifier.notify();
}

View File

@@ -0,0 +1,177 @@
//! \~\file pithreadpoolworker.h
//! \~\ingroup Thread
//! \brief
//! \~english Thread pool worker
//! \~russian Исполнитель пула потоков
/*
PIP - Platform Independent Primitives
Ivan Pelipenko, Stephan Fomenko
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PITHREADPOOLWORKER_H
#define PITHREADPOOLWORKER_H
#include "piprotectedvariable.h"
#include "pithread.h"
//! \~\ingroup Thread
//! \~\brief
//! \~english Fixed-size pool of worker threads for generic-purpose tasks.
//! \~russian Фиксированный пул рабочих потоков для задач общего назначения.
class PIP_EXPORT PIThreadPoolWorker: public PIObject {
PIOBJECT(PIThreadPoolWorker)
public:
//! \~english Constructs executor with \a threads_count worker threads. If \a threads_count < 0 processor threads count used.
//! \~russian Создает исполнитель с \a threads_count рабочими потоками. Если \a threads_count < 0 используется количество потоков
//! процессора.
explicit PIThreadPoolWorker(int threads_count = -1);
//! \~english Destroy worker threads. Call \a stopAndWait() before.
//! \~russian Уничтожает рабочие потоки. Вызывайте перед этим \a stopAndWait().
virtual ~PIThreadPoolWorker();
//! \~english Task status.
//! \~russian Статус задачи.
enum class TaskStatus {
Unknown /** \~english ID <= 0 or not queued yet \~russian ID <= 0 или не поставлена в очередь */,
Enqueued /** \~english Wait for execution \~russian Ожидает выполнения */,
InProgress /** \~english In execution now \~russian В процессе выполнения */,
DoneOrCancelled /** \~english Done or cancelled \~russian Выполнена или отменена */
};
//! \~english Starts the threads.
//! \~russian Запускает потоки.
void start();
//! \~english Requests graceful threads shutdown.
//! \~russian Запрашивает корректное завершение потоков.
void stop();
//! \~english Requests stop and waits for threads completion. Returns \b false if the timeout expires.
//! \~russian Запрашивает остановку и ожидает завершения потоков. Возвращает \b false, если таймаут истек.
bool stopAndWait(PISystemTime timeout = {});
//! \~english Waits until the threads starts. Returns \b false if the timeout expires first.
//! \~russian Ожидает запуска потоков. Возвращает \b false, если таймаут истек раньше.
bool waitForStart(PISystemTime timeout = {});
//! \~english Waits for threads completion. Returns \b false if the timeout expires first.
//! \~russian Ожидает завершения потоков. Возвращает \b false, если таймаут истек раньше.
bool waitForFinish(PISystemTime timeout = {});
//! \~english Returns whether the threads are currently running.
//! \~russian Возвращает, выполняются ли потоки в данный момент.
bool isRunning() const;
//! \~english Waits for all tasks completion. Returns \b false if the timeout expires first.
//! \~russian Ожидает завершения всех задач. Возвращает \b false, если таймаут истек раньше.
bool waitForTasks(PISystemTime timeout = {});
//! \~english Waits for task with id \a id completion. Returns \b false if the timeout expires first.
//! \~russian Ожидает завершения задачи с id \a id. Возвращает \b false, если таймаут истек раньше.
bool waitForTask(int64_t id, PISystemTime timeout = {});
//! \~english Starts threads, wait for all tasks complete and threads stop.
//! \~russian Запускает потоки, ожидает завершения всех задач и остановки потоков.
void exec();
//! \~english Queue functor to execution. Pass task ID in functor. Returns task ID.
//! \~russian Запланировать функтор на выполнение. В функтор передастся ID задачи. Возвращает ID задачи.
int64_t enqueueTask(std::function<void(int64_t)> func, PIObject * context = nullptr);
//! \~english Queue functor to execution. Returns task ID.
//! \~russian Запланировать функтор на выполнение. Возвращает ID задачи.
int64_t enqueueTask(std::function<void()> func, PIObject * context = nullptr) {
return enqueueTask([func](int64_t) { func(); }, context);
}
//! \~english Queue class member method to execution. Pass task ID in method. Returns task ID.
//! \~russian Запланировать член-метод класса на выполнение. В метод передастся ID задачи. Возвращает ID задачи.
template<typename O>
int64_t enqueueTask(O * obj, void (O::*member_func)(int64_t)) {
return enqueueTask([obj, member_func](int64_t id) { (obj->*member_func)(id); },
PIObject::isPIObject(obj) ? dynamic_cast<PIObject *>(obj) : nullptr);
}
//! \~english Queue class member method to execution. Returns task ID.
//! \~russian Запланировать член-метод класса на выполнение. Возвращает ID задачи.
template<typename O>
int64_t enqueueTask(O * obj, void (O::*member_func)()) {
return enqueueTask([obj, member_func](int64_t) { (obj->*member_func)(); },
PIObject::isPIObject(obj) ? dynamic_cast<PIObject *>(obj) : nullptr);
}
//! \~english Remove task with id \a id from queue. Returns if task delete.
//! \~russian Удаляет задачу с id \a id из очереди. Возвращиает была ли задача удалена.
bool removeTask(int64_t id);
//! \~english Remove all queued tasks.
//! \~russian Удаляет все задачи из очереди.
void clearTasks();
//! \~english Returns task status with id \a id.
//! \~russian Возвращиает статус задачи с id \a id.
TaskStatus taskStatus(int64_t id) const;
//! \events
//! \{
//! \fn void taskStarted(int64_t id)
//! \brief
//! \~english Raised on start execution task with id \a id.
//! \~russian Вызывается при старте выполнения задачи с id \a id.
EVENT1(taskStarted, int64_t, id);
//! \fn void taskFinished(int64_t id)
//! \brief
//! \~english Raised on finish execution task with id \a id.
//! \~russian Вызывается при завершении выполнения задачи с id \a id.
EVENT1(taskFinished, int64_t, id);
//! \}
private:
struct Worker {
PIThread thread;
PIThreadNotifier notifier;
std::atomic_int64_t in_work = {-1};
};
struct Task {
bool isValid() const { return func != nullptr; }
PIObject * context = nullptr;
std::function<void(int64_t)> func = nullptr;
int64_t id = -1;
};
void threadFunc(Worker * w);
mutable PIVector<Worker *> workers;
mutable PIProtectedVariable<PIQueue<Task>> tasks_queue;
PISet<PIObject *> contexts;
std::atomic_int64_t next_task_id = {0};
};
#endif // PITHREADPOOLWORKER_H

View File

@@ -122,13 +122,13 @@ PITimer::PITimer(): PIObject() {
PITimer::PITimer(std::function<void(int)> func) {
initFirst();
ret_func = func;
ret_func_delim = std::move(func);
}
PITimer::PITimer(std::function<void()> func) {
initFirst();
ret_func = [func](int) { func(); };
ret_func = std::move(func);
}
@@ -224,7 +224,8 @@ void PITimer::adjustTimes() {
void PITimer::execTick() {
if (!isRunning()) return;
if (lockRun) lock();
if (ret_func) ret_func(1);
if (ret_func) ret_func();
if (ret_func_delim) ret_func_delim(1);
tick(1);
tickEvent(1);
if (callEvents) maybeCallQueuedEvents();
@@ -233,8 +234,8 @@ void PITimer::execTick() {
i.tick = 0;
if (i.func)
i.func(i.delim);
else if (ret_func)
ret_func(i.delim);
else if (ret_func_delim)
ret_func_delim(i.delim);
tick(i.delim);
tickEvent(i.delim);
}
@@ -262,7 +263,7 @@ bool PITimer::start(PISystemTime interval) {
bool PITimer::start(PISystemTime interval, std::function<void()> func) {
if (isRunning()) stopAndWait();
setInterval(interval);
setSlot(func);
setSlot(std::move(func));
return start();
}
@@ -273,8 +274,20 @@ void PITimer::stopAndWait(PISystemTime timeout) {
}
void PITimer::setSlot(std::function<void()> func) {
ret_func_delim = nullptr;
ret_func = std::move(func);
}
void PITimer::setSlot(std::function<void(int)> func) {
ret_func = nullptr;
ret_func_delim = std::move(func);
}
void PITimer::addDelimiter(int delim, std::function<void(int)> func) {
delims << Delimiter(func, delim);
delims << Delimiter(std::move(func), delim);
}

View File

@@ -110,11 +110,6 @@ public:
//! \~english Deprecated overload of \a start() that accepts milliseconds.
//! \~russian Устаревшая перегрузка \a start(), принимающая миллисекунды.
bool start(double interval_ms) DEPRECATEDM("use start(PISystemTime)") { return start(PISystemTime::fromMilliseconds(interval_ms)); }
EVENT_HANDLER0(bool, start);
EVENT_HANDLER0(bool, restart);
EVENT_HANDLER0(void, stop);
//! \~english Deprecated overload of \a stopAndWait() that accepts milliseconds.
//! \~russian Устаревшая перегрузка \a stopAndWait(), принимающая миллисекунды.
@@ -126,22 +121,19 @@ public:
//! \~english Sets a tick callback that ignores the delimiter value.
//! \~russian Устанавливает обратный вызов тика, игнорирующий значение делителя.
void setSlot(std::function<void()> func) {
ret_func = [func](int) { func(); };
}
void setSlot(std::function<void()> func);
//! \~english Sets a tick callback that receives the current delimiter value.
//! \~russian Устанавливает обратный вызов тика, принимающий текущее значение делителя.
void setSlot(std::function<void(int)> func) { ret_func = func; }
void setSlot(std::function<void(int)> func);
//! \~english Enables locking of the internal mutex around tick processing.
//! \~russian Включает блокировку внутреннего мьютекса вокруг обработки тиков.
void needLockRun(bool need) { lockRun = need; }
EVENT_HANDLER0(void, lock) { mutex_.lock(); }
EVENT_HANDLER0(void, unlock) { mutex_.unlock(); }
//! \~english Returns whether the timer drains queued delivery for itself as performer on each main tick. By default \b true.
//! \~russian Возвращает, должен ли таймер обрабатывать отложенную доставку для себя как исполнителя на каждом основном тике. По умолчанию \b true.
//! \~russian Возвращает, должен ли таймер обрабатывать отложенную доставку для себя как исполнителя на каждом основном тике. По
//! умолчанию \b true.
bool isCallQueuedEvents() const { return callEvents; }
//! \~english Enables or disables queued-delivery draining through \a maybeCallQueuedEvents() on each main tick.
@@ -164,9 +156,6 @@ public:
//! \~russian Удаляет все делители со значением \a delim.
void removeDelimiter(int delim);
EVENT_HANDLER0(void, clearDelimiters) { delims.clear(); }
EVENT1(tickEvent, int, delimiter);
//! \handlers
//! \{
@@ -175,31 +164,37 @@ public:
//! \brief
//! \~english Starts the timer with the current \a interval().
//! \~russian Запускает таймер с текущим значением \a interval().
EVENT_HANDLER0(bool, start);
//! \fn bool restart()
//! \brief
//! \~english Stops the timer, then starts it again with the current \a interval().
//! \~russian Останавливает таймер, затем снова запускает его с текущим значением \a interval().
EVENT_HANDLER0(bool, restart);
//! \fn bool stop()
//! \brief
//! \~english Requests stop and wakes the timer thread, but does not wait for completion.
//! \~russian Запрашивает остановку и пробуждает поток таймера, но не ожидает завершения.
EVENT_HANDLER0(void, stop);
//! \fn void clearDelimiters()
//! \brief
//! \~english Remove all frequency delimiters
//! \~russian Удаляет все делители частоты
EVENT_HANDLER0(void, clearDelimiters) { delims.clear(); }
//! \fn void lock()
//! \brief
//! \~english Locks the internal timer mutex.
//! \~russian Блокирует внутренний мьютекс таймера.
EVENT_HANDLER0(void, lock) { mutex_.lock(); }
//! \fn void unlock()
//! \brief
//! \~english Unlocks the internal timer mutex.
//! \~russian Разблокирует внутренний мьютекс таймера.
EVENT_HANDLER0(void, unlock) { mutex_.unlock(); }
//! \}
//! \events
@@ -214,14 +209,14 @@ public:
//! "delimiter" is the frequency delimiter, 1 for the main loop.
//! \~russian
//! "delimiter" - делитель частоты, 1 для основного цикла.
EVENT1(tickEvent, int, delimiter);
//! \}
protected:
struct PIP_EXPORT Delimiter {
Delimiter(std::function<void(int)> func_ = nullptr, int delim_ = 1) {
func = func_;
func = std::move(func_);
delim = delim_;
}
std::function<void(int)> func;
@@ -245,7 +240,8 @@ protected:
PIMutex mutex_;
PISystemTime m_interval, m_interval_x5;
PISystemTime m_time_next;
std::function<void(int)> ret_func = nullptr;
std::function<void()> ret_func = nullptr;
std::function<void(int)> ret_func_delim = nullptr;
PIVector<Delimiter> delims;
PIConditionVariable event;
};

View File

@@ -198,7 +198,7 @@ public:
//! Метод возвращает **false** при любом условии для пустого массива.
//! \~\details
//! \~\sa \a every(), \a contains(), \a entries(), \a forEach()
inline bool any(std::function<bool(uchar e)> test) const { return d.any(test); }
inline bool any(std::function<bool(uchar e)> test) const { return d.any(std::move(test)); }
//! \~english Tests whether all elements in the array passes the test
//! implemented by the provided function `test`.
@@ -213,7 +213,7 @@ public:
//! Метод возвращает **true** при любом условии для пустого массива.
//! \~\details
//! \~\sa \a any(), \a contains(), \a entries(), \a forEach()
inline bool every(std::function<bool(uchar e)> test) const { return d.every(test); }
inline bool every(std::function<bool(uchar e)> test) const { return d.every(std::move(test)); }
//! \~english Full access to element by `index`.
//! \~russian Полный доступ к элементу по индексу `index`.
@@ -340,7 +340,7 @@ public:
//! Обратите внимание: если индекс отрицателен, массив всё равно просматривается от начала к концу.
//! Значение по умолчанию равно 0, что означает, что просматривается весь массив.
//! \~\sa \a every(), \a any(), \a contains(), \a indexWhere()
inline int entries(std::function<bool(uchar e)> test, ssize_t start = 0) const { return d.entries(test, start); }
inline int entries(std::function<bool(uchar e)> test, ssize_t start = 0) const { return d.entries(std::move(test), start); }
bool startsWith(const PIByteArray & o) const;
@@ -405,7 +405,9 @@ public:
//! piCout << v.indexWhere([](const uchar & s){return s > 10;}); // -1
//! \endcode
//! \~\sa \a indexOf(), \a lastIndexOf(), \a lastIndexWhere(), \a contains()
inline ssize_t indexWhere(std::function<bool(const uchar & e)> test, ssize_t start = 0) const { return d.indexWhere(test, start); }
inline ssize_t indexWhere(std::function<bool(const uchar & e)> test, ssize_t start = 0) const {
return d.indexWhere(std::move(test), start);
}
//! \~english Returns the last index at which a given element `e`
//! can be found in the array, or `-1` if it is not present.
@@ -469,7 +471,7 @@ public:
//! и означает, что просматривается весь массив.
//! \~\sa \a indexOf(), \a lastIndexOf(), \a indexWhere(), \a contains()
inline ssize_t lastIndexWhere(std::function<bool(const uchar & e)> test, ssize_t start = -1) const {
return d.lastIndexWhere(test, start);
return d.lastIndexWhere(std::move(test), start);
}
//! \~english Pointer to array
@@ -525,7 +527,7 @@ public:
//! \~\details
//! \~\sa \a resize()
inline PIByteArray & fill(std::function<uchar(size_t i)> f) {
d.fill(f);
d.fill(std::move(f));
return *this;
}
@@ -570,7 +572,7 @@ public:
//! лишние элементы удаляются с конца массива.
//! \~\sa \a size(), \a clear()
inline PIByteArray & resize(size_t new_size, std::function<uchar(size_t i)> f) {
d.resize(new_size, f);
d.resize(new_size, std::move(f));
return *this;
}
@@ -738,7 +740,7 @@ public:
//! \~\details
//! \~\sa \a remove(), \a removeOne(), \a removeWhere()
inline PIByteArray & removeWhere(std::function<bool(uchar e)> test) {
d.removeWhere(test);
d.removeWhere(std::move(test));
return *this;
}
@@ -950,7 +952,7 @@ public:
//! piCout << v2; // {3, 5, 7}
//! \endcode
//! \~\sa \a map(), \a any(), \a every()
inline PIByteArray filter(std::function<bool(const uchar & e)> test) const { return PIByteArray(d.filter(test)); }
inline PIByteArray filter(std::function<bool(const uchar & e)> test) const { return PIByteArray(d.filter(std::move(test))); }
//! \~english Execute function `void f(const uchar & e)` for every element in array.
//! \~russian Выполняет функцию `void f(const uchar & e)` для каждого элемента массива.
@@ -966,7 +968,7 @@ public:
//! piCout << s; // 15
//! \endcode
//! \~\sa \a filter(), \a map(), \a reduce(), \a any(), \a every()
inline void forEach(std::function<void(const uchar & e)> f) const { d.forEach(f); }
inline void forEach(std::function<void(const uchar & e)> f) const { d.forEach(std::move(f)); }
//! \~english Execute function `void f(uchar & e)` for every element in array.
//! \~russian Выполняет функцию `void f(uchar & e)` для каждого элемента массива.
@@ -982,7 +984,7 @@ public:
//! \endcode
//! \~\sa \a filter(), \a map(), \a reduce(), \a any(), \a every()
inline PIByteArray & forEach(std::function<void(uchar & e)> f) {
d.forEach(f);
d.forEach(std::move(f));
return *this;
}
@@ -1005,7 +1007,7 @@ public:
//! \~\sa \a forEach(), \a reduce()
template<typename ST>
inline PIDeque<ST> map(std::function<ST(const uchar & e)> f) const {
return d.map<ST>(f);
return d.map<ST>(std::move(f));
}
//! \~english Applies the function `ST f(const uchar & e, const ST & acc)`
@@ -1051,7 +1053,7 @@ public:
//! \~\sa \a forEach(), \a map()
template<typename ST>
inline ST reduce(std::function<ST(const uchar & e, const ST & acc)> f, const ST & initial = ST()) const {
return d.reduce<ST>(f, initial);
return d.reduce<ST>(std::move(f), initial);
}
//! \~english Convert data to Base 64 and return this byte array
@@ -1103,9 +1105,18 @@ public:
//! \~english Add to the end byte array "data"
//! \~russian Добавляет в конец массива содержимое массива "data"
PIByteArray & append(const PIByteArray & data_) {
#ifdef CC_GCC
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wstringop-overflow"
# pragma GCC diagnostic ignored "-Warray-bounds"
# pragma GCC diagnostic ignored "-Wrestrict"
#endif
const size_t ps = size();
enlarge(data_.size_s());
memcpy(data(ps), data_.data(), data_.size());
#ifdef CC_GCC
# pragma GCC diagnostic pop
#endif
return *this;
}

View File

@@ -105,7 +105,7 @@ extern clock_serv_t __pi_mac_clock;
//! Используйте этот метод для ожидания разниц системных времен или своего времени.
//! Если метод будет вызван для системного времени \a PISystemTime::current(), то
//! ожидание будет почти бесконечным
void PISystemTime::sleep() {
void PISystemTime::sleep() const {
piUSleep(piFloord(toMicroseconds()));
}
@@ -118,7 +118,7 @@ void PISystemTime::toTimespec(void * ts) {
}
PISystemTime::Frequency PISystemTime::toFrequency() {
PISystemTime::Frequency PISystemTime::toFrequency() const {
return PISystemTime::Frequency::fromSystemTime(*this);
}

View File

@@ -253,7 +253,7 @@ public:
//! \~english Sleep for this time
//! \~russian Ожидать это время
void sleep();
void sleep() const;
//! \~english On *nix system assign current value to timespec struct
//! \~russian На *nix системах присваивает время к timespec структуре
@@ -261,7 +261,7 @@ public:
//! \~english Returns \a Frequency that corresponds this time interval
//! \~russian Возвращает \a Frequency соответствующую этому временному интервалу
PISystemTime::Frequency toFrequency();
PISystemTime::Frequency toFrequency() const;
//! \~english Returns "yyyy-MM-dd hh:mm:ss.zzz" for absolute time and "<V> <d|h|m|s|ms|us|ns> ..." for relative
//! \~russian Возвращает "yyyy-MM-dd hh:mm:ss.zzz" для абсолютного времени и "<V> <d|h|m|s|ms|us|ns> ..." для относительного

View File

@@ -262,7 +262,7 @@ PIValueTree & PIValueTree::remove(const PIString & name) {
void PIValueTree::forEachRecursive(std::function<void(const PIValueTree &, const PIString &)> func) {
forEachRecursiveInternal(func);
forEachRecursiveInternal(std::move(func));
}