27 Commits

Author SHA1 Message Date
f52fd45936 version 2026-03-26 09:33:23 +03:00
88d4a8e74d PIByteArray gcc one annoying warning off 2026-03-26 09:32:30 +03:00
6ddc7263e8 Merge pull request 'add new PIThreadPoolWorker - rework of PIThreadPoolExecutor' (#202) from PIThreadPoolWorker into master
Reviewed-on: #202
Reviewed-by: Бычков Андрей <andrey@signalmodelling.ru>
2026-03-26 09:12:23 +03:00
a16f629dc5 PIThreadPoolWorker ready to use 2026-03-25 10:59:32 +03:00
5868e0ec9d work with PIThreadPoolWorker 2026-03-24 19:56:43 +03:00
3102b985d5 add new PIThreadPoolWorker - rework of PIThreadPoolExecutor 2026-03-24 14:09:41 +03:00
93547beb38 Merge pull request 'disable gmock build and remove obsolete tests' (#201) from disable_gmock into master
Reviewed-on: #201
2026-03-21 17:00:45 +03:00
5794eac20a Merge pull request 'move std function' (#200) from pitimer_slot into master
Reviewed-on: #200
2026-03-21 17:00:36 +03:00
edb7189013 disable gmock build and remove obsolete tests 2026-03-21 13:31:29 +03:00
dc16a0c903 CMakeLists.txt 2026-03-20 17:26:58 +03:00
96c22e1184 move std function 2026-03-20 16:31:30 +03:00
4537e40832 Merge branch 'master' into pitimer_slot 2026-03-20 15:35:34 +03:00
6efe77a395 add move 2026-03-20 13:46:31 +03:00
6cfc4524f0 PITimer slot optimize 2026-03-20 13:19:55 +03:00
fe3b30bd49 version 5.6.1
patch deploy_tool: procDpkg now prioritize "non-cross" and "non-dev" packages, then only "non-cross"
add PIP_MANUAL_TEST CMake option
2026-03-20 13:00:24 +03:00
a1be5be5a1 increase timeout and remove sleep 2026-03-18 15:01:17 +03:00
99c99c39c2 separate cmake opts for tests 2026-03-18 13:39:01 +03:00
ba63e72bfa Merge pull request 'disable exeptions in cmake' (#199) from disable_exeptions into master
Reviewed-on: #199
2026-03-18 11:48:54 +03:00
79fa549201 fix matrix test 2026-03-18 11:46:08 +03:00
35140ee002 Merge pull request 'some fixes' (#198) from some_fixes into master
Reviewed-on: #198
2026-03-18 11:08:48 +03:00
f0c8bfef0a Merge pull request 'simplify piprotectedvariable' (#197) from protected_var_refact into master
Reviewed-on: #197
2026-03-18 10:58:58 +03:00
ba57aa0144 disable exceptions on win 2026-03-18 09:40:32 +03:00
ccbf86f781 disable exeptions in cmake 2026-03-18 09:30:28 +03:00
ac415ebbb6 revert can_unlock flag 2026-03-18 08:54:06 +03:00
c02b627d47 some fixes
PIEthernet::listen
PIClientServer::Server::~Server
PISystemTime const sleep
ClientServer tests fast and stable
2026-03-17 20:14:22 +03:00
9dc1af921c more simplify Pointer 2026-03-17 19:18:44 +03:00
e761625eab using recursive mutex 2026-03-17 19:14:14 +03:00
54 changed files with 738 additions and 1212 deletions

View File

@@ -97,8 +97,10 @@ public:
{ {
int code = lua_pcall (L, nargs, nresults, msgh); int code = lua_pcall (L, nargs, nresults, msgh);
if (code != LUABRIDGE_LUA_OK) if (code != LUABRIDGE_LUA_OK) {
Throw (LuaException (L, code)); // Throw (LuaException (L, code));
assert(true);
}
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@@ -128,7 +130,8 @@ protected:
private: private:
static int throwAtPanic (lua_State* L) static int throwAtPanic (lua_State* L)
{ {
throw LuaException (L, -1); // throw LuaException (L, -1);
return -1;
} }
}; };

View File

@@ -101,7 +101,8 @@ protected:
{ {
if (m_stackSize == 0) if (m_stackSize == 0)
{ {
throw std::logic_error ("Unable to continue registration"); std::cerr << ("Unable to continue registration");
assert(true);
} }
} }
}; };
@@ -1054,7 +1055,8 @@ public:
{ {
if (m_stackSize == 1) if (m_stackSize == 1)
{ {
throw std::logic_error ("endNamespace () called on global namespace"); std::cerr << ("endNamespace () called on global namespace");
assert(true);
} }
assert (m_stackSize > 1); assert (m_stackSize > 1);
@@ -1150,7 +1152,8 @@ public:
{ {
if (m_stackSize == 1) if (m_stackSize == 1)
{ {
throw std::logic_error ("addProperty () called on global namespace"); std::cerr << ("addProperty () called on global namespace");
assert(true);
} }
assert (lua_istable (L, -1)); // Stack: namespace table (ns) assert (lua_istable (L, -1)); // Stack: namespace table (ns)

View File

@@ -33,6 +33,7 @@
#include <cassert> #include <cassert>
#include <stdexcept> #include <stdexcept>
#include <iostream>
namespace luabridge { namespace luabridge {
@@ -320,7 +321,8 @@ public:
lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ()); lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());
if (!lua_istable (L, -1)) if (!lua_istable (L, -1))
{ {
throw std::logic_error ("The class is not registered in LuaBridge"); std::cerr << ("The class is not registered in LuaBridge");
assert(true);
} }
lua_setmetatable (L, -2); lua_setmetatable (L, -2);
return ud; return ud;
@@ -375,7 +377,8 @@ private:
lua_rawgetp (L, LUA_REGISTRYINDEX, key); lua_rawgetp (L, LUA_REGISTRYINDEX, key);
if (!lua_istable (L, -1)) if (!lua_istable (L, -1))
{ {
throw std::logic_error ("The class is not registered in LuaBridge"); std::cerr << ("The class is not registered in LuaBridge");
assert(true);
} }
lua_setmetatable (L, -2); lua_setmetatable (L, -2);
} }

View File

@@ -5,9 +5,9 @@ if (POLICY CMP0177)
endif() endif()
project(PIP) project(PIP)
set(PIP_MAJOR 5) set(PIP_MAJOR 5)
set(PIP_MINOR 6) set(PIP_MINOR 7)
set(PIP_REVISION 0) set(PIP_REVISION 0)
set(PIP_SUFFIX ) set(PIP_SUFFIX _alpha)
set(PIP_COMPANY SHS) set(PIP_COMPANY SHS)
set(PIP_DOMAIN org.SHS) set(PIP_DOMAIN org.SHS)
@@ -67,11 +67,13 @@ set(PIP_DLL_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE STRING "")
option(ICU "ICU support for convert codepages" ${_ICU_DEFAULT}) option(ICU "ICU support for convert codepages" ${_ICU_DEFAULT})
option(STD_IOSTREAM "Building with std iostream operators support" OFF) option(STD_IOSTREAM "Building with std iostream operators support" OFF)
option(INTROSPECTION "Build with introspection" OFF) option(INTROSPECTION "Build with introspection" OFF)
option(TESTS "Build tests and perform their before install step" OFF) option(TESTS "Build tests" OFF)
option(TESTS_RUN "Run tests before install step" OFF)
option(COVERAGE "Build project with coverage info" OFF) option(COVERAGE "Build project with coverage info" OFF)
option(PIP_FFTW_F "Support fftw module for float" ON) option(PIP_FFTW_F "Support fftw module for float" ON)
option(PIP_FFTW_L "Support fftw module for long double" ON) option(PIP_FFTW_L "Support fftw module for long double" ON)
option(PIP_FFTW_Q "Support fftw module for quad double" OFF) option(PIP_FFTW_Q "Support fftw module for quad double" OFF)
option(PIP_MANUAL_TEST "Build dev test (main.cpp)" OFF)
set(PIP_UTILS 1) set(PIP_UTILS 1)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
@@ -360,10 +362,13 @@ endif()
if(WIN32) if(WIN32)
add_definitions(-DPSAPI_VERSION=1) add_definitions(-DPSAPI_VERSION=1)
if(${C_COMPILER} STREQUAL "cl.exe") if(${C_COMPILER} STREQUAL "cl.exe")
set(CMAKE_CXX_FLAGS "/O2 /Ob2 /Ot /W0") set(CMAKE_CXX_FLAGS "/O2 /Ob2 /Ot /W0 /EH-")
endif() endif()
else() else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
if (NOT DEFINED ANDROID_PLATFORM)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
endif()
if(DEFINED ENV{QNX_HOST} OR PIP_FREERTOS) if(DEFINED ENV{QNX_HOST} OR PIP_FREERTOS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-32") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-32")
endif() endif()
@@ -592,12 +597,14 @@ if (NOT CROSSTOOLS)
#target_link_libraries(pip_plugin pip) #target_link_libraries(pip_plugin pip)
if (NOT DEFINED ANDROID_PLATFORM) if (NOT DEFINED ANDROID_PLATFORM)
if(microhttpd_FOUND AND curl_FOUND) if (PIP_MANUAL_TEST)
add_executable(pip_test "main.cpp") if(microhttpd_FOUND AND curl_FOUND)
target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client) add_executable(pip_test "main.cpp")
if(sodium_FOUND) target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client)
add_executable(pip_cloud_test "main_picloud_test.cpp") if(sodium_FOUND)
target_link_libraries(pip_cloud_test pip_cloud) add_executable(pip_cloud_test "main_picloud_test.cpp")
target_link_libraries(pip_cloud_test pip_cloud)
endif()
endif() endif()
endif() endif()
endif() endif()
@@ -823,6 +830,9 @@ if (PIP_TESTS_LIST)
foreach(_test ${PIP_TESTS_LIST}) foreach(_test ${PIP_TESTS_LIST})
message(" * ${_test}") message(" * ${_test}")
endforeach() endforeach()
if (TESTS_RUN)
message("TESTS_RUN ON -> Run tests before install step")
endif()
else() else()
message(" Tests: skip (tests off)") message(" Tests: skip (tests off)")
endif() endif()

View File

@@ -72,11 +72,7 @@ PIClientServer::Server::~Server() {
clean_thread->waitForFinish(); clean_thread->waitForFinish();
piDeleteSafety(clean_thread); piDeleteSafety(clean_thread);
stopServer(); stopServer();
for (auto c: clients) { closeAll();
c->aboutDelete();
c->destroy();
delete c;
}
piDeleteSafety(tcp_server); piDeleteSafety(tcp_server);
} }

View File

@@ -247,7 +247,7 @@ PIHTTPClient * PIHTTPClient::onFinish(std::function<void()> f) {
PIHTTPClient * PIHTTPClient::onFinish(std::function<void(const PIHTTP::MessageConst &)> f) { PIHTTPClient * PIHTTPClient::onFinish(std::function<void(const PIHTTP::MessageConst &)> f) {
on_finish = f; on_finish = std::move(f);
return this; return this;
} }
@@ -258,7 +258,7 @@ PIHTTPClient * PIHTTPClient::onError(std::function<void()> f) {
PIHTTPClient * PIHTTPClient::onError(std::function<void(const PIHTTP::MessageConst &)> f) { PIHTTPClient * PIHTTPClient::onError(std::function<void(const PIHTTP::MessageConst &)> f) {
on_error = f; on_error = std::move(f);
return this; return this;
} }
@@ -269,7 +269,7 @@ PIHTTPClient * PIHTTPClient::onAbort(std::function<void()> f) {
PIHTTPClient * PIHTTPClient::onAbort(std::function<void(const PIHTTP::MessageConst &)> f) { PIHTTPClient * PIHTTPClient::onAbort(std::function<void(const PIHTTP::MessageConst &)> f) {
on_abort = f; on_abort = std::move(f);
return this; return this;
} }

View File

@@ -45,7 +45,7 @@ bool PIHTTPServer::registerPath(const PIString & path, PIHTTP::Method method, Re
Endpoint ep; Endpoint ep;
if (!ep.create(path)) return false; if (!ep.create(path)) return false;
ep.method = method; ep.method = method;
ep.function = functor; ep.function = std::move(functor);
endpoints[ep.priority] << ep; endpoints[ep.priority] << ep;
return true; return true;
} }

View File

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

View File

@@ -204,7 +204,7 @@ public:
//! \~english Sets the callback receiving both key event and user data. //! \~english Sets the callback receiving both key event and user data.
//! \~russian Устанавливает обратный вызов, получающий событие клавиши и пользовательские данные. //! \~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. //! \~english Sets the callback that only receives the key event and ignores user data.
//! \~russian Устанавливает обратный вызов, получающий только событие клавиши и игнорирующий пользовательские данные. //! \~russian Устанавливает обратный вызов, получающий только событие клавиши и игнорирующий пользовательские данные.

View File

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

View File

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

View File

@@ -1070,7 +1070,7 @@ public:
//! \~english Counts elements in the flat vector that pass the `test`. //! \~english Counts elements in the flat vector that pass the `test`.
//! \~russian Подсчитывает элементы в плоском векторе, проходящие `test`. //! \~russian Подсчитывает элементы в плоском векторе, проходящие `test`.
//! \~\sa PIVector::entries(std::function) //! \~\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. //! \~english Returns the first index (row, col) of `e` in the 2D array.
@@ -1086,7 +1086,7 @@ public:
//! \~russian Возвращает первый индекс (строка, столбец) в двумерном массиве, проходящий `test`. //! \~russian Возвращает первый индекс (строка, столбец) в двумерном массиве, проходящий `test`.
//! \~\sa PIVector::indexWhere() //! \~\sa PIVector::indexWhere()
inline Index indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const { 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}; if (flat < 0 || cols_ == 0) return Index{-1, -1};
return Index{flat / static_cast<ssize_t>(cols_), flat % static_cast<ssize_t>(cols_)}; return Index{flat / static_cast<ssize_t>(cols_), flat % static_cast<ssize_t>(cols_)};
} }
@@ -1104,7 +1104,7 @@ public:
//! \~russian Возвращает последний индекс (строка, столбец) в двумерном массиве, проходящий `test`. //! \~russian Возвращает последний индекс (строка, столбец) в двумерном массиве, проходящий `test`.
//! \~\sa PIVector::lastIndexWhere() //! \~\sa PIVector::lastIndexWhere()
inline Index lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const { 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}; if (flat < 0 || cols_ == 0) return Index{-1, -1};
return Index{flat / static_cast<ssize_t>(cols_), flat % static_cast<ssize_t>(cols_)}; 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`. //! \~english Tests if any element in the flat vector passes the `test`.
//! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`. //! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`.
//! \~\sa PIVector::any() //! \~\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`. //! \~english Tests if all elements in the flat vector pass the `test`.
//! \~russian Проверяет, проходят ли все элементы в плоском векторе `test`. //! \~russian Проверяет, проходят ли все элементы в плоском векторе `test`.
//! \~\sa PIVector::every() //! \~\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`. //! \~english Fills the entire 2D array with copies of `e`.
//! \~russian Заполняет весь двумерный массив копиями `e`. //! \~russian Заполняет весь двумерный массив копиями `e`.
@@ -1132,7 +1132,7 @@ public:
//! \~russian Заполняет весь двумерный массив, используя функцию-генератор `f` на основе плоского индекса. //! \~russian Заполняет весь двумерный массив, используя функцию-генератор `f` на основе плоского индекса.
//! \~\sa PIVector::fill(std::function) //! \~\sa PIVector::fill(std::function)
inline PIVector2D<T> & fill(std::function<T(size_t i)> f) { inline PIVector2D<T> & fill(std::function<T(size_t i)> f) {
mat.fill(f); mat.fill(std::move(f));
return *this; return *this;
} }
@@ -1228,7 +1228,7 @@ public:
//! \~\sa PIVector::map() //! \~\sa PIVector::map()
template<typename ST> template<typename ST>
inline PIVector2D<ST> map(std::function<ST(const T & e)> f) const { 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. //! \~english Applies a function (with row and col indices) to each element and returns a new 2D array.
@@ -1250,23 +1250,26 @@ public:
//! \~russian Применяет функцию к каждой строке (с возможностью изменения). //! \~russian Применяет функцию к каждой строке (с возможностью изменения).
//! \~\sa forEachRow() const, PIVector::forEach() //! \~\sa forEachRow() const, PIVector::forEach()
inline PIVector2D<T> & forEachRow(std::function<void(Row)> f) { 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)); f(row(r));
}
return *this; return *this;
} }
//! \~english Applies a function to each row (read-only). //! \~english Applies a function to each row (read-only).
//! \~russian Применяет функцию к каждой строке (только чтение). //! \~russian Применяет функцию к каждой строке (только чтение).
inline void forEachRow(std::function<void(RowConst)> f) const { 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)); f(row(r));
}
} }
//! \~english Applies a function to each column (modifiable). //! \~english Applies a function to each column (modifiable).
//! \~russian Применяет функцию к каждому столбцу (с возможностью изменения). //! \~russian Применяет функцию к каждому столбцу (с возможностью изменения).
inline PIVector2D<T> & forEachColumn(std::function<void(Col)> f) { 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)); f(col(c));
}
return *this; return *this;
} }
@@ -1274,8 +1277,9 @@ public:
//! \~russian Применяет функцию к каждому столбцу (только чтение). //! \~russian Применяет функцию к каждому столбцу (только чтение).
//! \param f Function taking a \a ColConst. //! \param f Function taking a \a ColConst.
inline void forEachColumn(std::function<void(ColConst)> f) const { 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)); f(col(c));
}
} }
//! \~english Accumulates a value across all elements. //! \~english Accumulates a value across all elements.
@@ -1283,7 +1287,7 @@ public:
//! \~\sa PIVector::reduce() //! \~\sa PIVector::reduce()
template<typename ST> template<typename ST>
inline ST reduce(std::function<ST(const T & e, const ST & acc)> f, const ST & initial = ST()) const { 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. //! \~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. //! \~english In this example "Error!" will be printed on every \b false function return.
//! \~russian В данном примере будет выведен "Error!" при каждом \b false возврате из функции. //! \~russian В данном примере будет выведен "Error!" при каждом \b false возврате из функции.
class PIP_EXPORT PIScopeExitCall { class PIP_EXPORT PIScopeExitCall {
NO_COPY_CLASS(PIScopeExitCall)
public: public:
//! \~\brief //! \~\brief
//! \~english Constructor that takes a function to execute //! \~english Constructor that takes a function to execute
//! \~russian Конструктор, который принимает функцию для выполнения //! \~russian Конструктор, который принимает функцию для выполнения
explicit PIScopeExitCall(std::function<void()> f): func(f) {} explicit PIScopeExitCall(std::function<void()> f): func(std::move(f)) {}
//! \~\brief //! \~\brief
//! \~english Destructor that executes the function if it exists //! \~english Destructor that executes the function if it exists
@@ -758,8 +759,6 @@ public:
} }
private: private:
NO_COPY_CLASS(PIScopeExitCall)
std::function<void()> func; std::function<void()> func;
}; };
@@ -768,14 +767,14 @@ private:
//! \~english Inherit from this class to make your class non-trivially copyable. //! \~english Inherit from this class to make your class non-trivially copyable.
//! \~russian Наследуйтесь от этого класса чтобы сделать свой класс нетривиально копируемым. //! \~russian Наследуйтесь от этого класса чтобы сделать свой класс нетривиально копируемым.
struct PIP_EXPORT PINonTriviallyCopyable { struct PIP_EXPORT PINonTriviallyCopyable {
PINonTriviallyCopyable() noexcept = default; PINonTriviallyCopyable() = default;
PINonTriviallyCopyable(const PINonTriviallyCopyable &) noexcept = default; PINonTriviallyCopyable(const PINonTriviallyCopyable &) = default;
PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept; PINonTriviallyCopyable(PINonTriviallyCopyable &&) ;
PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) noexcept = default; PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) = default;
PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) noexcept = default; PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) = default;
~PINonTriviallyCopyable() = default; ~PINonTriviallyCopyable() = default;
}; };
inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept = default; inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) = default;
//! \~\brief //! \~\brief

