24 Commits

Author SHA1 Message Date
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
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
449978bda0 revert main and more tests 2026-03-17 19:07:01 +03:00
fe01c353e6 simplify tests 2026-03-17 18:56:20 +03:00
9f57f0107e remove picout 2026-03-17 18:34:48 +03:00
ac877f1024 fixes 2026-03-17 18:13:23 +03:00
8ccc05ee78 simplify piprotectedvariable 2026-03-17 17:33:27 +03:00
48 changed files with 375 additions and 917 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

@@ -6,7 +6,7 @@ endif()
project(PIP) project(PIP)
set(PIP_MAJOR 5) set(PIP_MAJOR 5)
set(PIP_MINOR 6) set(PIP_MINOR 6)
set(PIP_REVISION 0) set(PIP_REVISION 1)
set(PIP_SUFFIX ) set(PIP_SUFFIX )
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,6 +597,7 @@ 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 (PIP_MANUAL_TEST)
if(microhttpd_FOUND AND curl_FOUND) if(microhttpd_FOUND AND curl_FOUND)
add_executable(pip_test "main.cpp") add_executable(pip_test "main.cpp")
target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client) target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client)
@@ -602,6 +608,7 @@ if (NOT CROSSTOOLS)
endif() endif()
endif() endif()
endif() endif()
endif()
else() else()
@@ -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;
} }
@@ -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])) {

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,16 +1277,17 @@ 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.
//! \~russian Аккумулирует значение по всем элементам. //! \~russian Аккумулирует значение по всем элементам.
//! \~\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

@@ -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

@@ -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

@@ -34,21 +34,29 @@
template<typename T> template<typename T>
class PIP_EXPORT PIProtectedVariable { class PIP_EXPORT PIProtectedVariable {
public: public:
//! \~english Constructs %PIProtectedVariable and initialize variable by value `v`.
//! \~russian Создает %PIProtectedVariable и инициализирует переменную значением `v`.
PIProtectedVariable(T v = T()): var(std::move(v)) {}
//! \~\brief //! \~\brief
//! \~english Pointer-like wrapper returned by \a getRef() while the protected value remains locked. //! \~english Pointer-like wrapper returned by \a getRef() while the protected value remains locked.
//! \~russian Указателеподобная обертка, возвращаемая \a getRef(), пока защищенное значение остается заблокированным. //! \~russian Указателеподобная обертка, возвращаемая \a getRef(), пока защищенное значение остается заблокированным.
class PIP_EXPORT Pointer { class PIP_EXPORT Pointer {
friend class PIProtectedVariable<T>; friend class PIProtectedVariable<T>;
NO_COPY_CLASS(Pointer);
Pointer & operator=(Pointer && other) = delete;
public: public:
//! \~english Copies wrapper state for access to the same protected value. //! \~english Move constructor - transfers ownership of the lock.
//! \~russian Копирует состояние обертки для доступа к тому же защищенному значению. //! \~russian Конструктор перемещения - передает владение блокировкой.
Pointer(const Pointer & v): pv(v.pv), counter(v.counter + 1) {} Pointer(Pointer && other): pv(other.pv) { other.can_unlock = false; };
//! \~english Destroys wrapper and releases the mutex when it owns the original lock. //! \~english Destroys wrapper and releases the mutex.
//! \~russian Уничтожает обертку и освобождает мьютекс, когда она владеет исходной блокировкой. //! \~russian Уничтожает обертку и освобождает мьютекс.
~Pointer() { ~Pointer() {
if (counter == 0) pv.mutex.unlock(); if (can_unlock) {
pv.mutex.unlock();
}
} }
//! \~english Returns pointer access to the protected value. //! \~english Returns pointer access to the protected value.
@@ -60,11 +68,11 @@ public:
T & operator*() { return pv.var; } T & operator*() { return pv.var; }
private: private:
Pointer() = delete; explicit Pointer() = delete;
Pointer(PIProtectedVariable<T> & v): pv(v) {} explicit Pointer(PIProtectedVariable<T> & v): pv(v) { pv.mutex.lock(); }
PIProtectedVariable<T> & pv; PIProtectedVariable<T> & pv;
int counter = 0; bool can_unlock = true;
}; };
//! \~english Replaces the protected value with \a v. //! \~english Replaces the protected value with \a v.
@@ -76,10 +84,7 @@ public:
//! \~english Locks the value and returns wrapper-based access to it. //! \~english Locks the value and returns wrapper-based access to it.
//! \~russian Блокирует значение и возвращает обертку для доступа к нему. //! \~russian Блокирует значение и возвращает обертку для доступа к нему.
Pointer getRef() { Pointer getRef() { return Pointer(*this); }
mutex.lock();
return Pointer(*this);
}
//! \~english Returns a copy of the protected value. //! \~english Returns a copy of the protected value.
//! \~russian Возвращает копию защищенного значения. //! \~russian Возвращает копию защищенного значения.
@@ -98,7 +103,7 @@ public:
private: private:
mutable PIMutex mutex; mutable PIMutex mutex;
T var; T var = {};
}; };

View File

@@ -540,7 +540,7 @@ PRIVATE_DEFINITION_END(PIThread)
PIThread::PIThread(void * data, ThreadFunc func, bool startNow, PISystemTime loop_delay): PIObject() { 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,7 +738,7 @@ 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,
@@ -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;
@@ -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

@@ -68,8 +68,8 @@ PIThreadPoolExecutor::~PIThreadPoolExecutor() {
} }
void PIThreadPoolExecutor::execute(const std::function<void()> & runnable) { void PIThreadPoolExecutor::execute(std::function<void()> runnable) {
if (!isShutdown_) taskQueue.offer(runnable); if (!isShutdown_) taskQueue.offer(std::move(runnable));
} }

View File

@@ -59,7 +59,7 @@ public:
//! \~russian //! \~russian
//! Это вызов по принципу best-effort без ожидания результата и без сообщения о том, была ли задача принята. //! Это вызов по принципу best-effort без ожидания результата и без сообщения о том, была ли задача принята.
//! \После запроса на завершение новые задачи игнорируются. //! \После запроса на завершение новые задачи игнорируются.
void execute(const std::function<void()> & runnable); void execute(std::function<void()> runnable);
//! \~english Requests immediate shutdown and stops worker threads without waiting for queued tasks to finish. //! \~english Requests immediate shutdown and stops worker threads without waiting for queued tasks to finish.
//! \~russian Запрашивает немедленное завершение и останавливает рабочие потоки без ожидания завершения задач в очереди. //! \~russian Запрашивает немедленное завершение и останавливает рабочие потоки без ожидания завершения задач в очереди.

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

@@ -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

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));
} }

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()
@@ -25,15 +26,17 @@ macro(pip_test NAME)
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)
if (TESTS_RUN)
gtest_discover_tests(${_target}) 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)
pip_test(client_server pip_client_server) pip_test(client_server pip_client_server)
pip_test(io) pip_test(io)
pip_test(system) pip_test(system)
pip_test(thread)

