diff --git a/.gitignore b/.gitignore index 1c093651..c420a4bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /src_main/piversion.h -/.svn +/.* /doc/rtf _unsused CMakeLists.txt.user* diff --git a/3rd/LuaBridge/detail/LuaException.h b/3rd/LuaBridge/detail/LuaException.h index 836cb852..2fe6d608 100644 --- a/3rd/LuaBridge/detail/LuaException.h +++ b/3rd/LuaBridge/detail/LuaException.h @@ -97,8 +97,10 @@ public: { int code = lua_pcall (L, nargs, nresults, msgh); - if (code != LUABRIDGE_LUA_OK) - Throw (LuaException (L, code)); + if (code != LUABRIDGE_LUA_OK) { + // Throw (LuaException (L, code)); + assert(true); + } } //---------------------------------------------------------------------------- @@ -128,7 +130,8 @@ protected: private: static int throwAtPanic (lua_State* L) { - throw LuaException (L, -1); + // throw LuaException (L, -1); + return -1; } }; diff --git a/3rd/LuaBridge/detail/Namespace.h b/3rd/LuaBridge/detail/Namespace.h index 68ae21fa..8a4dd20a 100644 --- a/3rd/LuaBridge/detail/Namespace.h +++ b/3rd/LuaBridge/detail/Namespace.h @@ -101,7 +101,8 @@ protected: { 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) { - throw std::logic_error ("endNamespace () called on global namespace"); + std::cerr << ("endNamespace () called on global namespace"); + assert(true); } assert (m_stackSize > 1); @@ -1150,7 +1152,8 @@ public: { 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) diff --git a/3rd/LuaBridge/detail/Userdata.h b/3rd/LuaBridge/detail/Userdata.h index ef13d9fc..3993e176 100644 --- a/3rd/LuaBridge/detail/Userdata.h +++ b/3rd/LuaBridge/detail/Userdata.h @@ -33,6 +33,7 @@ #include #include +#include namespace luabridge { @@ -320,7 +321,8 @@ public: lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo ::getClassKey ()); 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); return ud; @@ -375,7 +377,8 @@ private: lua_rawgetp (L, LUA_REGISTRYINDEX, key); 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); } diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ecdbb27..754101b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,9 @@ if (POLICY CMP0177) endif() project(PIP) set(PIP_MAJOR 5) -set(PIP_MINOR 6) +set(PIP_MINOR 7) set(PIP_REVISION 0) -set(PIP_SUFFIX ) +set(PIP_SUFFIX _alpha) set(PIP_COMPANY 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(STD_IOSTREAM "Building with std iostream operators support" 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(PIP_FFTW_F "Support fftw module for float" 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_MANUAL_TEST "Build dev test (main.cpp)" OFF) set(PIP_UTILS 1) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_CXX_STANDARD 11) @@ -96,7 +98,7 @@ set(PIP_TESTS_LIST) set(PIP_EXPORTS) set(PIP_3PL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rd") -set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;client_server;cloud;lua;http_client;http_server") +set(PIP_SRC_MODULES "console;crypt;compress;usb;fftw;opencl;io_utils;client_server;cloud;lua;http_client;http_server;mqtt_client") foreach(_m ${PIP_SRC_MODULES}) set(PIP_MSG_${_m} "no") string(TOUPPER "${_m}" _mu) @@ -360,10 +362,13 @@ endif() if(WIN32) add_definitions(-DPSAPI_VERSION=1) if(${C_COMPILER} STREQUAL "cl.exe") - set(CMAKE_CXX_FLAGS "/O2 /Ob2 /Ot /W0") + set(CMAKE_CXX_FLAGS "/O2 /Ob2 /Ot /W0 /EH-") endif() else() 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) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-32") endif() @@ -382,28 +387,6 @@ endif() add_subdirectory("3rd/pcre2" EXCLUDE_FROM_ALL) list(APPEND LIBS_MAIN pcre2-16-static) -#set(MQTT_C_OpenSSL_SUPPORT OFF CACHE BOOL "" FORCE) -#set(MQTT_C_MbedTLS_SUPPORT OFF CACHE BOOL "" FORCE) -#set(MQTT_C_BearSSL_SUPPORT OFF CACHE BOOL "" FORCE) -#set(MQTT_C_EXAMPLES OFF CACHE BOOL "" FORCE) -#set(MQTT_C_INSTALL_EXAMPLES OFF CACHE BOOL "" FORCE) -#set(MQTT_C_TESTS OFF CACHE BOOL "" FORCE) -set(PAHO_WITH_SSL OFF CACHE BOOL "" FORCE) -set(PAHO_WITH_LIBRESSL OFF CACHE BOOL "" FORCE) -set(PAHO_WITH_LIBUUID OFF CACHE BOOL "" FORCE) -set(PAHO_BUILD_SHARED OFF CACHE BOOL "" FORCE) -set(PAHO_BUILD_STATIC ON CACHE BOOL "" FORCE) -set(PAHO_BUILD_DOCUMENTATION OFF CACHE BOOL "" FORCE) -set(PAHO_BUILD_SAMPLES OFF CACHE BOOL "" FORCE) -set(PAHO_BUILD_DEB_PACKAGE OFF CACHE BOOL "" FORCE) -set(PAHO_ENABLE_TESTING OFF CACHE BOOL "" FORCE) -set(PAHO_ENABLE_CPACK OFF CACHE BOOL "" FORCE) -set(PAHO_HIGH_PERFORMANCE OFF CACHE BOOL "" FORCE) -set(PAHO_USE_SELECT OFF CACHE BOOL "" FORCE) -set(PAHO_NO_TCP_NODELAY OFF CACHE BOOL "" FORCE) -add_subdirectory("3rd/paho.mqtt.c" EXCLUDE_FROM_ALL) -list(APPEND LIBS_MAIN eclipse-paho-mqtt-c::paho-mqtt3c-static) - pip_module(main "${LIBS_MAIN}" "PIP main library" "" "${PIP_3PL_DIR}/BLAKE2;${PIP_3PL_DIR}/SipHash" "") generate_export_header(pip) @@ -607,6 +590,34 @@ if (NOT CROSSTOOLS) endif() + if (PIP_BUILD_MQTT_CLIENT) + # paho.mqtt.c + + #set(MQTT_C_OpenSSL_SUPPORT OFF CACHE BOOL "" FORCE) + #set(MQTT_C_MbedTLS_SUPPORT OFF CACHE BOOL "" FORCE) + #set(MQTT_C_BearSSL_SUPPORT OFF CACHE BOOL "" FORCE) + #set(MQTT_C_EXAMPLES OFF CACHE BOOL "" FORCE) + #set(MQTT_C_INSTALL_EXAMPLES OFF CACHE BOOL "" FORCE) + #set(MQTT_C_TESTS OFF CACHE BOOL "" FORCE) + set(PAHO_WITH_SSL OFF CACHE BOOL "" FORCE) + set(PAHO_WITH_LIBRESSL OFF CACHE BOOL "" FORCE) + set(PAHO_WITH_LIBUUID OFF CACHE BOOL "" FORCE) + set(PAHO_BUILD_SHARED OFF CACHE BOOL "" FORCE) + set(PAHO_BUILD_STATIC ON CACHE BOOL "" FORCE) + set(PAHO_BUILD_DOCUMENTATION OFF CACHE BOOL "" FORCE) + set(PAHO_BUILD_SAMPLES OFF CACHE BOOL "" FORCE) + set(PAHO_BUILD_DEB_PACKAGE OFF CACHE BOOL "" FORCE) + set(PAHO_ENABLE_TESTING OFF CACHE BOOL "" FORCE) + set(PAHO_ENABLE_CPACK OFF CACHE BOOL "" FORCE) + set(PAHO_HIGH_PERFORMANCE OFF CACHE BOOL "" FORCE) + set(PAHO_USE_SELECT OFF CACHE BOOL "" FORCE) + set(PAHO_NO_TCP_NODELAY OFF CACHE BOOL "" FORCE) + add_subdirectory("3rd/paho.mqtt.c" EXCLUDE_FROM_ALL) + + pip_module(mqtt_client eclipse-paho-mqtt-c::paho-mqtt3c-static "PIP MQTT Client" "" "" " (internal)") + endif() + + # Test program if(PIP_UTILS) @@ -614,12 +625,14 @@ if (NOT CROSSTOOLS) #target_link_libraries(pip_plugin pip) if (NOT DEFINED ANDROID_PLATFORM) - if(microhttpd_FOUND AND curl_FOUND) - add_executable(pip_test "main.cpp") - target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client) - if(sodium_FOUND) - add_executable(pip_cloud_test "main_picloud_test.cpp") - target_link_libraries(pip_cloud_test pip_cloud) + if (PIP_MANUAL_TEST) + if(microhttpd_FOUND AND curl_FOUND) + add_executable(pip_test "main.cpp") + target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client pip_mqtt_client) + if(sodium_FOUND) + add_executable(pip_cloud_test "main_picloud_test.cpp") + target_link_libraries(pip_cloud_test pip_cloud) + endif() endif() endif() endif() @@ -756,7 +769,7 @@ if ((NOT PIP_FREERTOS) AND (NOT CROSSTOOLS)) find_package(Doxygen) if(DOXYGEN_FOUND) set(DOXY_DEFINES "${PIP_EXPORTS}") - foreach (_m "console" "usb" "compress" "crypt" "client_server" "cloud" "fftw" "opencl" "io_utils" "lua" "http_server" "http_client") + foreach (_m "console" "usb" "compress" "crypt" "client_server" "cloud" "fftw" "opencl" "io_utils" "lua" "http_server" "http_client" "mqtt_client") string(TOUPPER "${_m}" _mdef) list(APPEND DOXY_DEFINES "PIP_${_mdef}_EXPORT") endforeach() @@ -845,6 +858,9 @@ if (PIP_TESTS_LIST) foreach(_test ${PIP_TESTS_LIST}) message(" * ${_test}") endforeach() + if (TESTS_RUN) + message("TESTS_RUN ON -> Run tests before install step") + endif() else() message(" Tests: skip (tests off)") endif() diff --git a/cmake/FindPIP.cmake b/cmake/FindPIP.cmake index 82edb664..10f4b48c 100644 --- a/cmake/FindPIP.cmake +++ b/cmake/FindPIP.cmake @@ -14,6 +14,7 @@ Create imported targets: * PIP::Lua * PIP::HTTPClient * PIP::HTTPServer + * PIP::MQTTClient These targets include directories and depends on main library @@ -25,10 +26,10 @@ include(SHSTKMacros) shstk_set_find_dirs(PIP) -set(__libs "usb;crypt;console;fftw;compress;opencl;io_utils;client_server;cloud;lua;http_client;http_server") +set(__libs "usb;crypt;console;fftw;compress;opencl;io_utils;client_server;cloud;lua;http_client;http_server;mqtt_client") if (BUILDING_PIP) - #set(_libs "pip;pip_usb;pip_console;pip_crypt;pip_fftw;pip_compress;pip_opencl;pip_io_utils;pip_cloud;pip_lua;pip_http_client;pip_http_server") + #set(_libs "pip;pip_usb;pip_console;pip_crypt;pip_fftw;pip_compress;pip_opencl;pip_io_utils;pip_cloud;pip_lua;pip_http_client;pip_http_server;pip_mqtt_client") #set(_bins "pip_cmg;pip_rc;deploy_tool") #get_target_property(_path pip BINARY_DIR) #get_target_property(_path pip LIBRARY_OUTPUT_NAME) @@ -100,6 +101,7 @@ set(__module_cloud Cloud ) set(__module_lua Lua ) set(__module_http_client HTTPClient ) set(__module_http_server HTTPServer ) +set(__module_mqtt_client MQTTClient ) foreach (_l ${__libs}) set( __inc_${_l} "") diff --git a/libs/client_server/piclientserver_server.cpp b/libs/client_server/piclientserver_server.cpp index f1700b01..edfd2083 100644 --- a/libs/client_server/piclientserver_server.cpp +++ b/libs/client_server/piclientserver_server.cpp @@ -72,11 +72,7 @@ PIClientServer::Server::~Server() { clean_thread->waitForFinish(); piDeleteSafety(clean_thread); stopServer(); - for (auto c: clients) { - c->aboutDelete(); - c->destroy(); - delete c; - } + closeAll(); piDeleteSafety(tcp_server); } diff --git a/libs/http_client/pihttpclient.cpp b/libs/http_client/pihttpclient.cpp index b6d3aa52..9dbea1e3 100644 --- a/libs/http_client/pihttpclient.cpp +++ b/libs/http_client/pihttpclient.cpp @@ -247,7 +247,7 @@ PIHTTPClient * PIHTTPClient::onFinish(std::function f) { PIHTTPClient * PIHTTPClient::onFinish(std::function f) { - on_finish = f; + on_finish = std::move(f); return this; } @@ -258,7 +258,7 @@ PIHTTPClient * PIHTTPClient::onError(std::function f) { PIHTTPClient * PIHTTPClient::onError(std::function f) { - on_error = f; + on_error = std::move(f); return this; } @@ -269,7 +269,7 @@ PIHTTPClient * PIHTTPClient::onAbort(std::function f) { PIHTTPClient * PIHTTPClient::onAbort(std::function f) { - on_abort = f; + on_abort = std::move(f); return this; } diff --git a/libs/http_server/pihttpserver.cpp b/libs/http_server/pihttpserver.cpp index b80dfd7b..796f88c4 100644 --- a/libs/http_server/pihttpserver.cpp +++ b/libs/http_server/pihttpserver.cpp @@ -45,7 +45,7 @@ bool PIHTTPServer::registerPath(const PIString & path, PIHTTP::Method method, Re Endpoint ep; if (!ep.create(path)) return false; ep.method = method; - ep.function = functor; + ep.function = std::move(functor); endpoints[ep.priority] << ep; return true; } diff --git a/libs/main/client_server/piclientserver_server.h b/libs/main/client_server/piclientserver_server.h index 14bf68bb..138d1189 100644 --- a/libs/main/client_server/piclientserver_server.h +++ b/libs/main/client_server/piclientserver_server.h @@ -103,7 +103,7 @@ public: //! \~english Sets the factory used to create accepted client objects. //! \~russian Устанавливает фабрику, создающую объекты принятых клиентов. - void setClientFactory(std::function f) { client_factory = f; } + void setClientFactory(std::function f) { client_factory = std::move(f); } private: void newClient(ServerClient * c); diff --git a/libs/main/console/pikbdlistener.h b/libs/main/console/pikbdlistener.h index b79fb85e..d74e112b 100644 --- a/libs/main/console/pikbdlistener.h +++ b/libs/main/console/pikbdlistener.h @@ -204,7 +204,7 @@ public: //! \~english Sets the callback receiving both key event and user data. //! \~russian Устанавливает обратный вызов, получающий событие клавиши и пользовательские данные. - void setSlot(KBFunc slot) { ret_func = slot; } + void setSlot(KBFunc slot) { ret_func = std::move(slot); } //! \~english Sets the callback that only receives the key event and ignores user data. //! \~russian Устанавливает обратный вызов, получающий только событие клавиши и игнорирующий пользовательские данные. diff --git a/libs/main/containers/pideque.h b/libs/main/containers/pideque.h index fe5c53be..047bf410 100644 --- a/libs/main/containers/pideque.h +++ b/libs/main/containers/pideque.h @@ -184,7 +184,7 @@ public: //! \endcode inline PIDeque(size_t piv_size, std::function f) { PIINTROSPECTION_CONTAINER_NEW(T, sizeof(T)) - expand(piv_size, f); + expand(piv_size, std::move(f)); } //! \~english Move constructor. @@ -729,7 +729,7 @@ public: //! заданному в передаваемой функции `test`, или `def` если такого элемента нет. //! \~\sa \a indexWhere() inline const T & atWhere(std::function test, ssize_t start = 0, const T & def = T()) const { - const ssize_t i = indexWhere(test, start); + const ssize_t i = indexWhere(std::move(test), start); if (i < 0) return def; else @@ -743,7 +743,7 @@ public: //! заданному в передаваемой функции `test`, или `def` если такого элемента нет. //! \~\sa \a lastIndexWhere() inline const T & lastAtWhere(std::function test, ssize_t start = -1, const T & def = T()) const { - const ssize_t i = lastIndexWhere(test, start); + const ssize_t i = lastIndexWhere(std::move(test), start); if (i < 0) return def; else @@ -1265,7 +1265,7 @@ public: deleteT(pid_data + pid_start + new_size, pid_size - new_size); pid_size = new_size; } else if (new_size > pid_size) { - expand(new_size, f); + expand(new_size, std::move(f)); } return *this; } @@ -1328,15 +1328,15 @@ public: if (index < pid_size - 1) { const size_t os = pid_size - index - 1; memmove(reinterpret_cast(pid_data + pid_start + index + 1), - reinterpret_cast(pid_data + pid_start + index), - os * sizeof(T)); + reinterpret_cast(pid_data + pid_start + index), + os * sizeof(T)); } } else { alloc_backward(pid_size + 1, -1); if (index > 0) { memmove(reinterpret_cast(pid_data + pid_start), - reinterpret_cast(pid_data + pid_start + 1), - index * sizeof(T)); + reinterpret_cast(pid_data + pid_start + 1), + index * sizeof(T)); } } elementNew(pid_data + pid_start + index, e); @@ -1358,15 +1358,15 @@ public: if (index < pid_size - 1) { const size_t os = pid_size - index - 1; memmove(reinterpret_cast(pid_data + pid_start + index + 1), - reinterpret_cast(pid_data + pid_start + index), - os * sizeof(T)); + reinterpret_cast(pid_data + pid_start + index), + os * sizeof(T)); } } else { alloc_backward(pid_size + 1, -1); if (index > 0) { memmove(reinterpret_cast(pid_data + pid_start), - reinterpret_cast(pid_data + pid_start + 1), - index * sizeof(T)); + reinterpret_cast(pid_data + pid_start + 1), + index * sizeof(T)); } } elementNew(pid_data + pid_start + index, std::move(e)); @@ -1393,15 +1393,15 @@ public: alloc_forward(pid_size + v.pid_size); if (os > 0) { memmove(reinterpret_cast(pid_data + pid_start + index + v.pid_size), - reinterpret_cast(pid_data + pid_start + index), - os * sizeof(T)); + reinterpret_cast(pid_data + pid_start + index), + os * sizeof(T)); } } else { alloc_backward(pid_size + v.pid_size, -v.pid_size); if (index > 0) { memmove(reinterpret_cast(pid_data + pid_start), - reinterpret_cast(pid_data + pid_start + v.pid_size), - index * sizeof(T)); + reinterpret_cast(pid_data + pid_start + v.pid_size), + index * sizeof(T)); } } newT(pid_data + pid_start + index, v.pid_data + v.pid_start, v.pid_size); @@ -1426,15 +1426,15 @@ public: alloc_forward(pid_size + init_list.size()); if (os > 0) { memmove(reinterpret_cast(pid_data + pid_start + index + init_list.size()), - reinterpret_cast(pid_data + pid_start + index), + reinterpret_cast(pid_data + pid_start + index), os * sizeof(T)); } } else { alloc_backward(pid_size + init_list.size(), -init_list.size()); if (index > 0) { memmove(reinterpret_cast(pid_data + pid_start), - reinterpret_cast(pid_data + pid_start + init_list.size()), - index * sizeof(T)); + reinterpret_cast(pid_data + pid_start + init_list.size()), + index * sizeof(T)); } } newT(pid_data + pid_start + index, init_list.begin(), init_list.size()); @@ -1462,13 +1462,13 @@ public: deleteT(pid_data + pid_start + index, count); if (os <= index) { memmove(reinterpret_cast(pid_data + pid_start + index), - reinterpret_cast(pid_data + pid_start + index + count), - os * sizeof(T)); + reinterpret_cast(pid_data + pid_start + index + count), + os * sizeof(T)); } else { if (index > 0) { memmove(reinterpret_cast(pid_data + pid_start + count), - reinterpret_cast(pid_data + pid_start), - index * sizeof(T)); + reinterpret_cast(pid_data + pid_start), + index * sizeof(T)); } pid_start += count; } @@ -1540,7 +1540,7 @@ public: //! \endcode //! \~\sa \a sort() inline PIDeque & sort(std::function comp) { - std::stable_sort(begin(), end(), comp); + std::stable_sort(begin(), end(), std::move(comp)); return *this; } @@ -1654,7 +1654,13 @@ public: //! \endcode //! \~\sa \a remove(), \a removeOne(), \a removeWhere() inline PIDeque & removeWhere(std::function test) { - ssize_t j = indexWhere(test); + ssize_t j = -1; + for (size_t i = pid_start; i < pid_start + pid_size; ++i) { + if (test(pid_data[i])) { + j = ssize_t(i) - pid_start; + break; + } + } if (j != -1) { for (size_t i = j + 1; i < pid_size; ++i) { if (!test(pid_data[i + pid_start])) { @@ -2545,21 +2551,21 @@ public: if (index + count > pid_size) count = pid_size - index; ret.alloc_forward(count); memcpy(reinterpret_cast(ret.pid_data + ret.pid_start), - reinterpret_cast(pid_data + pid_start + index), - count * sizeof(T)); + reinterpret_cast(pid_data + pid_start + index), + count * sizeof(T)); const size_t os = pid_size - index - count; if (os <= index) { if (os > 0) { memmove(reinterpret_cast(pid_data + pid_start + index), - reinterpret_cast(pid_data + pid_start + index + count), - os * sizeof(T)); + reinterpret_cast(pid_data + pid_start + index + count), + os * sizeof(T)); } } else { if (index > 0) { memmove(reinterpret_cast(pid_data + pid_start + count), - reinterpret_cast(pid_data + pid_start), - index * sizeof(T)); + reinterpret_cast(pid_data + pid_start), + index * sizeof(T)); } pid_start += count; } @@ -2646,8 +2652,8 @@ private: size_t ns = (pid_rsize - pid_size) / 2; if (pid_start != ns) { memmove(reinterpret_cast(pid_data + ns), - reinterpret_cast(pid_data + pid_start), - pid_size * sizeof(T)); + reinterpret_cast(pid_data + pid_start), + pid_size * sizeof(T)); pid_start = ns; } } @@ -2656,8 +2662,8 @@ private: const size_t ns = (pid_rsize - pid_size) / 2; if (pid_start != ns) { memmove(reinterpret_cast(pid_data + ns), - reinterpret_cast(pid_data + pid_start), - pid_size * sizeof(T)); + reinterpret_cast(pid_data + pid_start), + pid_size * sizeof(T)); pid_start = ns; } } @@ -2694,8 +2700,8 @@ private: PIINTROSPECTION_CONTAINER_ALLOC(T, (as - pid_rsize)) if (pid_rsize > 0 && pid_data) { memcpy(reinterpret_cast(tmp_data + new_start), - reinterpret_cast(pid_data + pid_start), - pid_size * sizeof(T)); + reinterpret_cast(pid_data + pid_start), + pid_size * sizeof(T)); dealloc(); } pid_data = tmp_data; diff --git a/libs/main/containers/pivector.h b/libs/main/containers/pivector.h index da13d844..bdd2a700 100644 --- a/libs/main/containers/pivector.h +++ b/libs/main/containers/pivector.h @@ -184,7 +184,7 @@ public: //! \endcode inline PIVector(size_t size, std::function f) { PIINTROSPECTION_CONTAINER_NEW(T, sizeof(T)) - expand(size, f); + expand(size, std::move(f)); } //! \~english Move constructor. @@ -725,7 +725,7 @@ public: //! заданному в передаваемой функции `test`, или `def` если такого элемента нет. //! \~\sa \a indexWhere() inline const T & atWhere(std::function test, ssize_t start = 0, const T & def = T()) const { - const ssize_t i = indexWhere(test, start); + const ssize_t i = indexWhere(std::move(test), start); if (i < 0) return def; else @@ -739,7 +739,7 @@ public: //! заданному в передаваемой функции `test`, или `def` если такого элемента нет. //! \~\sa \a lastIndexWhere() inline const T & lastAtWhere(std::function test, ssize_t start = -1, const T & def = T()) const { - const ssize_t i = lastIndexWhere(test, start); + const ssize_t i = lastIndexWhere(std::move(test), start); if (i < 0) return def; else @@ -1267,7 +1267,7 @@ public: deleteT(piv_data + new_size, piv_size - new_size); piv_size = new_size; } else if (new_size > piv_size) { - expand(new_size, f); + expand(new_size, std::move(f)); } return *this; } @@ -1486,7 +1486,7 @@ public: //! \endcode //! \~\sa \a sort() inline PIVector & sort(std::function comp) { - std::stable_sort(begin(), end(), comp); + std::stable_sort(begin(), end(), std::move(comp)); return *this; } @@ -1599,7 +1599,13 @@ public: //! \endcode //! \~\sa \a remove(), \a removeOne(), \a removeWhere() inline PIVector & removeWhere(std::function test) { - ssize_t j = indexWhere(test); + ssize_t j = -1; + for (size_t i = 0; i < piv_size; ++i) { + if (test(piv_data[i])) { + j = i; + break; + } + } if (j != -1) { for (size_t i = j + 1; i < piv_size; ++i) { if (!test(piv_data[i])) { diff --git a/libs/main/containers/pivector2d.h b/libs/main/containers/pivector2d.h index dd38c6f9..e60f5ed0 100644 --- a/libs/main/containers/pivector2d.h +++ b/libs/main/containers/pivector2d.h @@ -1070,7 +1070,7 @@ public: //! \~english Counts elements in the flat vector that pass the `test`. //! \~russian Подсчитывает элементы в плоском векторе, проходящие `test`. //! \~\sa PIVector::entries(std::function) - inline int entries(std::function test) const { return mat.entries(test); } + inline int entries(std::function test) const { return mat.entries(std::move(test)); } //! \~english Returns the first index (row, col) of `e` in the 2D array. @@ -1086,7 +1086,7 @@ public: //! \~russian Возвращает первый индекс (строка, столбец) в двумерном массиве, проходящий `test`. //! \~\sa PIVector::indexWhere() inline Index indexWhere(std::function test, ssize_t start = 0) const { - ssize_t flat = mat.indexWhere(test, start); + ssize_t flat = mat.indexWhere(std::move(test), start); if (flat < 0 || cols_ == 0) return Index{-1, -1}; return Index{flat / static_cast(cols_), flat % static_cast(cols_)}; } @@ -1104,7 +1104,7 @@ public: //! \~russian Возвращает последний индекс (строка, столбец) в двумерном массиве, проходящий `test`. //! \~\sa PIVector::lastIndexWhere() inline Index lastIndexWhere(std::function test, ssize_t start = -1) const { - ssize_t flat = mat.lastIndexWhere(test, start); + ssize_t flat = mat.lastIndexWhere(std::move(test), start); if (flat < 0 || cols_ == 0) return Index{-1, -1}; return Index{flat / static_cast(cols_), flat % static_cast(cols_)}; } @@ -1113,12 +1113,12 @@ public: //! \~english Tests if any element in the flat vector passes the `test`. //! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`. //! \~\sa PIVector::any() - inline bool any(std::function test) const { return mat.any(test); } + inline bool any(std::function test) const { return mat.any(std::move(test)); } //! \~english Tests if all elements in the flat vector pass the `test`. //! \~russian Проверяет, проходят ли все элементы в плоском векторе `test`. //! \~\sa PIVector::every() - inline bool every(std::function test) const { return mat.every(test); } + inline bool every(std::function test) const { return mat.every(std::move(test)); } //! \~english Fills the entire 2D array with copies of `e`. //! \~russian Заполняет весь двумерный массив копиями `e`. @@ -1132,7 +1132,7 @@ public: //! \~russian Заполняет весь двумерный массив, используя функцию-генератор `f` на основе плоского индекса. //! \~\sa PIVector::fill(std::function) inline PIVector2D & fill(std::function f) { - mat.fill(f); + mat.fill(std::move(f)); return *this; } @@ -1228,7 +1228,7 @@ public: //! \~\sa PIVector::map() template inline PIVector2D map(std::function f) const { - return PIVector2D(rows_, cols_, mat.template map(f)); + return PIVector2D(rows_, cols_, mat.template map(std::move(f))); } //! \~english Applies a function (with row and col indices) to each element and returns a new 2D array. @@ -1250,23 +1250,26 @@ public: //! \~russian Применяет функцию к каждой строке (с возможностью изменения). //! \~\sa forEachRow() const, PIVector::forEach() inline PIVector2D & forEachRow(std::function f) { - for (size_t r = 0; r < rows_; ++r) + for (size_t r = 0; r < rows_; ++r) { f(row(r)); + } return *this; } //! \~english Applies a function to each row (read-only). //! \~russian Применяет функцию к каждой строке (только чтение). inline void forEachRow(std::function f) const { - for (size_t r = 0; r < rows_; ++r) + for (size_t r = 0; r < rows_; ++r) { f(row(r)); + } } //! \~english Applies a function to each column (modifiable). //! \~russian Применяет функцию к каждому столбцу (с возможностью изменения). inline PIVector2D & forEachColumn(std::function f) { - for (size_t c = 0; c < cols_; ++c) + for (size_t c = 0; c < cols_; ++c) { f(col(c)); + } return *this; } @@ -1274,8 +1277,9 @@ public: //! \~russian Применяет функцию к каждому столбцу (только чтение). //! \param f Function taking a \a ColConst. inline void forEachColumn(std::function f) const { - for (size_t c = 0; c < cols_; ++c) + for (size_t c = 0; c < cols_; ++c) { f(col(c)); + } } //! \~english Accumulates a value across all elements. @@ -1283,7 +1287,7 @@ public: //! \~\sa PIVector::reduce() template inline ST reduce(std::function f, const ST & initial = ST()) const { - return mat.template reduce(f, initial); + return mat.template reduce(std::move(f), initial); } //! \~english Accumulates a value across all elements with indices. diff --git a/libs/main/core/pibase.h b/libs/main/core/pibase.h index 8f6223c8..babe62ac 100644 --- a/libs/main/core/pibase.h +++ b/libs/main/core/pibase.h @@ -726,11 +726,12 @@ inline bool piDeleteSafety(T *& pointer) { //! \~english In this example "Error!" will be printed on every \b false function return. //! \~russian В данном примере будет выведен "Error!" при каждом \b false возврате из функции. class PIP_EXPORT PIScopeExitCall { + NO_COPY_CLASS(PIScopeExitCall) public: //! \~\brief //! \~english Constructor that takes a function to execute //! \~russian Конструктор, который принимает функцию для выполнения - explicit PIScopeExitCall(std::function f): func(f) {} + explicit PIScopeExitCall(std::function f): func(std::move(f)) {} //! \~\brief //! \~english Destructor that executes the function if it exists @@ -758,8 +759,6 @@ public: } private: - NO_COPY_CLASS(PIScopeExitCall) - std::function func; }; @@ -768,14 +767,14 @@ private: //! \~english Inherit from this class to make your class non-trivially copyable. //! \~russian Наследуйтесь от этого класса чтобы сделать свой класс нетривиально копируемым. struct PIP_EXPORT PINonTriviallyCopyable { - PINonTriviallyCopyable() noexcept = default; - PINonTriviallyCopyable(const PINonTriviallyCopyable &) noexcept = default; - PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept; - PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) noexcept = default; - PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) noexcept = default; + PINonTriviallyCopyable() = default; + PINonTriviallyCopyable(const PINonTriviallyCopyable &) = default; + PINonTriviallyCopyable(PINonTriviallyCopyable &&) ; + PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) = default; + PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) = default; ~PINonTriviallyCopyable() = default; }; -inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept = default; +inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) = default; //! \~\brief diff --git a/libs/main/http_server/microhttpd_server.h b/libs/main/http_server/microhttpd_server.h index e706743f..fc90b321 100644 --- a/libs/main/http_server/microhttpd_server.h +++ b/libs/main/http_server/microhttpd_server.h @@ -113,11 +113,11 @@ public: //! \~english Sets the callback that receives parsed requests and returns replies. //! \~russian Устанавливает callback, который получает разобранные запросы и возвращает ответы. - void setRequestCallback(std::function c) { callback = c; } + void setRequestCallback(std::function c) { callback = std::move(c); } //! \~english Sets the credential validator used when HTTP Basic authentication is enabled. //! \~russian Устанавливает валидатор учетных данных, используемый при включенной HTTP Basic-аутентификации. - void setBasicAuthCallback(std::function c) { callback_auth = c; } + void setBasicAuthCallback(std::function c) { callback_auth = std::move(c); } private: static void addFixedHeaders(PIHTTP::MessageMutable & msg); diff --git a/libs/main/io_devices/pibinarylog.h b/libs/main/io_devices/pibinarylog.h index f26fe5e4..38b3d21c 100644 --- a/libs/main/io_devices/pibinarylog.h +++ b/libs/main/io_devices/pibinarylog.h @@ -419,7 +419,7 @@ public: //! \~\param f //! \~english The callback function returning the next file path, or nullptr to use the internal generator. //! \~russian Функция обратного вызова, возвращающая путь к следующему файлу, или nullptr для использования внутреннего генератора. - void setFuncGetNewFilePath(std::function f) { f_new_path = f; } + void setFuncGetNewFilePath(std::function f) { f_new_path = std::move(f); } //! \~english Writes one record with explicit ID and payload. //! \~russian Записывает одну запись с явным идентификатором и данными. diff --git a/libs/main/io_devices/piethernet.cpp b/libs/main/io_devices/piethernet.cpp index 19eddf28..7cb7a1dc 100644 --- a/libs/main/io_devices/piethernet.cpp +++ b/libs/main/io_devices/piethernet.cpp @@ -527,6 +527,7 @@ bool PIEthernet::listen(bool threaded) { listen_threaded = true; server_bounded = false; server_thread_.start(server_func); + server_thread_.waitForStart(); return true; } listen_threaded = server_bounded = false; diff --git a/libs/main/io_devices/piiodevice.cpp b/libs/main/io_devices/piiodevice.cpp index 45344dd4..c3fb8b73 100644 --- a/libs/main/io_devices/piiodevice.cpp +++ b/libs/main/io_devices/piiodevice.cpp @@ -177,7 +177,7 @@ void PIIODevice::setReopenTimeout(PISystemTime timeout) { //! после каждого успешного потокового чтения. Метод должен быть //! в формате "bool func(void * data, uchar * readed, int size)" void PIIODevice::setThreadedReadSlot(ReadRetFunc func) { - func_read = func; + func_read = std::move(func); } diff --git a/libs/main/io_utils/pipacketextractor.h b/libs/main/io_utils/pipacketextractor.h index bb831a3a..7b41e0da 100644 --- a/libs/main/io_utils/pipacketextractor.h +++ b/libs/main/io_utils/pipacketextractor.h @@ -103,15 +103,15 @@ public: //! \~english Sets custom header validation callback. //! \~russian Устанавливает пользовательский callback проверки заголовка. - void setHeaderCheckSlot(PacketExtractorHeaderFunc f) { func_header = f; } + void setHeaderCheckSlot(PacketExtractorHeaderFunc f) { func_header = std::move(f); } //! \~english Sets custom payload validation callback. //! \~russian Устанавливает пользовательский callback проверки полезной нагрузки. - void setPayloadCheckSlot(PacketExtractorPayloadFunc f) { func_payload = f; } + void setPayloadCheckSlot(PacketExtractorPayloadFunc f) { func_payload = std::move(f); } //! \~english Sets custom footer validation callback. //! \~russian Устанавливает пользовательский callback проверки окончания пакета. - void setFooterCheckSlot(PacketExtractorFooterFunc f) { func_footer = f; } + void setFooterCheckSlot(PacketExtractorFooterFunc f) { func_footer = std::move(f); } //! \~english Switches packet extraction to mode "mode". diff --git a/libs/main/io_utils/piparsehelper.h b/libs/main/io_utils/piparsehelper.h index fe8e9433..2fb13c39 100644 --- a/libs/main/io_utils/piparsehelper.h +++ b/libs/main/io_utils/piparsehelper.h @@ -62,7 +62,7 @@ public: //! \~russian Связывает ключ "key" с callback-функцией "func" без аргументов полезной нагрузки. void assign(Key key, std::function func) { auto lf = [func](PIByteArray) { func(); }; - functions[key] << lf; + functions[key] << std::move(lf); } @@ -77,7 +77,7 @@ public: func(v); } }; - functions[key] << lf; + functions[key] << std::move(lf); } diff --git a/libs/main/math/pievaluator.h b/libs/main/math/pievaluator.h index c492cb7a..69fb9b75 100644 --- a/libs/main/math/pievaluator.h +++ b/libs/main/math/pievaluator.h @@ -175,7 +175,7 @@ struct PIP_EXPORT Function { identifier = name; arguments = args; type = bfCustom; - handler = h; + handler = std::move(h); } PIString identifier; diff --git a/libs/main/math/pimathvector.h b/libs/main/math/pimathvector.h index 2deaa403..d7974bdf 100644 --- a/libs/main/math/pimathvector.h +++ b/libs/main/math/pimathvector.h @@ -5,7 +5,7 @@ //! \~russian Математический вектор /* PIP - Platform Independent Primitives - Math vector + Math vector Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru This program is free software: you can redistribute it and/or modify @@ -805,11 +805,12 @@ public: //! \~english Applies \a f to every coordinate without modifying the vector. //! \~russian Применяет \a f к каждой координате без изменения вектора. - void forEach(std::function f) const { c.forEach(f); } + void forEach(std::function f) const { c.forEach(std::move(f)); } + //! \~english Applies \a f to every coordinate and returns this vector. //! \~russian Применяет \a f к каждой координате и возвращает этот вектор. _CVector & forEach(std::function f) { - c.forEach(f); + c.forEach(std::move(f)); return *this; } diff --git a/libs/main/mqtt_client/pimqttclient.cpp b/libs/main/mqtt_client/pimqttclient.cpp deleted file mode 100644 index f12f1983..00000000 --- a/libs/main/mqtt_client/pimqttclient.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* - PIP - Platform Independent Primitives - MQTT Client - Ivan Pelipenko peri4ko@yandex.ru - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . -*/ - -#include "pimqttclient.h" - -#include "MQTTClient.h" - - -STATIC_INITIALIZER_BEGIN - MQTTClient_init_options opts = MQTTClient_init_options_initializer; - MQTTClient_global_init(&opts); -STATIC_INITIALIZER_END - - -PRIVATE_DEFINITION_START(PIMQTT::Client) - MQTTClient client = nullptr; - bool connected = false; - - static void connectionLost_callback(void * context, char *) { - ((PIMQTT::Client *)context)->mqtt_connectionLost(); - } - - static void deliveryComplete_callback(void * context, MQTTClient_deliveryToken dt) { - ((PIMQTT::Client *)context)->mqtt_deliveryComplete(dt); - } - - static int messageArrived_callback(void * context, char * topicName, int topicLen, MQTTClient_message * message) { - PIMQTT::Message msg; - msg.topic = PIString::fromUTF8(topicName); - msg.payload = PIByteArray(message->payload, message->payloadlen); - msg.msg_id = message->msgid; - msg.is_duplicate = message->dup != 0; - msg.qos = static_cast(message->qos); - MQTTClient_freeMessage(&message); - MQTTClient_free(topicName); - ((PIMQTT::Client *)context)->mqtt_messageArrived(msg); - return 1; - } - -PRIVATE_DEFINITION_END(PIMQTT::Client) - - -PIMQTT::Client::Client() { - thread = new PIThread([this] { run(); }); - thread->start(); -} - - -PIMQTT::Client::~Client() { - thread->stop(); - disconnect(); - thread->waitForFinish(); - piDeleteSafety(thread); -} - - -void PIMQTT::Client::connect(const PIString & address, const PIString & client, const PIString & username, const PIString & password) { - auto ci = connect_info.getRef(); - ci->address = address; - ci->clientID = client; - ci->username = username; - ci->password = password; - changeStatus(Connecting); -} - - -void PIMQTT::Client::disconnect() { - changeStatus(Disconnecting); -} - - -void PIMQTT::Client::subscribe(const PIString & topic, QoS qos) { - sub_queue.getRef()->enqueue({topic, qos}); - notifier.notify(); -} - - -void PIMQTT::Client::unsubscribe(const PIString & topic) { - unsub_queue.getRef()->enqueue(topic); - notifier.notify(); -} - - -void PIMQTT::Client::publish(const PIString & topic, const PIByteArray & msg, QoS qos) { - Message m; - m.topic = topic; - m.payload = msg; - m.qos = qos; - pub_queue.getRef()->enqueue(m); - notifier.notify(); -} - - -void PIMQTT::Client::mqtt_connectionLost() { - piCoutObj << "mqtt_connectionLost"; - PRIVATE->connected = false; - changeStatus(Idle); - disconnected(Error::ServerUnavailable); -} - - -void PIMQTT::Client::mqtt_deliveryComplete(int token) {} - - -void PIMQTT::Client::mqtt_messageArrived(const Message & msg) { - piCoutObj << "mqtt_messageArrived"; - received(msg); -} - - -void PIMQTT::Client::connectInternal() { - destroy(); - PRIVATE->connected = false; - auto ci = connect_info.get(); - PINetworkAddress net_addr = PINetworkAddress::resolve(ci.address); - if (net_addr.port() == 0) net_addr.setPort(1883); - PIString server_uri = "tcp://" + net_addr.toString(); - MQTTClient_create(&PRIVATE->client, server_uri.dataAscii(), ci.clientID.dataUTF8(), MQTTCLIENT_PERSISTENCE_NONE, nullptr); - MQTTClient_setCallbacks(PRIVATE->client, - this, - PRIVATE->connectionLost_callback, - PRIVATE->messageArrived_callback, - PRIVATE->deliveryComplete_callback); - MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; - conn_opts.keepAliveInterval = 9; - conn_opts.cleansession = 1; - conn_opts.connectTimeout = piRound(connect_timeout.toSeconds()); - if (ci.username.isNotEmpty()) conn_opts.username = ci.username.dataAscii(); - if (ci.password.isNotEmpty()) conn_opts.password = ci.password.dataAscii(); - int ret = MQTTClient_connect(PRIVATE->client, &conn_opts); - if (ret != MQTTCLIENT_SUCCESS) { - piCoutObj << "Failed to connect to " + server_uri << ", code" << ret; - changeStatus(Idle); - if (ret < 0) ret = static_cast(Error::ServerUnavailable); - disconnected(static_cast(ret)); - return; - } - PRIVATE->connected = true; - changeStatus(Connected); - connected(); -} - - -void PIMQTT::Client::publishInternal(const Message & m) { - if (!PRIVATE->client) return; - MQTTClient_message pubmsg = MQTTClient_message_initializer; - pubmsg.payload = const_cast(static_cast(m.payload.data())); - pubmsg.payloadlen = m.payload.size_s(); - pubmsg.qos = static_cast(m.qos); - int ret = MQTTClient_publishMessage(PRIVATE->client, m.topic.dataAscii(), &pubmsg, nullptr); - if (ret != MQTTCLIENT_SUCCESS) { - piCoutObj << "Failed to publishMessage, code" << ret; - } -} - - -void PIMQTT::Client::subscribeInternal(const Subscribe & sub) { - if (!PRIVATE->client) return; - int ret = MQTTClient_subscribe(PRIVATE->client, sub.topic.dataUTF8(), static_cast(sub.qos)); - if (ret != MQTTCLIENT_SUCCESS) { - piCoutObj << "Failed to subscribe" << sub.topic << ", code" << ret; - } -} - - -void PIMQTT::Client::unsubscribeInternal(const PIString & topic) { - if (!PRIVATE->client) return; - int ret = MQTTClient_unsubscribe(PRIVATE->client, topic.dataUTF8()); - if (ret != MQTTCLIENT_SUCCESS) { - piCoutObj << "Failed to unsubscribe" << topic << ", code" << ret; - } -} - - -void PIMQTT::Client::destroy() { - if (!PRIVATE->client) return; - if (PRIVATE->connected) MQTTClient_disconnect(PRIVATE->client, 1000); - MQTTClient_destroy(&PRIVATE->client); - PRIVATE->connected = false; - PRIVATE->client = nullptr; -} - - -void PIMQTT::Client::changeStatus(Status s) { - m_status = s; - notifier.notify(); -} - - -void PIMQTT::Client::run() { - notifier.wait(); - piCoutObj << "run" << (int)m_status; - if (thread->isStopping() || m_status == Disconnecting) { - bool was_connected = PRIVATE->connected; - changeStatus(Idle); - destroy(); - if (was_connected) disconnected(Error::Unknown); - PRIVATE->connected = false; - return; - } - if (m_status == Connecting) { - connectInternal(); - return; - } - if (m_status == Connected) { - Subscribe sub; - { - auto ref = sub_queue.getRef(); - if (!ref->isEmpty()) sub = ref->dequeue(); - } - if (sub.topic.isNotEmpty()) { - subscribeInternal(sub); - notifier.notify(); - return; - } - - PIString topic; - { - auto ref = unsub_queue.getRef(); - if (!ref->isEmpty()) topic = ref->dequeue(); - } - if (topic.isNotEmpty()) { - unsubscribeInternal(topic); - notifier.notify(); - return; - } - - Message msg; - { - auto ref = pub_queue.getRef(); - if (!ref->isEmpty()) msg = ref->dequeue(); - } - if (msg.isValid()) { - publishInternal(msg); - notifier.notify(); - return; - } - } -} diff --git a/libs/main/mqtt_client/pimqttclient.h b/libs/main/mqtt_client/pimqttclient.h index 78feef76..b6865ac9 100644 --- a/libs/main/mqtt_client/pimqttclient.h +++ b/libs/main/mqtt_client/pimqttclient.h @@ -27,14 +27,16 @@ #include "piliterals_time.h" #include "pimqtttypes.h" +#include "pip_mqtt_client_export.h" #include "piprotectedvariable.h" #include "pithread.h" +#include "pithreadpoolworker.h" namespace PIMQTT { -class PIP_EXPORT Client: public PIObject { +class PIP_MQTT_CLIENT_EXPORT Client: public PIObject { PIOBJECT(PIMQTT::Client) public: @@ -103,6 +105,7 @@ private: std::atomic_int m_status = {Idle}; PISystemTime connect_timeout = 10_s; PIThread * thread = nullptr; + // PIThreadPoolWorker * worker = nullptr; }; diff --git a/libs/main/state_machine/pistatemachine.h b/libs/main/state_machine/pistatemachine.h index d391bf68..a2db754e 100644 --- a/libs/main/state_machine/pistatemachine.h +++ b/libs/main/state_machine/pistatemachine.h @@ -57,7 +57,7 @@ public: //! \~english Sets a callback invoked when the machine finishes. //! \~russian Задает callback-функцию, вызываемую при завершении машины. - void setOnFinish(std::function f) { on_finish = f; } + void setOnFinish(std::function f) { on_finish = std::move(f); } //! \~english Posts an event to active states and triggers the first matching transition. diff --git a/libs/main/state_machine/pistatemachine_base.h b/libs/main/state_machine/pistatemachine_base.h index 60afe49c..661538d3 100644 --- a/libs/main/state_machine/pistatemachine_base.h +++ b/libs/main/state_machine/pistatemachine_base.h @@ -51,7 +51,7 @@ public: template FunctionBase * makeFunction(std::function func) { auto * ret = new Function(); - ret->func = func; + ret->func = std::move(func); return ret; } diff --git a/libs/main/state_machine/pistatemachine_state.h b/libs/main/state_machine/pistatemachine_state.h index 388c94ce..b79049ca 100644 --- a/libs/main/state_machine/pistatemachine_state.h +++ b/libs/main/state_machine/pistatemachine_state.h @@ -5,8 +5,8 @@ //! \~russian Объявляет состояния, используемые в PIStateMachine /* PIP - Platform Independent Primitives - State machine node - Ivan Pelipenko peri4ko@yandex.ru + State machine node + Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -196,10 +196,10 @@ class PIP_EXPORT PIStateLambda: public PIStateBase { public: //! \~english Creates a state backed by enter and exit callbacks. //! \~russian Создает состояние с callback-функциями входа и выхода. - PIStateLambda(std::function on_enter, std::function on_exit = nullptr, const PIString & n = {}): PIStateBase(n) { - enter = on_enter; - exit = on_exit; - } + PIStateLambda(std::function on_enter, std::function on_exit = nullptr, const PIString & n = {}) + : PIStateBase(n) + , enter(std::move(on_enter)) + , exit(std::move(on_exit)) {} //! \~english Executes the enter callback. @@ -228,9 +228,8 @@ class PIP_EXPORT PIStateFinal: public PIStateBase { public: //! \~english Creates a final state with an optional callback executed on entry. //! \~russian Создает финальное состояние с необязательной callback-функцией, выполняемой при входе. - PIStateFinal(std::function on_finish = nullptr, const PIString & n = {}): PIStateBase(n) { + PIStateFinal(std::function on_finish = nullptr, const PIString & n = {}): PIStateBase(n), enter(std::move(on_finish)) { is_final = true; - enter = on_finish; } diff --git a/libs/main/state_machine/pistatemachine_transition.cpp b/libs/main/state_machine/pistatemachine_transition.cpp index b59150b6..849681fb 100644 --- a/libs/main/state_machine/pistatemachine_transition.cpp +++ b/libs/main/state_machine/pistatemachine_transition.cpp @@ -35,7 +35,7 @@ PITransitionBase::~PITransitionBase() { PITransitionBase * PITransitionBase::addAction(std::function a) { - action = a; + action = std::move(a); return this; } diff --git a/libs/main/system/pisignals.h b/libs/main/system/pisignals.h index e308001c..55e920fb 100644 --- a/libs/main/system/pisignals.h +++ b/libs/main/system/pisignals.h @@ -68,7 +68,7 @@ public: //! \~english Installs callback that receives grabbed signals. //! \~russian Устанавливает обратный вызов, получающий перехваченные сигналы. - static void setSlot(SignalEvent slot) { ret_func = slot; } + static void setSlot(SignalEvent slot) { ret_func = std::move(slot); } //! \~english Redirects selected signals to the slot set by \a setSlot(). //! \~russian Перенаправляет выбранные сигналы в обработчик, заданный через \a setSlot(). diff --git a/libs/main/thread/piconditionvar.cpp b/libs/main/thread/piconditionvar.cpp index 0c6412c4..753b2cfc 100644 --- a/libs/main/thread/piconditionvar.cpp +++ b/libs/main/thread/piconditionvar.cpp @@ -86,11 +86,9 @@ void PIConditionVariable::wait(PIMutex & lk) { } -void PIConditionVariable::wait(PIMutex & lk, const std::function & condition) { - bool isCondition; +void PIConditionVariable::wait(PIMutex & lk, std::function condition) { while (true) { - isCondition = condition(); - if (isCondition) break; + if (condition()) break; #if defined(WINDOWS) SleepConditionVariableCS(&PRIVATE->nativeHandle, (PCRITICAL_SECTION)lk.handle(), INFINITE); #elif defined(FREERTOS) @@ -122,8 +120,7 @@ bool PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout) { } -bool PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout, const std::function & condition) { - bool isCondition; +bool PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout, std::function condition) { #if defined(WINDOWS) || defined(FREERTOS) PITimeMeasurer measurer; #else @@ -135,8 +132,7 @@ bool PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout, const std: xEventGroupClearBits(PRIVATE->nativeHandle, 1); #endif while (true) { - isCondition = condition(); - if (isCondition) break; + if (condition()) break; bool isTimeout; #if defined(WINDOWS) isTimeout = SleepConditionVariableCS(&PRIVATE->nativeHandle, diff --git a/libs/main/thread/piconditionvar.h b/libs/main/thread/piconditionvar.h index 4487edea..25626a09 100644 --- a/libs/main/thread/piconditionvar.h +++ b/libs/main/thread/piconditionvar.h @@ -106,7 +106,7 @@ public: //! \param condition вызываемый объект или функция, не принимающая аргументов и возвращающая значение, которое может быть оценено как //! bool. Вызывается повторно, пока не примет значение true //! - virtual void wait(PIMutex & lk, const std::function & condition); + virtual void wait(PIMutex & lk, std::function condition); //! \~english Waits for at most \a timeout and returns \c true if awakened before it expires. @@ -167,7 +167,7 @@ public: //! bool. Вызывается повторно, пока не примет значение true //! \return false если достигнут таймаут, или true если условие пробуждения истинно //! - virtual bool waitFor(PIMutex & lk, PISystemTime timeout, const std::function & condition); + virtual bool waitFor(PIMutex & lk, PISystemTime timeout, std::function condition); private: PRIVATE_DECLARATION(PIP_EXPORT) diff --git a/libs/main/thread/piprotectedvariable.h b/libs/main/thread/piprotectedvariable.h index 5390fb5d..754eb4e3 100644 --- a/libs/main/thread/piprotectedvariable.h +++ b/libs/main/thread/piprotectedvariable.h @@ -4,22 +4,22 @@ //! \~english Thread-safe variable //! \~russian Потокобезопасная переменная /* - PIP - Platform Independent Primitives - Thread-safe variable - Ivan Pelipenko peri4ko@yandex.ru + PIP - Platform Independent Primitives + Thread-safe variable + Ivan Pelipenko peri4ko@yandex.ru - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ #ifndef PIPROTECTEDVARIABLE_H @@ -34,21 +34,29 @@ template class PIP_EXPORT PIProtectedVariable { public: + //! \~english Constructs %PIProtectedVariable and initialize variable by value `v`. + //! \~russian Создает %PIProtectedVariable и инициализирует переменную значением `v`. + PIProtectedVariable(T v = T()): var(std::move(v)) {} + //! \~\brief //! \~english Pointer-like wrapper returned by \a getRef() while the protected value remains locked. //! \~russian Указателеподобная обертка, возвращаемая \a getRef(), пока защищенное значение остается заблокированным. class PIP_EXPORT Pointer { friend class PIProtectedVariable; + NO_COPY_CLASS(Pointer); + Pointer & operator=(Pointer && other) = delete; public: - //! \~english Copies wrapper state for access to the same protected value. - //! \~russian Копирует состояние обертки для доступа к тому же защищенному значению. - Pointer(const Pointer & v): pv(v.pv), counter(v.counter + 1) {} + //! \~english Move constructor - transfers ownership of the lock. + //! \~russian Конструктор перемещения - передает владение блокировкой. + Pointer(Pointer && other): pv(other.pv) { other.can_unlock = false; }; - //! \~english Destroys wrapper and releases the mutex when it owns the original lock. - //! \~russian Уничтожает обертку и освобождает мьютекс, когда она владеет исходной блокировкой. + //! \~english Destroys wrapper and releases the mutex. + //! \~russian Уничтожает обертку и освобождает мьютекс. ~Pointer() { - if (counter == 0) pv.mutex.unlock(); + if (can_unlock) { + pv.mutex.unlock(); + } } //! \~english Returns pointer access to the protected value. @@ -60,11 +68,11 @@ public: T & operator*() { return pv.var; } private: - Pointer() = delete; - Pointer(PIProtectedVariable & v): pv(v) {} + explicit Pointer() = delete; + explicit Pointer(PIProtectedVariable & v): pv(v) { pv.mutex.lock(); } PIProtectedVariable & pv; - int counter = 0; + bool can_unlock = true; }; //! \~english Replaces the protected value with \a v. @@ -76,10 +84,7 @@ public: //! \~english Locks the value and returns wrapper-based access to it. //! \~russian Блокирует значение и возвращает обертку для доступа к нему. - Pointer getRef() { - mutex.lock(); - return Pointer(*this); - } + Pointer getRef() { return Pointer(*this); } //! \~english Returns a copy of the protected value. //! \~russian Возвращает копию защищенного значения. @@ -98,7 +103,7 @@ public: private: mutable PIMutex mutex; - T var; + T var = {}; }; diff --git a/libs/main/thread/pithread.cpp b/libs/main/thread/pithread.cpp index 4e03b73a..0f9aa389 100644 --- a/libs/main/thread/pithread.cpp +++ b/libs/main/thread/pithread.cpp @@ -540,7 +540,7 @@ PRIVATE_DEFINITION_END(PIThread) PIThread::PIThread(void * data, ThreadFunc func, bool startNow, PISystemTime loop_delay): PIObject() { PIINTROSPECTION_THREAD_NEW(this); data_ = data; - ret_func = func; + ret_func = std::move(func); terminating = running_ = lockRun = false; priority_ = piNormal; delay_ = loop_delay; @@ -609,13 +609,13 @@ bool PIThread::start(PISystemTime loop_delay) { bool PIThread::start(ThreadFunc func) { - ret_func = func; + ret_func = std::move(func); return start(); } bool PIThread::start(ThreadFunc func, PISystemTime loop_delay) { - ret_func = func; + ret_func = std::move(func); delay_ = loop_delay; return start(); } @@ -641,7 +641,7 @@ bool PIThread::startOnce() { bool PIThread::startOnce(ThreadFunc func) { - ret_func = func; + ret_func = std::move(func); return startOnce(); } @@ -738,12 +738,12 @@ bool PIThread::_startThread(void * func) { #ifdef FREERTOS auto name_ba = createThreadName(); - if (xTaskCreate((__THREAD_FUNC_RET__ (*)(void *))func, - (const char *)name_ba.data(), // A name just for humans - 128, // This stack size can be checked & adjusted by reading the Stack Highwater - this, - priority_, - &PRIVATE->thread) == pdPASS) { + if (xTaskCreate((__THREAD_FUNC_RET__(*)(void *))func, + (const char *)name_ba.data(), // A name just for humans + 128, // This stack size can be checked & adjusted by reading the Stack Highwater + this, + priority_, + &PRIVATE->thread) == pdPASS) { tid_ = (llong)PRIVATE->thread; return true; } @@ -752,7 +752,7 @@ bool PIThread::_startThread(void * func) { if (PRIVATE->thread) CloseHandle(PRIVATE->thread); # ifdef CC_GCC - PRIVATE->thread = (void *)_beginthreadex(0, 0, (__THREAD_FUNC_RET__ (*)(void *))func, this, CREATE_SUSPENDED, 0); + PRIVATE->thread = (void *)_beginthreadex(0, 0, (__THREAD_FUNC_RET__(*)(void *))func, this, CREATE_SUSPENDED, 0); # else PRIVATE->thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)func, this, CREATE_SUSPENDED, 0); # endif @@ -766,7 +766,7 @@ bool PIThread::_startThread(void * func) { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - int ret = pthread_create(&PRIVATE->thread, &attr, (__THREAD_FUNC_RET__ (*)(void *))func, this); + int ret = pthread_create(&PRIVATE->thread, &attr, (__THREAD_FUNC_RET__(*)(void *))func, this); pthread_attr_destroy(&attr); // PICout(PICoutManipulators::DefaultControls) << "pthread_create" << PRIVATE->thread; // piCout << "started" << PRIVATE->thread; @@ -884,8 +884,8 @@ void PIThread::_runThread() { PIINTROSPECTION_THREAD_RUN(this); // PICout(PICoutManipulators::DefaultControls) << "thread" << this << "lock" << "..."; if (lockRun) thread_mutex.lock(); - // PICout(PICoutManipulators::DefaultControls) << "thread" << this << "lock" << "ok"; - // PICout(PICoutManipulators::DefaultControls) << "thread" << this << "run" << "..."; + // PICout(PICoutManipulators::DefaultControls) << "thread" << this << "lock" << "ok"; + // PICout(PICoutManipulators::DefaultControls) << "thread" << this << "run" << "..."; #ifdef PIP_INTROSPECTION PITimeMeasurer _tm; #endif @@ -1042,7 +1042,7 @@ void PIThread::runOnce(PIObject * object, const char * handler, const PIString & void PIThread::runOnce(std::function func, const PIString & name) { PIThread * t = new PIThread(); t->setName(name); - t->setSlot(func); + t->setSlot(std::move(func)); #ifndef MICRO_PIP __PIThreadCollection::instance()->startedAuto(t); CONNECT0(void, t, stopped, __PIThreadCollection::instance(), stoppedAuto); diff --git a/libs/main/thread/pithread.h b/libs/main/thread/pithread.h index 2c80e367..102b790c 100644 --- a/libs/main/thread/pithread.h +++ b/libs/main/thread/pithread.h @@ -217,6 +217,7 @@ public: //! \~english Waits until the thread starts. Returns \b false if the timeout expires first. //! \~russian Ожидает запуска потока. Возвращает \b false, если таймаут истек раньше. bool waitForStart(PISystemTime timeout = {}); + //! \~english Deprecated overload of \a waitForStart() that accepts milliseconds. //! \~russian Устаревшая перегрузка \a waitForStart(), принимающая миллисекунды. bool waitForStart(int timeout_msecs) DEPRECATEDM("use waitForStart(PISystemTime)") { @@ -226,6 +227,7 @@ public: //! \~english Waits for thread completion. Returns \b false if the timeout expires first. //! \~russian Ожидает завершения потока. Возвращает \b false, если таймаут истек раньше. bool waitForFinish(PISystemTime timeout = {}); + //! \~english Deprecated overload of \a waitForFinish() that accepts milliseconds. //! \~russian Устаревшая перегрузка \a waitForFinish(), принимающая миллисекунды. bool waitForFinish(int timeout_msecs) DEPRECATEDM("use waitForFinish(PISystemTime)") { diff --git a/libs/main/thread/pithreadmodule.h b/libs/main/thread/pithreadmodule.h index 55ce08ab..15557d1d 100644 --- a/libs/main/thread/pithreadmodule.h +++ b/libs/main/thread/pithreadmodule.h @@ -71,8 +71,8 @@ #include "pispinlock.h" #include "pithread.h" #include "pithreadnotifier.h" -#include "pithreadpoolexecutor.h" #include "pithreadpoolloop.h" +#include "pithreadpoolworker.h" #include "pitimer.h" #endif // PITHREADMODULE_H diff --git a/libs/main/thread/pithreadpoolexecutor.cpp b/libs/main/thread/pithreadpoolexecutor.cpp deleted file mode 100644 index 55d3ae29..00000000 --- a/libs/main/thread/pithreadpoolexecutor.cpp +++ /dev/null @@ -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 . -*/ - -#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()); - 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 & runnable) { - if (!isShutdown_) taskQueue.offer(runnable); -} - - -bool PIThreadPoolExecutor::isShutdown() const { - return isShutdown_; -} - - -void PIThreadPoolExecutor::shutdown() { - isShutdown_ = true; -} diff --git a/libs/main/thread/pithreadpoolexecutor.h b/libs/main/thread/pithreadpoolexecutor.h index a94cd4e5..c5e78142 100644 --- a/libs/main/thread/pithreadpoolexecutor.h +++ b/libs/main/thread/pithreadpoolexecutor.h @@ -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 . -*/ - #ifndef PITHREADPOOLEXECUTOR_H #define PITHREADPOOLEXECUTOR_H -#include "piblockingqueue.h" -#include "pithread.h" +#include "pithreadpoolworker.h" -#include +namespace { +DEPRECATEDM("Use pithreadpoolworker.h") const bool deprecated_header_is_used = true; +} - -//! \~\ingroup Thread -//! \~\brief -//! \~english Fixed-size pool of worker threads for fire-and-forget tasks. -//! \~russian Фиксированный пул рабочих потоков для задач без ожидания результата. -class PIP_EXPORT PIThreadPoolExecutor { +class DEPRECATEDM("Use PIThreadPoolWorker") PIThreadPoolExecutor: public PIThreadPoolWorker { public: - //! \~english Constructs executor with \a corePoolSize worker threads. - //! \~russian Создает исполнитель с \a corePoolSize рабочими потоками. - explicit PIThreadPoolExecutor(int corePoolSize); + PIThreadPoolExecutor(int threads_count): PIThreadPoolWorker(threads_count) { start(); } + ~PIThreadPoolExecutor() { stopAndWait(); } - //! \~english Stops worker threads and destroys executor resources. - //! \~russian Останавливает рабочие потоки и уничтожает ресурсы исполнителя. - virtual ~PIThreadPoolExecutor(); - - //! \~\brief - //! \~english Submits \a runnable for asynchronous execution by a worker thread. - //! \~russian Передает \a runnable на асинхронное выполнение рабочим потоком. - //! \details - //! \~english - //! This is a best-effort fire-and-forget call and does not report whether the task was accepted. - //! \After shutdown requests new tasks are ignored. - //! \~russian - //! Это вызов по принципу best-effort без ожидания результата и без сообщения о том, была ли задача принята. - //! \После запроса на завершение новые задачи игнорируются. - void execute(const std::function & 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> taskQueue; - PIVector threadPool; + void execute(std::function runnable) DEPRECATEDM("Use enqueueTask()") { enqueueTask(runnable); } + void shutdownNow() DEPRECATEDM("Use stopAndWait()") { stopAndWait(); } + void shutdown() DEPRECATEDM("Use stop()") { stop(); } + bool isShutdown() const DEPRECATEDM("Use !isRunning()") { return !isRunning(); } + bool awaitTermination(PISystemTime timeout) DEPRECATEDM("Use waitForFinish()") { return waitForFinish(timeout); } }; -#endif // PITHREADPOOLEXECUTOR_H + +#endif diff --git a/libs/main/thread/pithreadpoolloop.cpp b/libs/main/thread/pithreadpoolloop.cpp index b8e1494f..41367adc 100644 --- a/libs/main/thread/pithreadpoolloop.cpp +++ b/libs/main/thread/pithreadpoolloop.cpp @@ -136,7 +136,7 @@ PIThreadPoolLoop::~PIThreadPoolLoop() { void PIThreadPoolLoop::setFunction(std::function 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 f) { - setFunction(f); + setFunction(std::move(f)); exec(index_start, index_count); } diff --git a/libs/main/thread/pithreadpoolworker.cpp b/libs/main/thread/pithreadpoolworker.cpp new file mode 100644 index 00000000..26032f12 --- /dev/null +++ b/libs/main/thread/pithreadpoolworker.cpp @@ -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 . +*/ + +#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 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([](const Task & t) { return t.context; }); + qref->removeWhere([context](const Task & t) { return t.context == context; }); + // piCout << prev_size << qref->size() << qref->map([](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(); +} diff --git a/libs/main/thread/pithreadpoolworker.h b/libs/main/thread/pithreadpoolworker.h new file mode 100644 index 00000000..295ea6ea --- /dev/null +++ b/libs/main/thread/pithreadpoolworker.h @@ -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 . +*/ + +#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 func, PIObject * context = nullptr); + + //! \~english Queue functor to execution. Returns task ID. + //! \~russian Запланировать функтор на выполнение. Возвращает ID задачи. + int64_t enqueueTask(std::function 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 + 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(obj) : nullptr); + } + + //! \~english Queue class member method to execution. Returns task ID. + //! \~russian Запланировать член-метод класса на выполнение. Возвращает ID задачи. + template + int64_t enqueueTask(O * obj, void (O::*member_func)()) { + return enqueueTask([obj, member_func](int64_t) { (obj->*member_func)(); }, + PIObject::isPIObject(obj) ? dynamic_cast(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 func = nullptr; + int64_t id = -1; + }; + + void threadFunc(Worker * w); + + mutable PIVector workers; + mutable PIProtectedVariable> tasks_queue; + + PISet contexts; + std::atomic_int64_t next_task_id = {0}; +}; + + +#endif // PITHREADPOOLWORKER_H diff --git a/libs/main/thread/pitimer.cpp b/libs/main/thread/pitimer.cpp index 84a72e6f..8cb72a22 100644 --- a/libs/main/thread/pitimer.cpp +++ b/libs/main/thread/pitimer.cpp @@ -122,13 +122,13 @@ PITimer::PITimer(): PIObject() { PITimer::PITimer(std::function func) { initFirst(); - ret_func = func; + ret_func_delim = std::move(func); } PITimer::PITimer(std::function func) { initFirst(); - ret_func = [func](int) { func(); }; + ret_func = std::move(func); } @@ -224,7 +224,8 @@ void PITimer::adjustTimes() { void PITimer::execTick() { if (!isRunning()) return; if (lockRun) lock(); - if (ret_func) ret_func(1); + if (ret_func) ret_func(); + if (ret_func_delim) ret_func_delim(1); tick(1); tickEvent(1); if (callEvents) maybeCallQueuedEvents(); @@ -233,8 +234,8 @@ void PITimer::execTick() { i.tick = 0; if (i.func) i.func(i.delim); - else if (ret_func) - ret_func(i.delim); + else if (ret_func_delim) + ret_func_delim(i.delim); tick(i.delim); tickEvent(i.delim); } @@ -262,7 +263,7 @@ bool PITimer::start(PISystemTime interval) { bool PITimer::start(PISystemTime interval, std::function func) { if (isRunning()) stopAndWait(); setInterval(interval); - setSlot(func); + setSlot(std::move(func)); return start(); } @@ -273,8 +274,20 @@ void PITimer::stopAndWait(PISystemTime timeout) { } +void PITimer::setSlot(std::function func) { + ret_func_delim = nullptr; + ret_func = std::move(func); +} + + +void PITimer::setSlot(std::function func) { + ret_func = nullptr; + ret_func_delim = std::move(func); +} + + void PITimer::addDelimiter(int delim, std::function func) { - delims << Delimiter(func, delim); + delims << Delimiter(std::move(func), delim); } diff --git a/libs/main/thread/pitimer.h b/libs/main/thread/pitimer.h index d3c1c4d4..3ff367a8 100644 --- a/libs/main/thread/pitimer.h +++ b/libs/main/thread/pitimer.h @@ -110,11 +110,6 @@ public: //! \~english Deprecated overload of \a start() that accepts milliseconds. //! \~russian Устаревшая перегрузка \a start(), принимающая миллисекунды. bool start(double interval_ms) DEPRECATEDM("use start(PISystemTime)") { return start(PISystemTime::fromMilliseconds(interval_ms)); } - EVENT_HANDLER0(bool, start); - - EVENT_HANDLER0(bool, restart); - - EVENT_HANDLER0(void, stop); //! \~english Deprecated overload of \a stopAndWait() that accepts milliseconds. //! \~russian Устаревшая перегрузка \a stopAndWait(), принимающая миллисекунды. @@ -126,22 +121,19 @@ public: //! \~english Sets a tick callback that ignores the delimiter value. //! \~russian Устанавливает обратный вызов тика, игнорирующий значение делителя. - void setSlot(std::function func) { - ret_func = [func](int) { func(); }; - } + void setSlot(std::function func); //! \~english Sets a tick callback that receives the current delimiter value. //! \~russian Устанавливает обратный вызов тика, принимающий текущее значение делителя. - void setSlot(std::function func) { ret_func = func; } + void setSlot(std::function func); //! \~english Enables locking of the internal mutex around tick processing. //! \~russian Включает блокировку внутреннего мьютекса вокруг обработки тиков. void needLockRun(bool need) { lockRun = need; } - EVENT_HANDLER0(void, lock) { mutex_.lock(); } - EVENT_HANDLER0(void, unlock) { mutex_.unlock(); } //! \~english Returns whether the timer drains queued delivery for itself as performer on each main tick. By default \b true. - //! \~russian Возвращает, должен ли таймер обрабатывать отложенную доставку для себя как исполнителя на каждом основном тике. По умолчанию \b true. + //! \~russian Возвращает, должен ли таймер обрабатывать отложенную доставку для себя как исполнителя на каждом основном тике. По + //! умолчанию \b true. bool isCallQueuedEvents() const { return callEvents; } //! \~english Enables or disables queued-delivery draining through \a maybeCallQueuedEvents() on each main tick. @@ -164,9 +156,6 @@ public: //! \~russian Удаляет все делители со значением \a delim. void removeDelimiter(int delim); - EVENT_HANDLER0(void, clearDelimiters) { delims.clear(); } - - EVENT1(tickEvent, int, delimiter); //! \handlers //! \{ @@ -175,31 +164,37 @@ public: //! \brief //! \~english Starts the timer with the current \a interval(). //! \~russian Запускает таймер с текущим значением \a interval(). + EVENT_HANDLER0(bool, start); //! \fn bool restart() //! \brief //! \~english Stops the timer, then starts it again with the current \a interval(). //! \~russian Останавливает таймер, затем снова запускает его с текущим значением \a interval(). + EVENT_HANDLER0(bool, restart); //! \fn bool stop() //! \brief //! \~english Requests stop and wakes the timer thread, but does not wait for completion. //! \~russian Запрашивает остановку и пробуждает поток таймера, но не ожидает завершения. + EVENT_HANDLER0(void, stop); //! \fn void clearDelimiters() //! \brief //! \~english Remove all frequency delimiters //! \~russian Удаляет все делители частоты + EVENT_HANDLER0(void, clearDelimiters) { delims.clear(); } //! \fn void lock() //! \brief //! \~english Locks the internal timer mutex. //! \~russian Блокирует внутренний мьютекс таймера. + EVENT_HANDLER0(void, lock) { mutex_.lock(); } //! \fn void unlock() //! \brief //! \~english Unlocks the internal timer mutex. //! \~russian Разблокирует внутренний мьютекс таймера. + EVENT_HANDLER0(void, unlock) { mutex_.unlock(); } //! \} //! \events @@ -214,14 +209,14 @@ public: //! "delimiter" is the frequency delimiter, 1 for the main loop. //! \~russian //! "delimiter" - делитель частоты, 1 для основного цикла. - + EVENT1(tickEvent, int, delimiter); //! \} protected: struct PIP_EXPORT Delimiter { Delimiter(std::function func_ = nullptr, int delim_ = 1) { - func = func_; + func = std::move(func_); delim = delim_; } std::function func; @@ -245,7 +240,8 @@ protected: PIMutex mutex_; PISystemTime m_interval, m_interval_x5; PISystemTime m_time_next; - std::function ret_func = nullptr; + std::function ret_func = nullptr; + std::function ret_func_delim = nullptr; PIVector delims; PIConditionVariable event; }; diff --git a/libs/main/types/pibytearray.h b/libs/main/types/pibytearray.h index c519b152..5d0ada7f 100644 --- a/libs/main/types/pibytearray.h +++ b/libs/main/types/pibytearray.h @@ -198,7 +198,7 @@ public: //! Метод возвращает **false** при любом условии для пустого массива. //! \~\details //! \~\sa \a every(), \a contains(), \a entries(), \a forEach() - inline bool any(std::function test) const { return d.any(test); } + inline bool any(std::function test) const { return d.any(std::move(test)); } //! \~english Tests whether all elements in the array passes the test //! implemented by the provided function `test`. @@ -213,7 +213,7 @@ public: //! Метод возвращает **true** при любом условии для пустого массива. //! \~\details //! \~\sa \a any(), \a contains(), \a entries(), \a forEach() - inline bool every(std::function test) const { return d.every(test); } + inline bool every(std::function test) const { return d.every(std::move(test)); } //! \~english Full access to element by `index`. //! \~russian Полный доступ к элементу по индексу `index`. @@ -340,7 +340,7 @@ public: //! Обратите внимание: если индекс отрицателен, массив всё равно просматривается от начала к концу. //! Значение по умолчанию равно 0, что означает, что просматривается весь массив. //! \~\sa \a every(), \a any(), \a contains(), \a indexWhere() - inline int entries(std::function test, ssize_t start = 0) const { return d.entries(test, start); } + inline int entries(std::function test, ssize_t start = 0) const { return d.entries(std::move(test), start); } bool startsWith(const PIByteArray & o) const; @@ -405,7 +405,9 @@ public: //! piCout << v.indexWhere([](const uchar & s){return s > 10;}); // -1 //! \endcode //! \~\sa \a indexOf(), \a lastIndexOf(), \a lastIndexWhere(), \a contains() - inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { return d.indexWhere(test, start); } + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { + return d.indexWhere(std::move(test), start); + } //! \~english Returns the last index at which a given element `e` //! can be found in the array, or `-1` if it is not present. @@ -469,7 +471,7 @@ public: //! и означает, что просматривается весь массив. //! \~\sa \a indexOf(), \a lastIndexOf(), \a indexWhere(), \a contains() inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { - return d.lastIndexWhere(test, start); + return d.lastIndexWhere(std::move(test), start); } //! \~english Pointer to array @@ -525,7 +527,7 @@ public: //! \~\details //! \~\sa \a resize() inline PIByteArray & fill(std::function f) { - d.fill(f); + d.fill(std::move(f)); return *this; } @@ -570,7 +572,7 @@ public: //! лишние элементы удаляются с конца массива. //! \~\sa \a size(), \a clear() inline PIByteArray & resize(size_t new_size, std::function f) { - d.resize(new_size, f); + d.resize(new_size, std::move(f)); return *this; } @@ -738,7 +740,7 @@ public: //! \~\details //! \~\sa \a remove(), \a removeOne(), \a removeWhere() inline PIByteArray & removeWhere(std::function test) { - d.removeWhere(test); + d.removeWhere(std::move(test)); return *this; } @@ -950,7 +952,7 @@ public: //! piCout << v2; // {3, 5, 7} //! \endcode //! \~\sa \a map(), \a any(), \a every() - inline PIByteArray filter(std::function test) const { return PIByteArray(d.filter(test)); } + inline PIByteArray filter(std::function test) const { return PIByteArray(d.filter(std::move(test))); } //! \~english Execute function `void f(const uchar & e)` for every element in array. //! \~russian Выполняет функцию `void f(const uchar & e)` для каждого элемента массива. @@ -966,7 +968,7 @@ public: //! piCout << s; // 15 //! \endcode //! \~\sa \a filter(), \a map(), \a reduce(), \a any(), \a every() - inline void forEach(std::function f) const { d.forEach(f); } + inline void forEach(std::function f) const { d.forEach(std::move(f)); } //! \~english Execute function `void f(uchar & e)` for every element in array. //! \~russian Выполняет функцию `void f(uchar & e)` для каждого элемента массива. @@ -982,7 +984,7 @@ public: //! \endcode //! \~\sa \a filter(), \a map(), \a reduce(), \a any(), \a every() inline PIByteArray & forEach(std::function f) { - d.forEach(f); + d.forEach(std::move(f)); return *this; } @@ -1005,7 +1007,7 @@ public: //! \~\sa \a forEach(), \a reduce() template inline PIDeque map(std::function f) const { - return d.map(f); + return d.map(std::move(f)); } //! \~english Applies the function `ST f(const uchar & e, const ST & acc)` @@ -1051,7 +1053,7 @@ public: //! \~\sa \a forEach(), \a map() template inline ST reduce(std::function f, const ST & initial = ST()) const { - return d.reduce(f, initial); + return d.reduce(std::move(f), initial); } //! \~english Convert data to Base 64 and return this byte array @@ -1103,9 +1105,18 @@ public: //! \~english Add to the end byte array "data" //! \~russian Добавляет в конец массива содержимое массива "data" PIByteArray & append(const PIByteArray & data_) { +#ifdef CC_GCC +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstringop-overflow" +# pragma GCC diagnostic ignored "-Warray-bounds" +# pragma GCC diagnostic ignored "-Wrestrict" +#endif const size_t ps = size(); enlarge(data_.size_s()); memcpy(data(ps), data_.data(), data_.size()); +#ifdef CC_GCC +# pragma GCC diagnostic pop +#endif return *this; } diff --git a/libs/main/types/pisystemtime.cpp b/libs/main/types/pisystemtime.cpp index 4d218968..f26b7bd6 100644 --- a/libs/main/types/pisystemtime.cpp +++ b/libs/main/types/pisystemtime.cpp @@ -105,7 +105,7 @@ extern clock_serv_t __pi_mac_clock; //! Используйте этот метод для ожидания разниц системных времен или своего времени. //! Если метод будет вызван для системного времени \a PISystemTime::current(), то //! ожидание будет почти бесконечным -void PISystemTime::sleep() { +void PISystemTime::sleep() const { piUSleep(piFloord(toMicroseconds())); } @@ -118,7 +118,7 @@ void PISystemTime::toTimespec(void * ts) { } -PISystemTime::Frequency PISystemTime::toFrequency() { +PISystemTime::Frequency PISystemTime::toFrequency() const { return PISystemTime::Frequency::fromSystemTime(*this); } diff --git a/libs/main/types/pisystemtime.h b/libs/main/types/pisystemtime.h index bb197377..c7438c7e 100644 --- a/libs/main/types/pisystemtime.h +++ b/libs/main/types/pisystemtime.h @@ -253,7 +253,7 @@ public: //! \~english Sleep for this time //! \~russian Ожидать это время - void sleep(); + void sleep() const; //! \~english On *nix system assign current value to timespec struct //! \~russian На *nix системах присваивает время к timespec структуре @@ -261,7 +261,7 @@ public: //! \~english Returns \a Frequency that corresponds this time interval //! \~russian Возвращает \a Frequency соответствующую этому временному интервалу - PISystemTime::Frequency toFrequency(); + PISystemTime::Frequency toFrequency() const; //! \~english Returns "yyyy-MM-dd hh:mm:ss.zzz" for absolute time and " ..." for relative //! \~russian Возвращает "yyyy-MM-dd hh:mm:ss.zzz" для абсолютного времени и " ..." для относительного diff --git a/libs/main/types/pivaluetree.cpp b/libs/main/types/pivaluetree.cpp index b21f6279..a02ce13c 100644 --- a/libs/main/types/pivaluetree.cpp +++ b/libs/main/types/pivaluetree.cpp @@ -262,7 +262,7 @@ PIValueTree & PIValueTree::remove(const PIString & name) { void PIValueTree::forEachRecursive(std::function func) { - forEachRecursiveInternal(func); + forEachRecursiveInternal(std::move(func)); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a5ba75e2..4a1a80ef 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,6 +13,7 @@ FetchContent_Declare( ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +set(BUILD_GMOCK OFF CACHE BOOL "Build Google Mock" FORCE) FetchContent_MakeAvailable(googletest) enable_testing() @@ -24,16 +25,18 @@ macro(pip_test NAME) file(GLOB _HDRS "${NAME}/*.h") set(_target pip_${NAME}_test) add_executable(${_target} ${_CPPS} ${_HDRS}) - target_link_libraries(${_target} pip ${ARGN} gtest_main) - gtest_discover_tests(${_target}) + target_link_libraries(${_target} pip ${ARGN} gtest_main) + if (TESTS_RUN) + gtest_discover_tests(${_target}) + endif() list(APPEND PIP_TESTS_LIST "${NAME}") set(PIP_TESTS_LIST ${PIP_TESTS_LIST} PARENT_SCOPE) endmacro() -#pip_test(concurrent) pip_test(math) pip_test(core) pip_test(piobject) pip_test(client_server pip_client_server) pip_test(io) pip_test(system) +pip_test(thread) diff --git a/tests/client_server/client_server_test.cpp b/tests/client_server/client_server_test.cpp index bbe639c5..cb6fc68c 100644 --- a/tests/client_server/client_server_test.cpp +++ b/tests/client_server/client_server_test.cpp @@ -32,7 +32,8 @@ Client * createAndConnectClient() { TEST(ClientServer, OneClient) { auto const loop_timeout = 1000_ms; auto s = createServer(); - auto c = createAndConnectClient>(); + piMinSleep(); + auto c = createAndConnectClient>(); waitLoop([s]() { return s->clientsCount() > 0; }, loop_timeout); EXPECT_EQ(1, s->clientsCount()); @@ -102,10 +103,11 @@ int getClientsPings(const PIVector & clients) { TEST(ClientServer, ManyClients) { - auto const loop_timeout = 100_ms; + auto const loop_timeout = 1_s; constexpr int clients_count = 20; PIVector clients; auto s = createServer(); + piMinSleep(); piForTimes(clients_count) { clients.append(new ClientSendThread()); @@ -137,8 +139,9 @@ TEST(ClientServer, ManyClients) { for (const auto c: clients) { c->startSend(); } - (100_ms).sleep(); + waitLoop([&clients]() { return getClientsPings(clients) > clients_count * 2; }, loop_timeout); EXPECT_TRUE(getClientsPings(clients) > clients_count * 2); + waitLoop([s]() { return getServerPongs(s) > clients_count * 2; }, loop_timeout); EXPECT_TRUE(getServerPongs(s) > clients_count * 2); piDeleteAllAndClear(clients); waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout); @@ -147,7 +150,7 @@ TEST(ClientServer, ManyClients) { } TEST(ClientServer, DynamicClients) { - auto const loop_timeout = 100_ms; + auto const loop_timeout = 3_s; constexpr int clients_count = 20; PIVector clients; PIMutex clients_mutex; @@ -160,7 +163,6 @@ TEST(ClientServer, DynamicClients) { clients_mutex.lock(); clients << c; clients_mutex.unlock(); - piCout << "new client" << clients.size(); }; piForTimes(clients_count) { @@ -178,9 +180,8 @@ TEST(ClientServer, DynamicClients) { piForTimes(new_cnt) { spawnClient(); } - piCout << "+++++++"; }, - 12_Hz); + 120_Hz); deleteThread.start( [&clients, &clients_mutex]() { @@ -194,26 +195,17 @@ TEST(ClientServer, DynamicClients) { clients_mutex.unlock(); if (c) { delete c; - piCout << "remove client" << clients.size(); } } - piCout << "----------"; }, - 13_Hz); - - (2_s).sleep(); + 130_Hz); + waitLoop([s]() { return s->clientsCount() >= 10; }, loop_timeout); EXPECT_GE(s->clientsCount(), 10); - piCout << "now clients" << clients.size(); - - deleteThread.stopAndWait(); spawnThread.stopAndWait(); - - piCout << "total clients" << clients.size(); - piDeleteAllAndClear(clients); waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout); EXPECT_EQ(0, s->clientsCount()); diff --git a/tests/concurrent/BlockingDequeueUnitTest.cpp b/tests/concurrent/BlockingDequeueUnitTest.cpp deleted file mode 100644 index c0b42b9d..00000000 --- a/tests/concurrent/BlockingDequeueUnitTest.cpp +++ /dev/null @@ -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 & 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 & 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 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 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 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 dequeue(capacity); - ASSERT_TRUE(dequeue.offer(10)); -} - -TEST(BlockingDequeueUnitTest, offer_is_false_when_capacity_reach) { - size_t capacity = 1; - PIBlockingQueue 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 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 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 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 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 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 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 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 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 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 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 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 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 dequeue(capacity); - ASSERT_EQ(dequeue.capacity(), capacity); -} - -TEST(BlockingDequeueUnitTest, remainingCapacity_is_dif_of_capacity_and_size) { - size_t capacity = 2; - PIBlockingQueue 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 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 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 dequeue(capacity); - dequeue.offer(111); - dequeue.offer(111); - ASSERT_EQ(dequeue.size(), capacity); -} - -TEST(BlockingDequeueUnitTest, drainTo_is_elements_moved) { - size_t capacity = 10; - PIDeque refDeque; - for (size_t i = 0; i < capacity / 2; ++i) - refDeque.push_back(i * 10); - PIBlockingQueue blockingDequeue(refDeque); - PIDeque 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 refDeque; - for (size_t i = 0; i < capacity / 2; ++i) - refDeque.push_back(i * 10); - PIBlockingQueue blockingDequeue(refDeque); - PIDeque deque; - ASSERT_EQ(blockingDequeue.drainTo(deque), refDeque.size()); -} - -TEST(BlockingDequeueUnitTest, drainTo_is_ret_eq_to_maxCount) { - size_t capacity = 10; - PIDeque refDeque; - for (size_t i = 0; i < capacity / 2; ++i) - refDeque.push_back(i * 10); - PIBlockingQueue blockingDequeue(refDeque); - PIDeque deque; - ASSERT_EQ(blockingDequeue.drainTo(deque, refDeque.size() - 1), refDeque.size() - 1); -} diff --git a/tests/concurrent/ConditionLockIntegrationTest.cpp b/tests/concurrent/ConditionLockIntegrationTest.cpp deleted file mode 100644 index 0e1e22f0..00000000 --- a/tests/concurrent/ConditionLockIntegrationTest.cpp +++ /dev/null @@ -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()); -} diff --git a/tests/concurrent/ConditionVariableIntegrationTest.cpp b/tests/concurrent/ConditionVariableIntegrationTest.cpp deleted file mode 100644 index 4b514a94..00000000 --- a/tests/concurrent/ConditionVariableIntegrationTest.cpp +++ /dev/null @@ -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 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 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()); -} diff --git a/tests/concurrent/ExecutorIntegrationTest.cpp b/tests/concurrent/ExecutorIntegrationTest.cpp deleted file mode 100644 index 885fa9c6..00000000 --- a/tests/concurrent/ExecutorIntegrationTest.cpp +++ /dev/null @@ -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); -} diff --git a/tests/concurrent/pithreadnotifier_test.cpp b/tests/concurrent/pithreadnotifier_test.cpp deleted file mode 100644 index 4b9994e3..00000000 --- a/tests/concurrent/pithreadnotifier_test.cpp +++ /dev/null @@ -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); -} diff --git a/tests/concurrent/testutil.h b/tests/concurrent/testutil.h deleted file mode 100644 index 21f597a0..00000000 --- a/tests/concurrent/testutil.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef AWRCANFLASHER_TESTUTIL_H -#define AWRCANFLASHER_TESTUTIL_H - -#include "pithread.h" - -#include - -/** - * 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 adapterFunctionDefault; - - TestUtil(): isRunning(false) {} - - bool createThread(const std::function & fun = nullptr, PIThread * thread_ = nullptr) { - std::function 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 diff --git a/tests/math/testpimathmatrixt.cpp b/tests/math/testpimathmatrixt.cpp index 0245599c..243431ec 100644 --- a/tests/math/testpimathmatrixt.cpp +++ b/tests/math/testpimathmatrixt.cpp @@ -376,7 +376,7 @@ TEST(PIMathMatrixT_Test, invert) { matrix3 = matrix1; matrix1.invert(); 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) { diff --git a/tests/thread/piprotectedvariable_test.cpp b/tests/thread/piprotectedvariable_test.cpp new file mode 100644 index 00000000..e7facc40 --- /dev/null +++ b/tests/thread/piprotectedvariable_test.cpp @@ -0,0 +1,118 @@ +#include "piprotectedvariable.h" +#include "pistring.h" +#include "pithread.h" +#include "piliterals_time.h" + +#include "gtest/gtest.h" +#include + +// Basic functionality tests +TEST(PIProtectedVariable_Basic, BasicFunctionality) { + // Test basic set/get with different types + PIProtectedVariable pvInt; + PIProtectedVariable pvDouble; + PIProtectedVariable 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 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 pv; + + std::atomic writeCount(0); + std::atomic readCount(0); + std::atomic 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 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); +} diff --git a/utils/deploy_tool/main.cpp b/utils/deploy_tool/main.cpp index 899c9a46..69b15942 100644 --- a/utils/deploy_tool/main.cpp +++ b/utils/deploy_tool/main.cpp @@ -467,11 +467,20 @@ bool procDpkg(const PIString & l) { if (!vs.isEmpty()) { PIStringList lines = vs.split('\n').reverse(); for (auto l: lines) { - auto sl = l; - l = l.left(l.find(":")); - if (!l.isEmpty() && !l.endsWith("-cross") && !l.contains(' ')) all_deps << l; - // PICout(true) << "** found \"" << l << "\" in \"" << sl << "\""; - return true; + l = l.left(l.find(":")); + if (!l.isEmpty() && !l.contains(' ') && !l.endsWith("-cross") && !l.endsWith("-dev")) { + // PICout(true) << "** found \"" << l << "\" in \"" << sl << "\""; + all_deps << l; + 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;