View File

@@ -113,11 +113,11 @@ public:
//! \~english Sets the callback that receives parsed requests and returns replies. //! \~english Sets the callback that receives parsed requests and returns replies.
//! \~russian Устанавливает callback, который получает разобранные запросы и возвращает ответы. //! \~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. //! \~english Sets the credential validator used when HTTP Basic authentication is enabled.
//! \~russian Устанавливает валидатор учетных данных, используемый при включенной HTTP Basic-аутентификации. //! \~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: private:
static void addFixedHeaders(PIHTTP::MessageMutable & msg); static void addFixedHeaders(PIHTTP::MessageMutable & msg);

View File

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

View File

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

View File

@@ -177,7 +177,7 @@ void PIIODevice::setReopenTimeout(PISystemTime timeout) {
//! после каждого успешного потокового чтения. Метод должен быть //! после каждого успешного потокового чтения. Метод должен быть
//! в формате "bool func(void * data, uchar * readed, int size)" //! в формате "bool func(void * data, uchar * readed, int size)"
void PIIODevice::setThreadedReadSlot(ReadRetFunc func) { 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. //! \~english Sets custom header validation callback.
//! \~russian Устанавливает пользовательский 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. //! \~english Sets custom payload validation callback.
//! \~russian Устанавливает пользовательский 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. //! \~english Sets custom footer validation callback.
//! \~russian Устанавливает пользовательский 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". //! \~english Switches packet extraction to mode "mode".

View File

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

View File

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

View File

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

View File

@@ -57,7 +57,7 @@ public:
//! \~english Sets a callback invoked when the machine finishes. //! \~english Sets a callback invoked when the machine finishes.
//! \~russian Задает callback-функцию, вызываемую при завершении машины. //! \~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. //! \~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> template<typename Ret, typename... Args>
FunctionBase * makeFunction(std::function<Ret(Args...)> func) { FunctionBase * makeFunction(std::function<Ret(Args...)> func) {
auto * ret = new Function<Args...>(); auto * ret = new Function<Args...>();
ret->func = func; ret->func = std::move(func);
return ret; return ret;
} }

View File

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

View File

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

View File

@@ -68,7 +68,7 @@ public:
//! \~english Installs callback that receives grabbed signals. //! \~english Installs callback that receives grabbed signals.
//! \~russian Устанавливает обратный вызов, получающий перехваченные сигналы. //! \~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(). //! \~english Redirects selected signals to the slot set by \a setSlot().
//! \~russian Перенаправляет выбранные сигналы в обработчик, заданный через \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) { void PIConditionVariable::wait(PIMutex & lk, std::function<bool()> condition) {
bool isCondition;
while (true) { while (true) {
isCondition = condition(); if (condition()) break;
if (isCondition) break;
#if defined(WINDOWS) #if defined(WINDOWS)
SleepConditionVariableCS(&PRIVATE->nativeHandle, (PCRITICAL_SECTION)lk.handle(), INFINITE); SleepConditionVariableCS(&PRIVATE->nativeHandle, (PCRITICAL_SECTION)lk.handle(), INFINITE);
#elif defined(FREERTOS) #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 PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout, std::function<bool()> condition) {
bool isCondition;
#if defined(WINDOWS) || defined(FREERTOS) #if defined(WINDOWS) || defined(FREERTOS)
PITimeMeasurer measurer; PITimeMeasurer measurer;
#else #else
@@ -135,8 +132,7 @@ bool PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout, const std:
xEventGroupClearBits(PRIVATE->nativeHandle, 1); xEventGroupClearBits(PRIVATE->nativeHandle, 1);
#endif #endif
while (true) { while (true) {
isCondition = condition(); if (condition()) break;
if (isCondition) break;
bool isTimeout; bool isTimeout;
#if defined(WINDOWS) #if defined(WINDOWS)
isTimeout = SleepConditionVariableCS(&PRIVATE->nativeHandle, isTimeout = SleepConditionVariableCS(&PRIVATE->nativeHandle,

View File

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

View File

@@ -540,7 +540,7 @@ PRIVATE_DEFINITION_END(PIThread)
PIThread::PIThread(void * data, ThreadFunc func, bool startNow, PISystemTime loop_delay): PIObject() { PIThread::PIThread(void * data, ThreadFunc func, bool startNow, PISystemTime loop_delay): PIObject() {
PIINTROSPECTION_THREAD_NEW(this); PIINTROSPECTION_THREAD_NEW(this);
data_ = data; data_ = data;
ret_func = func; ret_func = std::move(func);
terminating = running_ = lockRun = false; terminating = running_ = lockRun = false;
priority_ = piNormal; priority_ = piNormal;
delay_ = loop_delay; delay_ = loop_delay;
@@ -609,13 +609,13 @@ bool PIThread::start(PISystemTime loop_delay) {
bool PIThread::start(ThreadFunc func) { bool PIThread::start(ThreadFunc func) {
ret_func = func; ret_func = std::move(func);
return start(); return start();
} }
bool PIThread::start(ThreadFunc func, PISystemTime loop_delay) { bool PIThread::start(ThreadFunc func, PISystemTime loop_delay) {
ret_func = func; ret_func = std::move(func);
delay_ = loop_delay; delay_ = loop_delay;
return start(); return start();
} }
@@ -641,7 +641,7 @@ bool PIThread::startOnce() {
bool PIThread::startOnce(ThreadFunc func) { bool PIThread::startOnce(ThreadFunc func) {
ret_func = func; ret_func = std::move(func);
return startOnce(); return startOnce();
} }
@@ -738,12 +738,12 @@ bool PIThread::_startThread(void * func) {
#ifdef FREERTOS #ifdef FREERTOS
auto name_ba = createThreadName(); auto name_ba = createThreadName();
if (xTaskCreate((__THREAD_FUNC_RET__ (*)(void *))func, if (xTaskCreate((__THREAD_FUNC_RET__(*)(void *))func,
(const char *)name_ba.data(), // A name just for humans (const char *)name_ba.data(), // A name just for humans
128, // This stack size can be checked & adjusted by reading the Stack Highwater 128, // This stack size can be checked & adjusted by reading the Stack Highwater
this, this,
priority_, priority_,
&PRIVATE->thread) == pdPASS) { &PRIVATE->thread) == pdPASS) {
tid_ = (llong)PRIVATE->thread; tid_ = (llong)PRIVATE->thread;
return true; return true;
} }
@@ -752,7 +752,7 @@ bool PIThread::_startThread(void * func) {
if (PRIVATE->thread) CloseHandle(PRIVATE->thread); if (PRIVATE->thread) CloseHandle(PRIVATE->thread);
# ifdef CC_GCC # 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 # else
PRIVATE->thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)func, this, CREATE_SUSPENDED, 0); PRIVATE->thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)func, this, CREATE_SUSPENDED, 0);
# endif # endif
@@ -766,7 +766,7 @@ bool PIThread::_startThread(void * func) {
pthread_attr_t attr; pthread_attr_t attr;
pthread_attr_init(&attr); pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 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); pthread_attr_destroy(&attr);
// PICout(PICoutManipulators::DefaultControls) << "pthread_create" << PRIVATE->thread; // PICout(PICoutManipulators::DefaultControls) << "pthread_create" << PRIVATE->thread;
// piCout << "started" << PRIVATE->thread; // piCout << "started" << PRIVATE->thread;
@@ -884,8 +884,8 @@ void PIThread::_runThread() {
PIINTROSPECTION_THREAD_RUN(this); PIINTROSPECTION_THREAD_RUN(this);
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "lock" << "..."; // PICout(PICoutManipulators::DefaultControls) << "thread" << this << "lock" << "...";
if (lockRun) thread_mutex.lock(); if (lockRun) thread_mutex.lock();
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "lock" << "ok"; // PICout(PICoutManipulators::DefaultControls) << "thread" << this << "lock" << "ok";
// PICout(PICoutManipulators::DefaultControls) << "thread" << this << "run" << "..."; // PICout(PICoutManipulators::DefaultControls) << "thread" << this << "run" << "...";
#ifdef PIP_INTROSPECTION #ifdef PIP_INTROSPECTION
PITimeMeasurer _tm; PITimeMeasurer _tm;
#endif #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) { void PIThread::runOnce(std::function<void()> func, const PIString & name) {
PIThread * t = new PIThread(); PIThread * t = new PIThread();
t->setName(name); t->setName(name);
t->setSlot(func); t->setSlot(std::move(func));
#ifndef MICRO_PIP #ifndef MICRO_PIP
__PIThreadCollection::instance()->startedAuto(t); __PIThreadCollection::instance()->startedAuto(t);
CONNECT0(void, t, stopped, __PIThreadCollection::instance(), stoppedAuto); 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. //! \~english Waits until the thread starts. Returns \b false if the timeout expires first.
//! \~russian Ожидает запуска потока. Возвращает \b false, если таймаут истек раньше. //! \~russian Ожидает запуска потока. Возвращает \b false, если таймаут истек раньше.
bool waitForStart(PISystemTime timeout = {}); bool waitForStart(PISystemTime timeout = {});
//! \~english Deprecated overload of \a waitForStart() that accepts milliseconds. //! \~english Deprecated overload of \a waitForStart() that accepts milliseconds.
//! \~russian Устаревшая перегрузка \a waitForStart(), принимающая миллисекунды. //! \~russian Устаревшая перегрузка \a waitForStart(), принимающая миллисекунды.
bool waitForStart(int timeout_msecs) DEPRECATEDM("use waitForStart(PISystemTime)") { 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. //! \~english Waits for thread completion. Returns \b false if the timeout expires first.
//! \~russian Ожидает завершения потока. Возвращает \b false, если таймаут истек раньше. //! \~russian Ожидает завершения потока. Возвращает \b false, если таймаут истек раньше.
bool waitForFinish(PISystemTime timeout = {}); bool waitForFinish(PISystemTime timeout = {});
//! \~english Deprecated overload of \a waitForFinish() that accepts milliseconds. //! \~english Deprecated overload of \a waitForFinish() that accepts milliseconds.
//! \~russian Устаревшая перегрузка \a waitForFinish(), принимающая миллисекунды. //! \~russian Устаревшая перегрузка \a waitForFinish(), принимающая миллисекунды.
bool waitForFinish(int timeout_msecs) DEPRECATEDM("use waitForFinish(PISystemTime)") { bool waitForFinish(int timeout_msecs) DEPRECATEDM("use waitForFinish(PISystemTime)") {

View File

@@ -71,8 +71,8 @@
#include "pispinlock.h" #include "pispinlock.h"
#include "pithread.h" #include "pithread.h"
#include "pithreadnotifier.h" #include "pithreadnotifier.h"
#include "pithreadpoolexecutor.h"
#include "pithreadpoolloop.h" #include "pithreadpoolloop.h"
#include "pithreadpoolworker.h"
#include "pitimer.h" #include "pitimer.h"
#endif // PITHREADMODULE_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 #ifndef PITHREADPOOLEXECUTOR_H
#define PITHREADPOOLEXECUTOR_H #define PITHREADPOOLEXECUTOR_H
#include "piblockingqueue.h" #include "pithreadpoolworker.h"
#include "pithread.h"
#include <atomic> namespace {
DEPRECATEDM("Use pithreadpoolworker.h") const bool deprecated_header_is_used = true;
}
class DEPRECATEDM("Use PIThreadPoolWorker") PIThreadPoolExecutor: public PIThreadPoolWorker {
//! \~\ingroup Thread
//! \~\brief
//! \~english Fixed-size pool of worker threads for fire-and-forget tasks.
//! \~russian Фиксированный пул рабочих потоков для задач без ожидания результата.
class PIP_EXPORT PIThreadPoolExecutor {
public: public:
//! \~english Constructs executor with \a corePoolSize worker threads. PIThreadPoolExecutor(int threads_count): PIThreadPoolWorker(threads_count) { start(); }
//! \~russian Создает исполнитель с \a corePoolSize рабочими потоками. ~PIThreadPoolExecutor() { stopAndWait(); }
explicit PIThreadPoolExecutor(int corePoolSize);
//! \~english Stops worker threads and destroys executor resources. void execute(std::function<void()> runnable) DEPRECATEDM("Use enqueueTask()") { enqueueTask(runnable); }
//! \~russian Останавливает рабочие потоки и уничтожает ресурсы исполнителя. void shutdownNow() DEPRECATEDM("Use stopAndWait()") { stopAndWait(); }
virtual ~PIThreadPoolExecutor(); void shutdown() DEPRECATEDM("Use stop()") { stop(); }
bool isShutdown() const DEPRECATEDM("Use !isRunning()") { return !isRunning(); }
//! \~\brief bool awaitTermination(PISystemTime timeout) DEPRECATEDM("Use waitForFinish()") { return waitForFinish(timeout); }
//! \~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;
}; };
#endif // PITHREADPOOLEXECUTOR_H
#endif

View File

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

View File

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

View File

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

View File

@@ -253,7 +253,7 @@ public:
//! \~english Sleep for this time //! \~english Sleep for this time
//! \~russian Ожидать это время //! \~russian Ожидать это время
void sleep(); void sleep() const;
//! \~english On *nix system assign current value to timespec struct //! \~english On *nix system assign current value to timespec struct
//! \~russian На *nix системах присваивает время к timespec структуре //! \~russian На *nix системах присваивает время к timespec структуре
@@ -261,7 +261,7 @@ public:
//! \~english Returns \a Frequency that corresponds this time interval //! \~english Returns \a Frequency that corresponds this time interval
//! \~russian Возвращает \a Frequency соответствующую этому временному интервалу //! \~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 //! \~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> ..." для относительного //! \~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) { void PIValueTree::forEachRecursive(std::function<void(const PIValueTree &, const PIString &)> func) {
forEachRecursiveInternal(func); forEachRecursiveInternal(std::move(func));
} }

168
main.cpp
View File

@@ -1,143 +1,53 @@
#include "libs/http_client/curl_thread_pool_p.h"
#include "picodeparser.h"
#include "pidigest.h"
#include "pihttpclient.h"
#include "piliterals.h"
#include "pip.h" #include "pip.h"
#include "piunits.h" #include "pithreadpoolexecutor.h"
#include "pivaluetree_conversions.h"
using namespace PICoutManipulators; class A: public PIObject {
using namespace PIHTTP; PIOBJECT(A)
using namespace PIUnits::Class;
int rcnt = 0, scnt = 0;
inline PIByteArray SMBusTypeInfo_genHash(PIString n) {
PICrypt c;
return piSerialize(c.shorthash(n.removeAll(" "), PIString("SMBusDataHashKey").toByteArray()));
}
public:
A(PIString n = {}) { setName(n); }
void foo() {
100_ms .sleep();
piCoutObj << "foo!";
100_ms .sleep();
}
};
int main(int argc, char * argv[]) { int main(int argc, char * argv[]) {
PICrypt _crypt; PIVector<A *> objects;
// auto ba = PIFile::readAll("logo.png"); objects << new A("1") << new A("2") << new A("3");
PIString str = "hello!"_a;
PIByteArray ba = str.toAscii();
PIByteArray key = PIString("SMBusDataHashKey").toByteArray();
const int times = 1000000; PITimer status_timer;
PITimeMeasurer tm; PIThreadPoolWorker pool(2);
PISystemTime el; pool.start();
tm.reset(); // int64_t id = -1;
piForTimes(times) { // status_timer.start(10_Hz, [&id, &pool] { piCout << "[timer] status" << (int)pool.taskStatus(id); });
PIDigest::calculateWithKey(ba, key, PIDigest::Type::SipHash_2_4_128);
}
el = tm.elapsed();
piCout << "PIDigest" << el.toString();
tm.reset(); 100_ms .sleep();
piForTimes(times) { // pool.enqueueTask([](int64_t id) {
_crypt.shorthash(str, key); // piCout << "[task ] start, id" << id;
} // // 500_ms .sleep();
el = tm.elapsed(); // piCout << "[task ] done";
piCout << " sodium" << el.toString(); // });
pool.enqueueTask(objects[0], &A::foo);
pool.enqueueTask(objects[1], &A::foo);
pool.enqueueTask(objects[1], &A::foo);
pool.enqueueTask(objects[2], &A::foo);
tm.reset(); 10_ms .sleep();
piForTimes(times) { delete objects[1];
PIDigest::calculateWithKey(ba, key, PIDigest::Type::BLAKE2b_128); objects.remove(1);
} // piCout << "[main ]" << "enqueued, id" << id;
el = tm.elapsed();
piCout << " blake" << el.toString();
return 0; // 200_ms .sleep();
piCout << "[main ]" << "wait ...";
piCout << "[main ]" << "wait done";
1000_ms .sleep();
PIEthernet *eth_r, *eth_s; pool.stopAndWait();
eth_r = PIIODevice::createFromFullPath("eth://udp: 192.168.1.25 :10000")->cast<PIEthernet>(); status_timer.stopAndWait();
eth_s = PIIODevice::createFromFullPath("eth://udp: : : 192.168.1.25:10000")->cast<PIEthernet>(); piDeleteAll(objects);
eth_r->setReadBufferSize(1_MiB);
CONNECTL(eth_r, threadedReadEvent, [](const uchar * readed, ssize_t size) {
// piCout << "rec";
piMSleep(1);
++rcnt;
});
eth_r->startThreadedRead();
PIByteArray _ba(1400);
for (int i = 0; i < 100; ++i) {
eth_s->write(_ba);
++scnt;
}
0.2_s .sleep();
piCout << "snd" << scnt;
piCout << "rec" << rcnt;
piDeleteSafety(eth_r);
piDeleteSafety(eth_s);
return 0;
PITranslator::loadLang("ru");
/*auto ucl = PIUnits::allClasses();
for (auto c: ucl) {
piCout << (c->className() + ":");
for (auto t: c->allTypes()) {
piCout << " " << c->name(t) << "->" << c->unit(t);
}
}*/
// PIUnits::Value(1);
// piCout << PIUnits::name(PIUnits::Class::Information::Bit);
// piCout << PIUnits::name(PIUnits::Class::Information::Byte);
// piCout << PIUnits::name(PIUnits::Class::Information::_LastType);
// piCout << PIUnits::name((int)PIUnits::Class::Angle::Degree);
// piCout << PIUnits::unit(PIUnits::Class::Information::Bit);
// piCout << PIUnits::unit(PIUnits::Class::Information::Byte);
// piCout << PIUnits::unit(PIUnits::Class::Information::_LastType);
// piCout << PIUnits::unit((int)PIUnits::Class::Angle::Degree);
// for (int i = -10; i < 10; ++i)
// piCout << PIUnits::Value(pow10(i * 0.99), PIUnits::Class::Distance::Meter).toString();
auto v = PIUnits::Value(M_PI, Angle::Radian);
piCout << v << "=" << v.converted(Angle::Degree);
v = PIUnits::Value(45, Angle::Degree);
piCout << v << "=" << v.converted(Angle::Radian);
piCout << PIUnits::Value(5E-5, Time::Second);
piCout << PIUnits::Value(3E-3, Time::Second);
piCout << PIUnits::Value(0.8, Time::Second);
piCout << PIUnits::Value(1.2, Time::Second);
piCout << PIUnits::Value(1001, Time::Second);
piCout << PIUnits::Value(1000001, Time::Second);
piCout << PIUnits::Value(1_KB, Information::Byte);
piCout << PIUnits::Value(1_MB, Information::Byte);
piCout << PIUnits::Value(1_MiB, Information::Byte);
piCout << PIUnits::Value(1_MB, Information::Byte).converted(Information::Bit);
piCout << PIUnits::Value(1_MiB, Information::Byte).converted(Information::Bit);
piCout << PIUnits::Value(0., Temperature::Celsius).converted(Temperature::Kelvin);
piCout << PIUnits::Value(0., Temperature::Celsius).converted(Temperature::Fahrenheit);
piCout << PIUnits::Value(100., Temperature::Celsius).converted(Temperature::Fahrenheit);
piCout << PIUnits::Value(1., Pressure::Atmosphere).converted(Pressure::Pascal);
piCout << PIUnits::Value(1., Pressure::Atmosphere).converted(Pressure::MillimetreOfMercury);
piCout << PIUnits::Value(766., Pressure::MillimetreOfMercury).converted(Pressure::Atmosphere);
piCout << PIUnits::Value(5E-5, Time::Second).converted(Time::Hertz);
piCout << PIUnits::Value(3E-3, Time::Second).converted(Time::Hertz);
piCout << PIUnits::Value(0.8, Time::Second).converted(Time::Hertz);
piCout << PIUnits::Value(1.2, Time::Second).converted(Time::Hertz);
piCout << PIUnits::Value(1001, Time::Second).converted(Time::Hertz);
piCout << PIUnits::Value(1000001, Time::Second).converted(Time::Hertz);
// piCout << PIUnits::Value(0.2, Time::Second).converted(Time::Hertz);
// piCout << PIUnits::Value(5E-5, Time::Second).converted(Time::Hertz);
return 0; return 0;
} }

View File

@@ -13,6 +13,7 @@ FetchContent_Declare(
) )
# For Windows: Prevent overriding the parent project's compiler/linker settings # For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
set(BUILD_GMOCK OFF CACHE BOOL "Build Google Mock" FORCE)
FetchContent_MakeAvailable(googletest) FetchContent_MakeAvailable(googletest)
enable_testing() enable_testing()
@@ -24,13 +25,14 @@ macro(pip_test NAME)
file(GLOB _HDRS "${NAME}/*.h") file(GLOB _HDRS "${NAME}/*.h")
set(_target pip_${NAME}_test) set(_target pip_${NAME}_test)
add_executable(${_target} ${_CPPS} ${_HDRS}) add_executable(${_target} ${_CPPS} ${_HDRS})
target_link_libraries(${_target} pip ${ARGN} gtest_main) target_link_libraries(${_target} pip ${ARGN} gtest_main)
gtest_discover_tests(${_target}) if (TESTS_RUN)
gtest_discover_tests(${_target})
endif()
list(APPEND PIP_TESTS_LIST "${NAME}") list(APPEND PIP_TESTS_LIST "${NAME}")
set(PIP_TESTS_LIST ${PIP_TESTS_LIST} PARENT_SCOPE) set(PIP_TESTS_LIST ${PIP_TESTS_LIST} PARENT_SCOPE)
endmacro() endmacro()
#pip_test(concurrent)
pip_test(math) pip_test(math)
pip_test(core) pip_test(core)
pip_test(piobject) pip_test(piobject)

View File

@@ -32,7 +32,8 @@ Client * createAndConnectClient() {
TEST(ClientServer, OneClient) { TEST(ClientServer, OneClient) {
auto const loop_timeout = 1000_ms; auto const loop_timeout = 1000_ms;
auto s = createServer<false, true>(); auto s = createServer<false, true>();
auto c = createAndConnectClient<TestClient<false, false>>(); piMinSleep();
auto c = createAndConnectClient<TestClient<false, false>>();
waitLoop([s]() { return s->clientsCount() > 0; }, loop_timeout); waitLoop([s]() { return s->clientsCount() > 0; }, loop_timeout);
EXPECT_EQ(1, s->clientsCount()); EXPECT_EQ(1, s->clientsCount());
@@ -102,10 +103,11 @@ int getClientsPings(const PIVector<ClientSendThread *> & clients) {
TEST(ClientServer, ManyClients) { TEST(ClientServer, ManyClients) {
auto const loop_timeout = 100_ms; auto const loop_timeout = 1_s;
constexpr int clients_count = 20; constexpr int clients_count = 20;
PIVector<ClientSendThread *> clients; PIVector<ClientSendThread *> clients;
auto s = createServer<false, false, 100_KiB>(); auto s = createServer<false, false, 100_KiB>();
piMinSleep();
piForTimes(clients_count) { piForTimes(clients_count) {
clients.append(new ClientSendThread()); clients.append(new ClientSendThread());
@@ -137,8 +139,9 @@ TEST(ClientServer, ManyClients) {
for (const auto c: clients) { for (const auto c: clients) {
c->startSend(); c->startSend();
} }
(100_ms).sleep(); waitLoop([&clients]() { return getClientsPings(clients) > clients_count * 2; }, loop_timeout);
EXPECT_TRUE(getClientsPings(clients) > clients_count * 2); EXPECT_TRUE(getClientsPings(clients) > clients_count * 2);
waitLoop([s]() { return getServerPongs(s) > clients_count * 2; }, loop_timeout);
EXPECT_TRUE(getServerPongs(s) > clients_count * 2); EXPECT_TRUE(getServerPongs(s) > clients_count * 2);
piDeleteAllAndClear(clients); piDeleteAllAndClear(clients);
waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout); waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout);
@@ -147,7 +150,7 @@ TEST(ClientServer, ManyClients) {
} }
TEST(ClientServer, DynamicClients) { TEST(ClientServer, DynamicClients) {
auto const loop_timeout = 100_ms; auto const loop_timeout = 3_s;
constexpr int clients_count = 20; constexpr int clients_count = 20;
PIVector<ClientSendThread *> clients; PIVector<ClientSendThread *> clients;
PIMutex clients_mutex; PIMutex clients_mutex;
@@ -160,7 +163,6 @@ TEST(ClientServer, DynamicClients) {
clients_mutex.lock(); clients_mutex.lock();
clients << c; clients << c;
clients_mutex.unlock(); clients_mutex.unlock();
piCout << "new client" << clients.size();
}; };
piForTimes(clients_count) { piForTimes(clients_count) {
@@ -178,9 +180,8 @@ TEST(ClientServer, DynamicClients) {
piForTimes(new_cnt) { piForTimes(new_cnt) {
spawnClient(); spawnClient();
} }
piCout << "+++++++";
}, },
12_Hz); 120_Hz);
deleteThread.start( deleteThread.start(
[&clients, &clients_mutex]() { [&clients, &clients_mutex]() {
@@ -194,26 +195,17 @@ TEST(ClientServer, DynamicClients) {
clients_mutex.unlock(); clients_mutex.unlock();
if (c) { if (c) {
delete c; delete c;
piCout << "remove client" << clients.size();
} }
} }
piCout << "----------";
}, },
13_Hz); 130_Hz);
(2_s).sleep();
waitLoop([s]() { return s->clientsCount() >= 10; }, loop_timeout);
EXPECT_GE(s->clientsCount(), 10); EXPECT_GE(s->clientsCount(), 10);
piCout << "now clients" << clients.size();
deleteThread.stopAndWait(); deleteThread.stopAndWait();
spawnThread.stopAndWait(); spawnThread.stopAndWait();
piCout << "total clients" << clients.size();
piDeleteAllAndClear(clients); piDeleteAllAndClear(clients);
waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout); waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout);
EXPECT_EQ(0, s->clientsCount()); EXPECT_EQ(0, s->clientsCount());

View File

@@ -1,262 +0,0 @@
#include "piblockingqueue.h"
#include "gtest/gtest.h"
class MockConditionVar: public PIConditionVariable {
public:
bool isWaitCalled = false;
bool isWaitForCalled = false;
bool isTrueCondition = false;
int timeout = -1;
void wait(PIMutex & lk) override { isWaitCalled = true; }
void wait(PIMutex & lk, const std::function<bool()> & condition) override {
isWaitCalled = true;
isTrueCondition = condition();
}
bool waitFor(PIMutex & lk, int timeoutMs) override {
isWaitForCalled = true;
timeout = timeoutMs;
return false;
}
bool waitFor(PIMutex & lk, int timeoutMs, const std::function<bool()> & condition) override {
isWaitForCalled = true;
isTrueCondition = condition();
timeout = timeoutMs;
return isTrueCondition;
}
};
TEST(BlockingDequeueUnitTest, put_is_block_when_capacity_reach) {
size_t capacity = 0;
auto conditionVarAdd = new MockConditionVar();
auto conditionVarRem = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
dequeue.put(11);
ASSERT_TRUE(conditionVarRem->isWaitCalled);
ASSERT_FALSE(conditionVarRem->isTrueCondition);
}
TEST(BlockingDequeueUnitTest, offer_timedout_is_false_when_capacity_reach) {
size_t capacity = 0;
int timeout = 11;
auto conditionVarAdd = new MockConditionVar();
auto conditionVarRem = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
ASSERT_FALSE(dequeue.offer(11, timeout));
}
TEST(BlockingDequeueUnitTest, offer_timedout_is_block_when_capacity_reach) {
size_t capacity = 0;
int timeout = 11;
auto conditionVarAdd = new MockConditionVar();
auto conditionVarRem = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
dequeue.offer(11, timeout);
EXPECT_TRUE(conditionVarRem->isWaitForCalled);
EXPECT_EQ(timeout, conditionVarRem->timeout);
ASSERT_FALSE(conditionVarRem->isTrueCondition);
}
TEST(BlockingDequeueUnitTest, offer_is_true_before_capacity_reach) {
size_t capacity = 1;
PIBlockingQueue<int> dequeue(capacity);
ASSERT_TRUE(dequeue.offer(10));
}
TEST(BlockingDequeueUnitTest, offer_is_false_when_capacity_reach) {
size_t capacity = 1;
PIBlockingQueue<int> dequeue(capacity);
dequeue.offer(11);
ASSERT_FALSE(dequeue.offer(10));
}
// TODO change take_is_block_when_empty to prevent segfault
TEST(DISABLED_BlockingDequeueUnitTest, take_is_block_when_empty) {
size_t capacity = 1;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
// May cause segfault because take front of empty queue
dequeue.take();
EXPECT_TRUE(conditionVar->isWaitCalled);
ASSERT_FALSE(conditionVar->isTrueCondition);
}
TEST(BlockingDequeueUnitTest, take_is_not_block_when_not_empty) {
size_t capacity = 1;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.offer(111);
dequeue.take();
EXPECT_TRUE(conditionVar->isWaitCalled);
ASSERT_TRUE(conditionVar->isTrueCondition);
}
TEST(BlockingDequeueUnitTest, take_is_value_eq_to_offer_value) {
size_t capacity = 1;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.offer(111);
ASSERT_EQ(dequeue.take(), 111);
}
TEST(BlockingDequeueUnitTest, take_is_last) {
size_t capacity = 10;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
EXPECT_TRUE(dequeue.offer(111));
EXPECT_TRUE(dequeue.offer(222));
ASSERT_EQ(dequeue.take(), 111);
ASSERT_EQ(dequeue.take(), 222);
}
TEST(BlockingDequeueUnitTest, poll_is_not_block_when_empty) {
size_t capacity = 1;
bool isOk;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.poll(0, 111, &isOk);
EXPECT_FALSE(conditionVar->isWaitForCalled);
}
TEST(BlockingDequeueUnitTest, poll_is_default_value_when_empty) {
size_t capacity = 1;
bool isOk;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
ASSERT_EQ(dequeue.poll(0, 111, &isOk), 111);
}
TEST(BlockingDequeueUnitTest, poll_is_offer_value_when_not_empty) {
size_t capacity = 1;
bool isOk;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.offer(111);
ASSERT_EQ(dequeue.poll(0, -1, &isOk), 111);
}
TEST(BlockingDequeueUnitTest, poll_timeouted_is_block_when_empty) {
size_t capacity = 1;
int timeout = 11;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.poll(timeout, 111);
EXPECT_TRUE(conditionVar->isWaitForCalled);
EXPECT_EQ(timeout, conditionVar->timeout);
ASSERT_FALSE(conditionVar->isTrueCondition);
}
TEST(BlockingDequeueUnitTest, poll_timeouted_is_default_value_when_empty) {
size_t capacity = 1;
int timeout = 11;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
ASSERT_EQ(dequeue.poll(timeout, 111), 111);
}
TEST(BlockingDequeueUnitTest, poll_timeouted_is_not_block_when_not_empty) {
size_t capacity = 1;
int timeout = 11;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.offer(111);
dequeue.poll(timeout, -1);
EXPECT_TRUE(conditionVar->isWaitForCalled);
ASSERT_TRUE(conditionVar->isTrueCondition);
}
TEST(BlockingDequeueUnitTest, poll_timeouted_is_offer_value_when_not_empty) {
size_t capacity = 1;
int timeout = 11;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.offer(111);
ASSERT_EQ(dequeue.poll(timeout, -1), 111);
}
TEST(BlockingDequeueUnitTest, poll_timeouted_is_last) {
size_t capacity = 10;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.offer(111);
dequeue.offer(222);
ASSERT_EQ(dequeue.poll(10, -1), 111);
ASSERT_EQ(dequeue.poll(10, -1), 222);
}
TEST(BlockingDequeueUnitTest, capacity_is_eq_constructor_capacity) {
size_t capacity = 10;
PIBlockingQueue<int> dequeue(capacity);
ASSERT_EQ(dequeue.capacity(), capacity);
}
TEST(BlockingDequeueUnitTest, remainingCapacity_is_dif_of_capacity_and_size) {
size_t capacity = 2;
PIBlockingQueue<int> dequeue(capacity);
ASSERT_EQ(dequeue.remainingCapacity(), capacity);
dequeue.offer(111);
ASSERT_EQ(dequeue.remainingCapacity(), capacity - 1);
}
TEST(BlockingDequeueUnitTest, remainingCapacity_is_zero_when_capacity_reach) {
size_t capacity = 1;
PIBlockingQueue<int> dequeue(capacity);
dequeue.offer(111);
dequeue.offer(111);
ASSERT_EQ(dequeue.remainingCapacity(), 0);
}
TEST(BlockingDequeueUnitTest, size_is_eq_to_num_of_elements) {
size_t capacity = 1;
PIBlockingQueue<int> dequeue(capacity);
ASSERT_EQ(dequeue.size(), 0);
dequeue.offer(111);
ASSERT_EQ(dequeue.size(), 1);
}
TEST(BlockingDequeueUnitTest, size_is_eq_to_capacity_when_capacity_reach) {
size_t capacity = 1;
PIBlockingQueue<int> dequeue(capacity);
dequeue.offer(111);
dequeue.offer(111);
ASSERT_EQ(dequeue.size(), capacity);
}
TEST(BlockingDequeueUnitTest, drainTo_is_elements_moved) {
size_t capacity = 10;
PIDeque<int> refDeque;
for (size_t i = 0; i < capacity / 2; ++i)
refDeque.push_back(i * 10);
PIBlockingQueue<int> blockingDequeue(refDeque);
PIDeque<int> deque;
blockingDequeue.drainTo(deque);
ASSERT_EQ(blockingDequeue.size(), 0);
ASSERT_TRUE(deque == refDeque);
}
TEST(BlockingDequeueUnitTest, drainTo_is_ret_eq_to_size_when_all_moved) {
size_t capacity = 10;
PIDeque<int> refDeque;
for (size_t i = 0; i < capacity / 2; ++i)
refDeque.push_back(i * 10);
PIBlockingQueue<int> blockingDequeue(refDeque);
PIDeque<int> deque;
ASSERT_EQ(blockingDequeue.drainTo(deque), refDeque.size());
}
TEST(BlockingDequeueUnitTest, drainTo_is_ret_eq_to_maxCount) {
size_t capacity = 10;
PIDeque<int> refDeque;
for (size_t i = 0; i < capacity / 2; ++i)
refDeque.push_back(i * 10);
PIBlockingQueue<int> blockingDequeue(refDeque);
PIDeque<int> deque;
ASSERT_EQ(blockingDequeue.drainTo(deque, refDeque.size() - 1), refDeque.size() - 1);
}

View File

@@ -1,57 +0,0 @@
#include "piconditionvar.h"
#include "pithread.h"
#include "testutil.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
class ConditionLock
: public ::testing::Test
, public TestUtil {
public:
PIMutex * m = new PIMutex();
bool isProtect;
bool isReleased;
};
TEST_F(ConditionLock, DISABLED_lock_is_protect) {
m->lock();
isProtect = true;
createThread([&]() {
m->lock();
isProtect = false;
});
EXPECT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
ASSERT_TRUE(isProtect);
}
TEST_F(ConditionLock, DISABLED_unlock_is_release) {
m->lock();
isReleased = false;
m->unlock();
createThread([&]() {
m->lock();
isReleased = true;
m->unlock();
});
ASSERT_TRUE(isReleased);
}
TEST_F(ConditionLock, tryLock_is_false_when_locked) {
createThread([&]() {
m->lock();
piMSleep(WAIT_THREAD_TIME_MS);
});
ASSERT_FALSE(m->tryLock());
}
TEST_F(ConditionLock, tryLock_is_true_when_unlocked) {
ASSERT_TRUE(m->tryLock());
}
TEST_F(ConditionLock, tryLock_is_recursive_lock_enable) {
m->lock();
ASSERT_TRUE(m->tryLock());
}

View File

@@ -1,209 +0,0 @@
#include "piconditionvar.h"
#include "pithread.h"
#include "testutil.h"
#include "gtest/gtest.h"
class ConditionVariable
: public ::testing::Test
, public TestUtil {
public:
~ConditionVariable() { delete variable; }
PIMutex m;
PIConditionVariable * variable;
protected:
void SetUp() override {
variable = new PIConditionVariable();
adapterFunctionDefault = [&]() {
m.lock();
variable->wait(m);
m.unlock();
};
}
};
TEST_F(ConditionVariable, wait_is_block) {
createThread();
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
}
TEST_F(ConditionVariable, wait_is_block_when_notifyOne_before_wait) {
variable->notifyOne();
createThread();
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
}
TEST_F(ConditionVariable, wait_is_block_when_notifyAll_before_wait) {
variable->notifyAll();
createThread();
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
}
TEST_F(ConditionVariable, wait_is_unblock_when_notifyOne_after_wait) {
createThread();
variable->notifyOne();
ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
}
TEST_F(ConditionVariable, wait_is_unblock_when_notifyAll_after_wait) {
PIVector<PIThread *> threads;
for (int i = 0; i < THREAD_COUNT; ++i) {
threads.push_back(new PIThread([=]() { adapterFunctionDefault(); }));
}
piForeach(PIThread * thread, threads)
thread->startOnce();
piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT);
variable->notifyAll();
PITimeMeasurer measurer;
piForeach(PIThread * thread, threads) {
int timeout = WAIT_THREAD_TIME_MS * THREAD_COUNT - (int)measurer.elapsed_m();
thread->waitForFinish(timeout > 0 ? timeout : 0);
}
for (size_t i = 0; i < threads.size(); ++i)
EXPECT_FALSE(threads[i]->isRunning()) << "Thread " << i << " still running";
piForeach(PIThread * thread, threads)
delete thread;
}
TEST_F(ConditionVariable, wait_is_one_unblock_when_notifyOne) {
PIVector<PIThread *> threads;
for (int i = 0; i < THREAD_COUNT; ++i) {
threads.push_back(new PIThread(adapterFunctionDefault));
}
piForeach(PIThread * thread, threads)
thread->startOnce();
piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT);
variable->notifyOne();
piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT);
int runningThreadCount = 0;
piForeach(PIThread * thread, threads)
if (thread->isRunning()) runningThreadCount++;
ASSERT_EQ(runningThreadCount, THREAD_COUNT - 1);
}
TEST_F(ConditionVariable, wait_is_protected_unblock_when_notifyOne) {
createThread([&]() {
m.lock();
variable->wait(m);
piMSleep(2 * WAIT_THREAD_TIME_MS);
// Missing unlock
});
variable->notifyOne();
piMSleep(WAIT_THREAD_TIME_MS);
ASSERT_FALSE(m.tryLock());
}
TEST_F(ConditionVariable, wait_condition_is_block) {
createThread([&]() {
m.lock();
variable->wait(m, []() { return false; });
m.unlock();
});
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
}
TEST_F(ConditionVariable, wait_condition_is_check_condition_before_block) {
bool isConditionChecked = false;
createThread([&]() {
m.lock();
variable->wait(m, [&]() {
isConditionChecked = true;
return false;
});
m.unlock();
});
m.lock();
ASSERT_TRUE(isConditionChecked);
m.unlock();
}
TEST_F(ConditionVariable, wait_condition_is_check_condition_when_notifyOne) {
bool isConditionChecked;
createThread([&]() {
m.lock();
variable->wait(m, [&]() {
isConditionChecked = true;
return false;
});
m.unlock();
});
m.lock();
isConditionChecked = false;
m.unlock();
variable->notifyOne();
piMSleep(threadStartTime + 1);
m.lock();
ASSERT_TRUE(isConditionChecked);
m.unlock();
}
TEST_F(ConditionVariable, wait_condition_is_unblock_when_condition_and_notifyOne) {
bool condition = false;
createThread([&]() {
m.lock();
variable->wait(m, [&]() { return condition; });
m.unlock();
});
m.lock();
condition = true;
m.unlock();
variable->notifyOne();
ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
}
TEST_F(ConditionVariable, DISABLED_waitFor_is_block_before_timeout) {
createThread([&]() {
PITimeMeasurer measurer;
m.lock();
variable->waitFor(m, WAIT_THREAD_TIME_MS * 2);
m.unlock();
// Not reliable because spurious wakeup may happen
ASSERT_GE(measurer.elapsed_m(), WAIT_THREAD_TIME_MS);
});
EXPECT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS * 3));
}
TEST_F(ConditionVariable, waitFor_is_unblock_when_timeout) {
std::atomic_bool isUnblock(false);
createThread([&]() {
m.lock();
variable->waitFor(m, WAIT_THREAD_TIME_MS);
isUnblock = true;
m.unlock();
});
// Test failed if suspend forever
EXPECT_TRUE(thread->waitForFinish(2 * WAIT_THREAD_TIME_MS));
ASSERT_TRUE(isUnblock);
}
TEST_F(ConditionVariable, waitFor_is_false_when_timeout) {
bool waitRet = true;
createThread([&]() {
m.lock();
waitRet = variable->waitFor(m, WAIT_THREAD_TIME_MS);
m.unlock();
});
EXPECT_TRUE(thread->waitForFinish(2 * WAIT_THREAD_TIME_MS));
ASSERT_FALSE(waitRet);
}
TEST_F(ConditionVariable, waitFor_is_unblock_when_condition_and_notifyOne) {
bool condition = false;
createThread([&]() {
m.lock();
variable->waitFor(m, 3 * WAIT_THREAD_TIME_MS, [&]() { return condition; });
m.unlock();
});
EXPECT_TRUE(thread->isRunning());
m.lock();
condition = true;
m.unlock();
variable->notifyOne();
piMSleep(WAIT_THREAD_TIME_MS);
ASSERT_FALSE(thread->isRunning());
}

View File

@@ -1,51 +0,0 @@
#include "pimutex.h"
#include "pithreadpoolexecutor.h"
#include "gtest/gtest.h"
const int WAIT_THREAD_TIME_MS = 30;
TEST(ExcutorIntegrationTest, execute_is_runnable_invoke) {
PIMutex m;
int invokedRunnables = 0;
PIThreadPoolExecutor executorService(1);
executorService.execute([&]() {
m.lock();
invokedRunnables++;
m.unlock();
});
piMSleep(WAIT_THREAD_TIME_MS);
ASSERT_EQ(invokedRunnables, 1);
}
TEST(ExcutorIntegrationTest, execute_is_not_execute_after_shutdown) {
bool isRunnableInvoke = false;
PIThreadPoolExecutor executorService(1);
executorService.shutdown();
executorService.execute([&]() { isRunnableInvoke = true; });
piMSleep(WAIT_THREAD_TIME_MS);
ASSERT_FALSE(isRunnableInvoke);
}
TEST(ExcutorIntegrationTest, execute_is_execute_before_shutdown) {
bool isRunnableInvoke = false;
PIThreadPoolExecutor executorService(1);
executorService.execute([&]() {
piMSleep(WAIT_THREAD_TIME_MS);
isRunnableInvoke = true;
});
executorService.shutdown();
piMSleep(2 * WAIT_THREAD_TIME_MS);
ASSERT_TRUE(isRunnableInvoke);
}
TEST(ExcutorIntegrationTest, execute_is_awaitTermination_wait) {
PIThreadPoolExecutor executorService(1);
executorService.execute([&]() { piMSleep(2 * WAIT_THREAD_TIME_MS); });
executorService.shutdown();
PITimeMeasurer measurer;
ASSERT_TRUE(executorService.awaitTermination(3 * WAIT_THREAD_TIME_MS));
double waitTime = measurer.elapsed_m();
ASSERT_GE(waitTime, WAIT_THREAD_TIME_MS);
ASSERT_LE(waitTime, 4 * WAIT_THREAD_TIME_MS);
}

View File

@@ -1,57 +0,0 @@
#include "pithreadnotifier.h"
#include "gtest/gtest.h"
TEST(PIThreadNotifierTest, One) {
PIThreadNotifier n;
int cnt = 0;
PIThread t1(
[&n, &cnt]() {
n.wait();
cnt++;
},
true);
piMSleep(10);
n.notifyOnce();
piMSleep(10);
ASSERT_EQ(cnt, 1);
n.notifyOnce();
piMSleep(10);
ASSERT_EQ(cnt, 2);
}
TEST(PIThreadNotifierTest, Two) {
PIThreadNotifier n;
int cnt1 = 0;
int cnt2 = 0;
int cnt3 = 0;
PIThread t1(
[&n, &cnt1]() {
n.wait();
cnt1++;
piMSleep(2);
},
true);
PIThread t2(
[&n, &cnt2]() {
n.wait();
cnt2++;
piMSleep(2);
},
true);
PIThread t3(
[&n, &cnt3]() {
n.notifyOnce();
cnt3++;
piMSleep(1);
},
true);
piMSleep(20);
t3.stop(true);
piMSleep(100);
t1.stop();
t2.stop();
ASSERT_EQ(cnt1 + cnt2, cnt3);
}

View File

@@ -1,62 +0,0 @@
#ifndef AWRCANFLASHER_TESTUTIL_H
#define AWRCANFLASHER_TESTUTIL_H
#include "pithread.h"
#include <atomic>
/**
* Minimum wait thread start, switch context or another interthread communication action time. Increase it if tests
* write "Start thread timeout reach!" message. You can reduce it if you want increase test performance.
*/
const int WAIT_THREAD_TIME_MS = 400;
const int THREAD_COUNT = 5;
class TestUtil: public PIObject {
PIOBJECT(TestUtil)
public:
double threadStartTime;
PIThread * thread = new PIThread();
std::atomic_bool isRunning;
std::function<void()> adapterFunctionDefault;
TestUtil(): isRunning(false) {}
bool createThread(const std::function<void()> & fun = nullptr, PIThread * thread_ = nullptr) {
std::function<void()> actualFun = fun == nullptr ? adapterFunctionDefault : fun;
if (thread_ == nullptr) thread_ = thread;
thread_->startOnce([=](void *) {
isRunning = true;
actualFun();
});
return waitThread(thread_);
}
bool waitThread(PIThread * thread_, bool runningStatus = true) {
PITimeMeasurer measurer;
bool isTimeout = !thread_->waitForStart(WAIT_THREAD_TIME_MS);
while (!isRunning) {
isTimeout = WAIT_THREAD_TIME_MS <= measurer.elapsed_m();
if (isTimeout) break;
piUSleep(100);
}
threadStartTime = measurer.elapsed_m();
if (isTimeout) piCout << "Start thread timeout reach!";
if (threadStartTime > 1) {
piCout << "Start time" << threadStartTime << "ms";
} else if (threadStartTime > 0.001) {
piCout << "Start time" << threadStartTime * 1000 << "mcs";
} else {
piCout << "Start time" << threadStartTime * 1000 * 1000 << "ns";
}
return !isTimeout;
}
};
#endif // AWRCANFLASHER_TESTUTIL_H

View File

@@ -376,7 +376,7 @@ TEST(PIMathMatrixT_Test, invert) {
matrix3 = matrix1; matrix3 = matrix1;
matrix1.invert(); matrix1.invert();
EXPECT_EQ(matrix1, matrix3); EXPECT_EQ(matrix1, matrix3);
EXPECT_DOUBLE_EQ(std::abs(d1 - (1. / d2)), 0.); EXPECT_NEAR(std::abs(d1 - (1. / d2)), 0., 1e-12);
} }
TEST(PIMathMatrixT_Test, inverted) { TEST(PIMathMatrixT_Test, inverted) {

View File

@@ -82,7 +82,9 @@ TEST(PIProtectedVariable_ThreadSafety, ConcurrentReadWrite) {
for (int j = 0; j < NUM_ITERATIONS; ++j) { for (int j = 0; j < NUM_ITERATIONS; ++j) {
auto val = pv.getRef(); auto val = pv.getRef();
(*val)++; (*val)++;
auto val2 = pv.getRef();
writeCount++; writeCount++;
ASSERT_EQ(writeCount, *val2);
} }
})); }));
} }

View File

@@ -467,11 +467,20 @@ bool procDpkg(const PIString & l) {
if (!vs.isEmpty()) { if (!vs.isEmpty()) {
PIStringList lines = vs.split('\n').reverse(); PIStringList lines = vs.split('\n').reverse();
for (auto l: lines) { for (auto l: lines) {
auto sl = l; l = l.left(l.find(":"));
l = l.left(l.find(":")); if (!l.isEmpty() && !l.contains(' ') && !l.endsWith("-cross") && !l.endsWith("-dev")) {
if (!l.isEmpty() && !l.endsWith("-cross") && !l.contains(' ')) all_deps << l; // PICout(true) << "** found \"" << l << "\" in \"" << sl << "\"";
// PICout(true) << "** found \"" << l << "\" in \"" << sl << "\""; all_deps << l;
return true; return true;
}
}
for (auto l: lines) {
l = l.left(l.find(":"));
if (!l.isEmpty() && !l.contains(' ') && !l.endsWith("-cross")) {
// PICout(true) << "** found \"" << l << "\" in \"" << sl << "\"";
all_deps << l;
return true;
}
} }
} }
// piCout << "No dep on" << l; // piCout << "No dep on" << l;