View File

@@ -32,6 +32,7 @@ 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>();
piMinSleep();
auto c = createAndConnectClient<TestClient<false, false>>(); auto c = createAndConnectClient<TestClient<false, false>>();
waitLoop([s]() { return s->clientsCount() > 0; }, loop_timeout); waitLoop([s]() { return s->clientsCount() > 0; }, loop_timeout);
@@ -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

@@ -0,0 +1,118 @@
#include "piprotectedvariable.h"
#include "pistring.h"
#include "pithread.h"
#include "piliterals_time.h"
#include "gtest/gtest.h"
#include <atomic>
// Basic functionality tests
TEST(PIProtectedVariable_Basic, BasicFunctionality) {
// Test basic set/get with different types
PIProtectedVariable<int> pvInt;
PIProtectedVariable<double> pvDouble;
PIProtectedVariable<PIString> pvString;
pvInt.set(123);
pvDouble.set(3.14159);
pvString.set(PIString("Hello, World!"));
EXPECT_EQ(pvInt.get(), 123);
EXPECT_DOUBLE_EQ(pvDouble.get(), 3.14159);
EXPECT_EQ(pvString.get(), PIString("Hello, World!"));
// Test operator=
pvInt = 999;
EXPECT_EQ(pvInt.get(), 999);
// Test getRef() with Pointer
struct TestStruct {
int x = 10;
int y = 20;
int getValue() const { return x + y; }
};
PIProtectedVariable<TestStruct> pvStruct;
auto ptr = pvStruct.getRef();
EXPECT_EQ(ptr->x, 10);
EXPECT_EQ(ptr->y, 20);
EXPECT_EQ(ptr->getValue(), 30);
// Modify through pointer
*ptr = TestStruct();
ptr->x = 100;
EXPECT_EQ(pvStruct.get().x, 100);
// Test for Pointer
pvInt.set(42);
auto ptr1 = pvInt.getRef();
EXPECT_EQ(*ptr1, 42);
*ptr1 = 55;
EXPECT_EQ(*ptr1, 55);
auto ptr2 = std::move(ptr1);
EXPECT_EQ(*ptr2, 55);
*ptr2 = 100;
EXPECT_EQ(*ptr2, 100);
auto ptr3 = pvInt.getRef();
EXPECT_EQ(*ptr3, 100);
*ptr3 = 333;
EXPECT_EQ(*ptr3, 333);
EXPECT_EQ(pvInt.get(), 333);
}
// Thread safety tests
TEST(PIProtectedVariable_ThreadSafety, ConcurrentReadWrite) {
PIProtectedVariable<int> pv;
std::atomic<int> writeCount(0);
std::atomic<int> readCount(0);
std::atomic<int> invalidReads(0);
const int NUM_ITERATIONS = 1000;
const int NUM_WRITERS = 10;
const int NUM_READERS = 20;
const int TOTAL_WRITES = NUM_WRITERS * NUM_ITERATIONS;
// Collect thread handles for joining
PIVector<PIThread *> threads;
// Create writer threads
for (int i = 0; i < NUM_WRITERS; ++i) {
threads.push_back(new PIThread([&pv, &writeCount]() {
for (int j = 0; j < NUM_ITERATIONS; ++j) {
auto val = pv.getRef();
(*val)++;
auto val2 = pv.getRef();
writeCount++;
ASSERT_EQ(writeCount, *val2);
}
}));
}
// Create reader threads
for (int i = 0; i < NUM_READERS; ++i) {
threads.push_back(new PIThread([&pv, &invalidReads, &readCount]() {
for (int j = 0; j < NUM_ITERATIONS; ++j) {
auto val = pv.get();
readCount++;
// Value should always be in valid range [0, TOTAL_WRITES]
if (val < 0 || val > TOTAL_WRITES) {
invalidReads++;
}
}
}));
}
threads.forEach([](PIThread * & t) {t->startOnce();});
threads.forEach([](PIThread * & t) {t->waitForFinish(2_s);});
piDeleteAll(threads);
// Verify results
EXPECT_EQ(writeCount, TOTAL_WRITES);
EXPECT_EQ(readCount, NUM_READERS * NUM_ITERATIONS);
EXPECT_EQ(invalidReads, 0) << "All reads should return valid values in range [0, " << TOTAL_WRITES << "]";
// Final value should be TOTAL_WRITES
int finalVal = pv.get();
EXPECT_EQ(finalVal, TOTAL_WRITES);
}

View File

@@ -467,13 +467,22 @@ 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.endsWith("-cross") && !l.contains(' ')) all_deps << l; if (!l.isEmpty() && !l.contains(' ') && !l.endsWith("-cross") && !l.endsWith("-dev")) {
// 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;
return false; return false;
} }