Merge branch 'master' into mqtt_client
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
|||||||
/src_main/piversion.h
|
/src_main/piversion.h
|
||||||
/.svn
|
/.*
|
||||||
/doc/rtf
|
/doc/rtf
|
||||||
_unsused
|
_unsused
|
||||||
CMakeLists.txt.user*
|
CMakeLists.txt.user*
|
||||||
|
|||||||
@@ -97,8 +97,10 @@ public:
|
|||||||
{
|
{
|
||||||
int code = lua_pcall (L, nargs, nresults, msgh);
|
int code = lua_pcall (L, nargs, nresults, msgh);
|
||||||
|
|
||||||
if (code != LUABRIDGE_LUA_OK)
|
if (code != LUABRIDGE_LUA_OK) {
|
||||||
Throw (LuaException (L, code));
|
// Throw (LuaException (L, code));
|
||||||
|
assert(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -128,7 +130,8 @@ protected:
|
|||||||
private:
|
private:
|
||||||
static int throwAtPanic (lua_State* L)
|
static int throwAtPanic (lua_State* L)
|
||||||
{
|
{
|
||||||
throw LuaException (L, -1);
|
// throw LuaException (L, -1);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,8 @@ protected:
|
|||||||
{
|
{
|
||||||
if (m_stackSize == 0)
|
if (m_stackSize == 0)
|
||||||
{
|
{
|
||||||
throw std::logic_error ("Unable to continue registration");
|
std::cerr << ("Unable to continue registration");
|
||||||
|
assert(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1054,7 +1055,8 @@ public:
|
|||||||
{
|
{
|
||||||
if (m_stackSize == 1)
|
if (m_stackSize == 1)
|
||||||
{
|
{
|
||||||
throw std::logic_error ("endNamespace () called on global namespace");
|
std::cerr << ("endNamespace () called on global namespace");
|
||||||
|
assert(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert (m_stackSize > 1);
|
assert (m_stackSize > 1);
|
||||||
@@ -1150,7 +1152,8 @@ public:
|
|||||||
{
|
{
|
||||||
if (m_stackSize == 1)
|
if (m_stackSize == 1)
|
||||||
{
|
{
|
||||||
throw std::logic_error ("addProperty () called on global namespace");
|
std::cerr << ("addProperty () called on global namespace");
|
||||||
|
assert(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert (lua_istable (L, -1)); // Stack: namespace table (ns)
|
assert (lua_istable (L, -1)); // Stack: namespace table (ns)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
namespace luabridge {
|
namespace luabridge {
|
||||||
@@ -320,7 +321,8 @@ public:
|
|||||||
lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());
|
lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());
|
||||||
if (!lua_istable (L, -1))
|
if (!lua_istable (L, -1))
|
||||||
{
|
{
|
||||||
throw std::logic_error ("The class is not registered in LuaBridge");
|
std::cerr << ("The class is not registered in LuaBridge");
|
||||||
|
assert(true);
|
||||||
}
|
}
|
||||||
lua_setmetatable (L, -2);
|
lua_setmetatable (L, -2);
|
||||||
return ud;
|
return ud;
|
||||||
@@ -375,7 +377,8 @@ private:
|
|||||||
lua_rawgetp (L, LUA_REGISTRYINDEX, key);
|
lua_rawgetp (L, LUA_REGISTRYINDEX, key);
|
||||||
if (!lua_istable (L, -1))
|
if (!lua_istable (L, -1))
|
||||||
{
|
{
|
||||||
throw std::logic_error ("The class is not registered in LuaBridge");
|
std::cerr << ("The class is not registered in LuaBridge");
|
||||||
|
assert(true);
|
||||||
}
|
}
|
||||||
lua_setmetatable (L, -2);
|
lua_setmetatable (L, -2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ if (POLICY CMP0177)
|
|||||||
endif()
|
endif()
|
||||||
project(PIP)
|
project(PIP)
|
||||||
set(PIP_MAJOR 5)
|
set(PIP_MAJOR 5)
|
||||||
set(PIP_MINOR 6)
|
set(PIP_MINOR 7)
|
||||||
set(PIP_REVISION 0)
|
set(PIP_REVISION 0)
|
||||||
set(PIP_SUFFIX )
|
set(PIP_SUFFIX _alpha)
|
||||||
set(PIP_COMPANY SHS)
|
set(PIP_COMPANY SHS)
|
||||||
set(PIP_DOMAIN org.SHS)
|
set(PIP_DOMAIN org.SHS)
|
||||||
|
|
||||||
@@ -67,11 +67,13 @@ set(PIP_DLL_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE STRING "")
|
|||||||
option(ICU "ICU support for convert codepages" ${_ICU_DEFAULT})
|
option(ICU "ICU support for convert codepages" ${_ICU_DEFAULT})
|
||||||
option(STD_IOSTREAM "Building with std iostream operators support" OFF)
|
option(STD_IOSTREAM "Building with std iostream operators support" OFF)
|
||||||
option(INTROSPECTION "Build with introspection" OFF)
|
option(INTROSPECTION "Build with introspection" OFF)
|
||||||
option(TESTS "Build tests and perform their before install step" OFF)
|
option(TESTS "Build tests" OFF)
|
||||||
|
option(TESTS_RUN "Run tests before install step" OFF)
|
||||||
option(COVERAGE "Build project with coverage info" OFF)
|
option(COVERAGE "Build project with coverage info" OFF)
|
||||||
option(PIP_FFTW_F "Support fftw module for float" ON)
|
option(PIP_FFTW_F "Support fftw module for float" ON)
|
||||||
option(PIP_FFTW_L "Support fftw module for long double" ON)
|
option(PIP_FFTW_L "Support fftw module for long double" ON)
|
||||||
option(PIP_FFTW_Q "Support fftw module for quad double" OFF)
|
option(PIP_FFTW_Q "Support fftw module for quad double" OFF)
|
||||||
|
option(PIP_MANUAL_TEST "Build dev test (main.cpp)" OFF)
|
||||||
set(PIP_UTILS 1)
|
set(PIP_UTILS 1)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
@@ -96,7 +98,7 @@ set(PIP_TESTS_LIST)
|
|||||||
set(PIP_EXPORTS)
|
set(PIP_EXPORTS)
|
||||||
set(PIP_3PL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rd")
|
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})
|
foreach(_m ${PIP_SRC_MODULES})
|
||||||
set(PIP_MSG_${_m} "no")
|
set(PIP_MSG_${_m} "no")
|
||||||
string(TOUPPER "${_m}" _mu)
|
string(TOUPPER "${_m}" _mu)
|
||||||
@@ -360,10 +362,13 @@ endif()
|
|||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_definitions(-DPSAPI_VERSION=1)
|
add_definitions(-DPSAPI_VERSION=1)
|
||||||
if(${C_COMPILER} STREQUAL "cl.exe")
|
if(${C_COMPILER} STREQUAL "cl.exe")
|
||||||
set(CMAKE_CXX_FLAGS "/O2 /Ob2 /Ot /W0")
|
set(CMAKE_CXX_FLAGS "/O2 /Ob2 /Ot /W0 /EH-")
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||||
|
if (NOT DEFINED ANDROID_PLATFORM)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
|
||||||
|
endif()
|
||||||
if(DEFINED ENV{QNX_HOST} OR PIP_FREERTOS)
|
if(DEFINED ENV{QNX_HOST} OR PIP_FREERTOS)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-32")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-32")
|
||||||
endif()
|
endif()
|
||||||
@@ -382,28 +387,6 @@ endif()
|
|||||||
add_subdirectory("3rd/pcre2" EXCLUDE_FROM_ALL)
|
add_subdirectory("3rd/pcre2" EXCLUDE_FROM_ALL)
|
||||||
list(APPEND LIBS_MAIN pcre2-16-static)
|
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" "")
|
pip_module(main "${LIBS_MAIN}" "PIP main library" "" "${PIP_3PL_DIR}/BLAKE2;${PIP_3PL_DIR}/SipHash" "")
|
||||||
|
|
||||||
generate_export_header(pip)
|
generate_export_header(pip)
|
||||||
@@ -607,6 +590,34 @@ if (NOT CROSSTOOLS)
|
|||||||
endif()
|
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
|
# Test program
|
||||||
if(PIP_UTILS)
|
if(PIP_UTILS)
|
||||||
|
|
||||||
@@ -614,9 +625,10 @@ if (NOT CROSSTOOLS)
|
|||||||
#target_link_libraries(pip_plugin pip)
|
#target_link_libraries(pip_plugin pip)
|
||||||
|
|
||||||
if (NOT DEFINED ANDROID_PLATFORM)
|
if (NOT DEFINED ANDROID_PLATFORM)
|
||||||
|
if (PIP_MANUAL_TEST)
|
||||||
if(microhttpd_FOUND AND curl_FOUND)
|
if(microhttpd_FOUND AND curl_FOUND)
|
||||||
add_executable(pip_test "main.cpp")
|
add_executable(pip_test "main.cpp")
|
||||||
target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client)
|
target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client pip_mqtt_client)
|
||||||
if(sodium_FOUND)
|
if(sodium_FOUND)
|
||||||
add_executable(pip_cloud_test "main_picloud_test.cpp")
|
add_executable(pip_cloud_test "main_picloud_test.cpp")
|
||||||
target_link_libraries(pip_cloud_test pip_cloud)
|
target_link_libraries(pip_cloud_test pip_cloud)
|
||||||
@@ -624,6 +636,7 @@ if (NOT CROSSTOOLS)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
else()
|
else()
|
||||||
|
|
||||||
@@ -756,7 +769,7 @@ if ((NOT PIP_FREERTOS) AND (NOT CROSSTOOLS))
|
|||||||
find_package(Doxygen)
|
find_package(Doxygen)
|
||||||
if(DOXYGEN_FOUND)
|
if(DOXYGEN_FOUND)
|
||||||
set(DOXY_DEFINES "${PIP_EXPORTS}")
|
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)
|
string(TOUPPER "${_m}" _mdef)
|
||||||
list(APPEND DOXY_DEFINES "PIP_${_mdef}_EXPORT")
|
list(APPEND DOXY_DEFINES "PIP_${_mdef}_EXPORT")
|
||||||
endforeach()
|
endforeach()
|
||||||
@@ -845,6 +858,9 @@ if (PIP_TESTS_LIST)
|
|||||||
foreach(_test ${PIP_TESTS_LIST})
|
foreach(_test ${PIP_TESTS_LIST})
|
||||||
message(" * ${_test}")
|
message(" * ${_test}")
|
||||||
endforeach()
|
endforeach()
|
||||||
|
if (TESTS_RUN)
|
||||||
|
message("TESTS_RUN ON -> Run tests before install step")
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
message(" Tests: skip (tests off)")
|
message(" Tests: skip (tests off)")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ Create imported targets:
|
|||||||
* PIP::Lua
|
* PIP::Lua
|
||||||
* PIP::HTTPClient
|
* PIP::HTTPClient
|
||||||
* PIP::HTTPServer
|
* PIP::HTTPServer
|
||||||
|
* PIP::MQTTClient
|
||||||
|
|
||||||
These targets include directories and depends on
|
These targets include directories and depends on
|
||||||
main library
|
main library
|
||||||
@@ -25,10 +26,10 @@ include(SHSTKMacros)
|
|||||||
|
|
||||||
shstk_set_find_dirs(PIP)
|
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)
|
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")
|
#set(_bins "pip_cmg;pip_rc;deploy_tool")
|
||||||
#get_target_property(_path pip BINARY_DIR)
|
#get_target_property(_path pip BINARY_DIR)
|
||||||
#get_target_property(_path pip LIBRARY_OUTPUT_NAME)
|
#get_target_property(_path pip LIBRARY_OUTPUT_NAME)
|
||||||
@@ -100,6 +101,7 @@ set(__module_cloud Cloud )
|
|||||||
set(__module_lua Lua )
|
set(__module_lua Lua )
|
||||||
set(__module_http_client HTTPClient )
|
set(__module_http_client HTTPClient )
|
||||||
set(__module_http_server HTTPServer )
|
set(__module_http_server HTTPServer )
|
||||||
|
set(__module_mqtt_client MQTTClient )
|
||||||
|
|
||||||
foreach (_l ${__libs})
|
foreach (_l ${__libs})
|
||||||
set( __inc_${_l} "")
|
set( __inc_${_l} "")
|
||||||
|
|||||||
@@ -72,11 +72,7 @@ PIClientServer::Server::~Server() {
|
|||||||
clean_thread->waitForFinish();
|
clean_thread->waitForFinish();
|
||||||
piDeleteSafety(clean_thread);
|
piDeleteSafety(clean_thread);
|
||||||
stopServer();
|
stopServer();
|
||||||
for (auto c: clients) {
|
closeAll();
|
||||||
c->aboutDelete();
|
|
||||||
c->destroy();
|
|
||||||
delete c;
|
|
||||||
}
|
|
||||||
piDeleteSafety(tcp_server);
|
piDeleteSafety(tcp_server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ PIHTTPClient * PIHTTPClient::onFinish(std::function<void()> f) {
|
|||||||
|
|
||||||
|
|
||||||
PIHTTPClient * PIHTTPClient::onFinish(std::function<void(const PIHTTP::MessageConst &)> f) {
|
PIHTTPClient * PIHTTPClient::onFinish(std::function<void(const PIHTTP::MessageConst &)> f) {
|
||||||
on_finish = f;
|
on_finish = std::move(f);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +258,7 @@ PIHTTPClient * PIHTTPClient::onError(std::function<void()> f) {
|
|||||||
|
|
||||||
|
|
||||||
PIHTTPClient * PIHTTPClient::onError(std::function<void(const PIHTTP::MessageConst &)> f) {
|
PIHTTPClient * PIHTTPClient::onError(std::function<void(const PIHTTP::MessageConst &)> f) {
|
||||||
on_error = f;
|
on_error = std::move(f);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +269,7 @@ PIHTTPClient * PIHTTPClient::onAbort(std::function<void()> f) {
|
|||||||
|
|
||||||
|
|
||||||
PIHTTPClient * PIHTTPClient::onAbort(std::function<void(const PIHTTP::MessageConst &)> f) {
|
PIHTTPClient * PIHTTPClient::onAbort(std::function<void(const PIHTTP::MessageConst &)> f) {
|
||||||
on_abort = f;
|
on_abort = std::move(f);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ bool PIHTTPServer::registerPath(const PIString & path, PIHTTP::Method method, Re
|
|||||||
Endpoint ep;
|
Endpoint ep;
|
||||||
if (!ep.create(path)) return false;
|
if (!ep.create(path)) return false;
|
||||||
ep.method = method;
|
ep.method = method;
|
||||||
ep.function = functor;
|
ep.function = std::move(functor);
|
||||||
endpoints[ep.priority] << ep;
|
endpoints[ep.priority] << ep;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ public:
|
|||||||
|
|
||||||
//! \~english Sets the factory used to create accepted client objects.
|
//! \~english Sets the factory used to create accepted client objects.
|
||||||
//! \~russian Устанавливает фабрику, создающую объекты принятых клиентов.
|
//! \~russian Устанавливает фабрику, создающую объекты принятых клиентов.
|
||||||
void setClientFactory(std::function<ServerClient *()> f) { client_factory = f; }
|
void setClientFactory(std::function<ServerClient *()> f) { client_factory = std::move(f); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void newClient(ServerClient * c);
|
void newClient(ServerClient * c);
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ public:
|
|||||||
|
|
||||||
//! \~english Sets the callback receiving both key event and user data.
|
//! \~english Sets the callback receiving both key event and user data.
|
||||||
//! \~russian Устанавливает обратный вызов, получающий событие клавиши и пользовательские данные.
|
//! \~russian Устанавливает обратный вызов, получающий событие клавиши и пользовательские данные.
|
||||||
void setSlot(KBFunc slot) { ret_func = slot; }
|
void setSlot(KBFunc slot) { ret_func = std::move(slot); }
|
||||||
|
|
||||||
//! \~english Sets the callback that only receives the key event and ignores user data.
|
//! \~english Sets the callback that only receives the key event and ignores user data.
|
||||||
//! \~russian Устанавливает обратный вызов, получающий только событие клавиши и игнорирующий пользовательские данные.
|
//! \~russian Устанавливает обратный вызов, получающий только событие клавиши и игнорирующий пользовательские данные.
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ public:
|
|||||||
//! \endcode
|
//! \endcode
|
||||||
inline PIDeque(size_t piv_size, std::function<T(size_t i)> f) {
|
inline PIDeque(size_t piv_size, std::function<T(size_t i)> f) {
|
||||||
PIINTROSPECTION_CONTAINER_NEW(T, sizeof(T))
|
PIINTROSPECTION_CONTAINER_NEW(T, sizeof(T))
|
||||||
expand(piv_size, f);
|
expand(piv_size, std::move(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \~english Move constructor.
|
//! \~english Move constructor.
|
||||||
@@ -729,7 +729,7 @@ public:
|
|||||||
//! заданному в передаваемой функции `test`, или `def` если такого элемента нет.
|
//! заданному в передаваемой функции `test`, или `def` если такого элемента нет.
|
||||||
//! \~\sa \a indexWhere()
|
//! \~\sa \a indexWhere()
|
||||||
inline const T & atWhere(std::function<bool(const T & e)> test, ssize_t start = 0, const T & def = T()) const {
|
inline const T & atWhere(std::function<bool(const T & e)> test, ssize_t start = 0, const T & def = T()) const {
|
||||||
const ssize_t i = indexWhere(test, start);
|
const ssize_t i = indexWhere(std::move(test), start);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return def;
|
return def;
|
||||||
else
|
else
|
||||||
@@ -743,7 +743,7 @@ public:
|
|||||||
//! заданному в передаваемой функции `test`, или `def` если такого элемента нет.
|
//! заданному в передаваемой функции `test`, или `def` если такого элемента нет.
|
||||||
//! \~\sa \a lastIndexWhere()
|
//! \~\sa \a lastIndexWhere()
|
||||||
inline const T & lastAtWhere(std::function<bool(const T & e)> test, ssize_t start = -1, const T & def = T()) const {
|
inline const T & lastAtWhere(std::function<bool(const T & e)> test, ssize_t start = -1, const T & def = T()) const {
|
||||||
const ssize_t i = lastIndexWhere(test, start);
|
const ssize_t i = lastIndexWhere(std::move(test), start);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return def;
|
return def;
|
||||||
else
|
else
|
||||||
@@ -1265,7 +1265,7 @@ public:
|
|||||||
deleteT(pid_data + pid_start + new_size, pid_size - new_size);
|
deleteT(pid_data + pid_start + new_size, pid_size - new_size);
|
||||||
pid_size = new_size;
|
pid_size = new_size;
|
||||||
} else if (new_size > pid_size) {
|
} else if (new_size > pid_size) {
|
||||||
expand(new_size, f);
|
expand(new_size, std::move(f));
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -1540,7 +1540,7 @@ public:
|
|||||||
//! \endcode
|
//! \endcode
|
||||||
//! \~\sa \a sort()
|
//! \~\sa \a sort()
|
||||||
inline PIDeque<T> & sort(std::function<bool(const T & a, const T & b)> comp) {
|
inline PIDeque<T> & sort(std::function<bool(const T & a, const T & b)> comp) {
|
||||||
std::stable_sort(begin(), end(), comp);
|
std::stable_sort(begin(), end(), std::move(comp));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1654,7 +1654,13 @@ public:
|
|||||||
//! \endcode
|
//! \endcode
|
||||||
//! \~\sa \a remove(), \a removeOne(), \a removeWhere()
|
//! \~\sa \a remove(), \a removeOne(), \a removeWhere()
|
||||||
inline PIDeque<T> & removeWhere(std::function<bool(const T & e)> test) {
|
inline PIDeque<T> & removeWhere(std::function<bool(const T & e)> test) {
|
||||||
ssize_t j = indexWhere(test);
|
ssize_t j = -1;
|
||||||
|
for (size_t i = pid_start; i < pid_start + pid_size; ++i) {
|
||||||
|
if (test(pid_data[i])) {
|
||||||
|
j = ssize_t(i) - pid_start;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (j != -1) {
|
if (j != -1) {
|
||||||
for (size_t i = j + 1; i < pid_size; ++i) {
|
for (size_t i = j + 1; i < pid_size; ++i) {
|
||||||
if (!test(pid_data[i + pid_start])) {
|
if (!test(pid_data[i + pid_start])) {
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ public:
|
|||||||
//! \endcode
|
//! \endcode
|
||||||
inline PIVector(size_t size, std::function<T(size_t i)> f) {
|
inline PIVector(size_t size, std::function<T(size_t i)> f) {
|
||||||
PIINTROSPECTION_CONTAINER_NEW(T, sizeof(T))
|
PIINTROSPECTION_CONTAINER_NEW(T, sizeof(T))
|
||||||
expand(size, f);
|
expand(size, std::move(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \~english Move constructor.
|
//! \~english Move constructor.
|
||||||
@@ -725,7 +725,7 @@ public:
|
|||||||
//! заданному в передаваемой функции `test`, или `def` если такого элемента нет.
|
//! заданному в передаваемой функции `test`, или `def` если такого элемента нет.
|
||||||
//! \~\sa \a indexWhere()
|
//! \~\sa \a indexWhere()
|
||||||
inline const T & atWhere(std::function<bool(const T & e)> test, ssize_t start = 0, const T & def = T()) const {
|
inline const T & atWhere(std::function<bool(const T & e)> test, ssize_t start = 0, const T & def = T()) const {
|
||||||
const ssize_t i = indexWhere(test, start);
|
const ssize_t i = indexWhere(std::move(test), start);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return def;
|
return def;
|
||||||
else
|
else
|
||||||
@@ -739,7 +739,7 @@ public:
|
|||||||
//! заданному в передаваемой функции `test`, или `def` если такого элемента нет.
|
//! заданному в передаваемой функции `test`, или `def` если такого элемента нет.
|
||||||
//! \~\sa \a lastIndexWhere()
|
//! \~\sa \a lastIndexWhere()
|
||||||
inline const T & lastAtWhere(std::function<bool(const T & e)> test, ssize_t start = -1, const T & def = T()) const {
|
inline const T & lastAtWhere(std::function<bool(const T & e)> test, ssize_t start = -1, const T & def = T()) const {
|
||||||
const ssize_t i = lastIndexWhere(test, start);
|
const ssize_t i = lastIndexWhere(std::move(test), start);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return def;
|
return def;
|
||||||
else
|
else
|
||||||
@@ -1267,7 +1267,7 @@ public:
|
|||||||
deleteT(piv_data + new_size, piv_size - new_size);
|
deleteT(piv_data + new_size, piv_size - new_size);
|
||||||
piv_size = new_size;
|
piv_size = new_size;
|
||||||
} else if (new_size > piv_size) {
|
} else if (new_size > piv_size) {
|
||||||
expand(new_size, f);
|
expand(new_size, std::move(f));
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -1486,7 +1486,7 @@ public:
|
|||||||
//! \endcode
|
//! \endcode
|
||||||
//! \~\sa \a sort()
|
//! \~\sa \a sort()
|
||||||
inline PIVector<T> & sort(std::function<bool(const T & a, const T & b)> comp) {
|
inline PIVector<T> & sort(std::function<bool(const T & a, const T & b)> comp) {
|
||||||
std::stable_sort(begin(), end(), comp);
|
std::stable_sort(begin(), end(), std::move(comp));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1599,7 +1599,13 @@ public:
|
|||||||
//! \endcode
|
//! \endcode
|
||||||
//! \~\sa \a remove(), \a removeOne(), \a removeWhere()
|
//! \~\sa \a remove(), \a removeOne(), \a removeWhere()
|
||||||
inline PIVector<T> & removeWhere(std::function<bool(const T & e)> test) {
|
inline PIVector<T> & removeWhere(std::function<bool(const T & e)> test) {
|
||||||
ssize_t j = indexWhere(test);
|
ssize_t j = -1;
|
||||||
|
for (size_t i = 0; i < piv_size; ++i) {
|
||||||
|
if (test(piv_data[i])) {
|
||||||
|
j = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (j != -1) {
|
if (j != -1) {
|
||||||
for (size_t i = j + 1; i < piv_size; ++i) {
|
for (size_t i = j + 1; i < piv_size; ++i) {
|
||||||
if (!test(piv_data[i])) {
|
if (!test(piv_data[i])) {
|
||||||
|
|||||||
@@ -1070,7 +1070,7 @@ public:
|
|||||||
//! \~english Counts elements in the flat vector that pass the `test`.
|
//! \~english Counts elements in the flat vector that pass the `test`.
|
||||||
//! \~russian Подсчитывает элементы в плоском векторе, проходящие `test`.
|
//! \~russian Подсчитывает элементы в плоском векторе, проходящие `test`.
|
||||||
//! \~\sa PIVector::entries(std::function)
|
//! \~\sa PIVector::entries(std::function)
|
||||||
inline int entries(std::function<bool(const T & e)> test) const { return mat.entries(test); }
|
inline int entries(std::function<bool(const T & e)> test) const { return mat.entries(std::move(test)); }
|
||||||
|
|
||||||
|
|
||||||
//! \~english Returns the first index (row, col) of `e` in the 2D array.
|
//! \~english Returns the first index (row, col) of `e` in the 2D array.
|
||||||
@@ -1086,7 +1086,7 @@ public:
|
|||||||
//! \~russian Возвращает первый индекс (строка, столбец) в двумерном массиве, проходящий `test`.
|
//! \~russian Возвращает первый индекс (строка, столбец) в двумерном массиве, проходящий `test`.
|
||||||
//! \~\sa PIVector::indexWhere()
|
//! \~\sa PIVector::indexWhere()
|
||||||
inline Index indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
inline Index indexWhere(std::function<bool(const T & e)> test, ssize_t start = 0) const {
|
||||||
ssize_t flat = mat.indexWhere(test, start);
|
ssize_t flat = mat.indexWhere(std::move(test), start);
|
||||||
if (flat < 0 || cols_ == 0) return Index{-1, -1};
|
if (flat < 0 || cols_ == 0) return Index{-1, -1};
|
||||||
return Index{flat / static_cast<ssize_t>(cols_), flat % static_cast<ssize_t>(cols_)};
|
return Index{flat / static_cast<ssize_t>(cols_), flat % static_cast<ssize_t>(cols_)};
|
||||||
}
|
}
|
||||||
@@ -1104,7 +1104,7 @@ public:
|
|||||||
//! \~russian Возвращает последний индекс (строка, столбец) в двумерном массиве, проходящий `test`.
|
//! \~russian Возвращает последний индекс (строка, столбец) в двумерном массиве, проходящий `test`.
|
||||||
//! \~\sa PIVector::lastIndexWhere()
|
//! \~\sa PIVector::lastIndexWhere()
|
||||||
inline Index lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
inline Index lastIndexWhere(std::function<bool(const T & e)> test, ssize_t start = -1) const {
|
||||||
ssize_t flat = mat.lastIndexWhere(test, start);
|
ssize_t flat = mat.lastIndexWhere(std::move(test), start);
|
||||||
if (flat < 0 || cols_ == 0) return Index{-1, -1};
|
if (flat < 0 || cols_ == 0) return Index{-1, -1};
|
||||||
return Index{flat / static_cast<ssize_t>(cols_), flat % static_cast<ssize_t>(cols_)};
|
return Index{flat / static_cast<ssize_t>(cols_), flat % static_cast<ssize_t>(cols_)};
|
||||||
}
|
}
|
||||||
@@ -1113,12 +1113,12 @@ public:
|
|||||||
//! \~english Tests if any element in the flat vector passes the `test`.
|
//! \~english Tests if any element in the flat vector passes the `test`.
|
||||||
//! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`.
|
//! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`.
|
||||||
//! \~\sa PIVector::any()
|
//! \~\sa PIVector::any()
|
||||||
inline bool any(std::function<bool(const T & e)> test) const { return mat.any(test); }
|
inline bool any(std::function<bool(const T & e)> test) const { return mat.any(std::move(test)); }
|
||||||
|
|
||||||
//! \~english Tests if all elements in the flat vector pass the `test`.
|
//! \~english Tests if all elements in the flat vector pass the `test`.
|
||||||
//! \~russian Проверяет, проходят ли все элементы в плоском векторе `test`.
|
//! \~russian Проверяет, проходят ли все элементы в плоском векторе `test`.
|
||||||
//! \~\sa PIVector::every()
|
//! \~\sa PIVector::every()
|
||||||
inline bool every(std::function<bool(const T & e)> test) const { return mat.every(test); }
|
inline bool every(std::function<bool(const T & e)> test) const { return mat.every(std::move(test)); }
|
||||||
|
|
||||||
//! \~english Fills the entire 2D array with copies of `e`.
|
//! \~english Fills the entire 2D array with copies of `e`.
|
||||||
//! \~russian Заполняет весь двумерный массив копиями `e`.
|
//! \~russian Заполняет весь двумерный массив копиями `e`.
|
||||||
@@ -1132,7 +1132,7 @@ public:
|
|||||||
//! \~russian Заполняет весь двумерный массив, используя функцию-генератор `f` на основе плоского индекса.
|
//! \~russian Заполняет весь двумерный массив, используя функцию-генератор `f` на основе плоского индекса.
|
||||||
//! \~\sa PIVector::fill(std::function)
|
//! \~\sa PIVector::fill(std::function)
|
||||||
inline PIVector2D<T> & fill(std::function<T(size_t i)> f) {
|
inline PIVector2D<T> & fill(std::function<T(size_t i)> f) {
|
||||||
mat.fill(f);
|
mat.fill(std::move(f));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1228,7 +1228,7 @@ public:
|
|||||||
//! \~\sa PIVector::map()
|
//! \~\sa PIVector::map()
|
||||||
template<typename ST>
|
template<typename ST>
|
||||||
inline PIVector2D<ST> map(std::function<ST(const T & e)> f) const {
|
inline PIVector2D<ST> map(std::function<ST(const T & e)> f) const {
|
||||||
return PIVector2D<ST>(rows_, cols_, mat.template map<ST>(f));
|
return PIVector2D<ST>(rows_, cols_, mat.template map<ST>(std::move(f)));
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \~english Applies a function (with row and col indices) to each element and returns a new 2D array.
|
//! \~english Applies a function (with row and col indices) to each element and returns a new 2D array.
|
||||||
@@ -1250,23 +1250,26 @@ public:
|
|||||||
//! \~russian Применяет функцию к каждой строке (с возможностью изменения).
|
//! \~russian Применяет функцию к каждой строке (с возможностью изменения).
|
||||||
//! \~\sa forEachRow() const, PIVector::forEach()
|
//! \~\sa forEachRow() const, PIVector::forEach()
|
||||||
inline PIVector2D<T> & forEachRow(std::function<void(Row)> f) {
|
inline PIVector2D<T> & forEachRow(std::function<void(Row)> f) {
|
||||||
for (size_t r = 0; r < rows_; ++r)
|
for (size_t r = 0; r < rows_; ++r) {
|
||||||
f(row(r));
|
f(row(r));
|
||||||
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \~english Applies a function to each row (read-only).
|
//! \~english Applies a function to each row (read-only).
|
||||||
//! \~russian Применяет функцию к каждой строке (только чтение).
|
//! \~russian Применяет функцию к каждой строке (только чтение).
|
||||||
inline void forEachRow(std::function<void(RowConst)> f) const {
|
inline void forEachRow(std::function<void(RowConst)> f) const {
|
||||||
for (size_t r = 0; r < rows_; ++r)
|
for (size_t r = 0; r < rows_; ++r) {
|
||||||
f(row(r));
|
f(row(r));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//! \~english Applies a function to each column (modifiable).
|
//! \~english Applies a function to each column (modifiable).
|
||||||
//! \~russian Применяет функцию к каждому столбцу (с возможностью изменения).
|
//! \~russian Применяет функцию к каждому столбцу (с возможностью изменения).
|
||||||
inline PIVector2D<T> & forEachColumn(std::function<void(Col)> f) {
|
inline PIVector2D<T> & forEachColumn(std::function<void(Col)> f) {
|
||||||
for (size_t c = 0; c < cols_; ++c)
|
for (size_t c = 0; c < cols_; ++c) {
|
||||||
f(col(c));
|
f(col(c));
|
||||||
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1274,16 +1277,17 @@ public:
|
|||||||
//! \~russian Применяет функцию к каждому столбцу (только чтение).
|
//! \~russian Применяет функцию к каждому столбцу (только чтение).
|
||||||
//! \param f Function taking a \a ColConst.
|
//! \param f Function taking a \a ColConst.
|
||||||
inline void forEachColumn(std::function<void(ColConst)> f) const {
|
inline void forEachColumn(std::function<void(ColConst)> f) const {
|
||||||
for (size_t c = 0; c < cols_; ++c)
|
for (size_t c = 0; c < cols_; ++c) {
|
||||||
f(col(c));
|
f(col(c));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//! \~english Accumulates a value across all elements.
|
//! \~english Accumulates a value across all elements.
|
||||||
//! \~russian Аккумулирует значение по всем элементам.
|
//! \~russian Аккумулирует значение по всем элементам.
|
||||||
//! \~\sa PIVector::reduce()
|
//! \~\sa PIVector::reduce()
|
||||||
template<typename ST>
|
template<typename ST>
|
||||||
inline ST reduce(std::function<ST(const T & e, const ST & acc)> f, const ST & initial = ST()) const {
|
inline ST reduce(std::function<ST(const T & e, const ST & acc)> f, const ST & initial = ST()) const {
|
||||||
return mat.template reduce<ST>(f, initial);
|
return mat.template reduce<ST>(std::move(f), initial);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \~english Accumulates a value across all elements with indices.
|
//! \~english Accumulates a value across all elements with indices.
|
||||||
|
|||||||
@@ -726,11 +726,12 @@ inline bool piDeleteSafety(T *& pointer) {
|
|||||||
//! \~english In this example "Error!" will be printed on every \b false function return.
|
//! \~english In this example "Error!" will be printed on every \b false function return.
|
||||||
//! \~russian В данном примере будет выведен "Error!" при каждом \b false возврате из функции.
|
//! \~russian В данном примере будет выведен "Error!" при каждом \b false возврате из функции.
|
||||||
class PIP_EXPORT PIScopeExitCall {
|
class PIP_EXPORT PIScopeExitCall {
|
||||||
|
NO_COPY_CLASS(PIScopeExitCall)
|
||||||
public:
|
public:
|
||||||
//! \~\brief
|
//! \~\brief
|
||||||
//! \~english Constructor that takes a function to execute
|
//! \~english Constructor that takes a function to execute
|
||||||
//! \~russian Конструктор, который принимает функцию для выполнения
|
//! \~russian Конструктор, который принимает функцию для выполнения
|
||||||
explicit PIScopeExitCall(std::function<void()> f): func(f) {}
|
explicit PIScopeExitCall(std::function<void()> f): func(std::move(f)) {}
|
||||||
|
|
||||||
//! \~\brief
|
//! \~\brief
|
||||||
//! \~english Destructor that executes the function if it exists
|
//! \~english Destructor that executes the function if it exists
|
||||||
@@ -758,8 +759,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NO_COPY_CLASS(PIScopeExitCall)
|
|
||||||
|
|
||||||
std::function<void()> func;
|
std::function<void()> func;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -768,14 +767,14 @@ private:
|
|||||||
//! \~english Inherit from this class to make your class non-trivially copyable.
|
//! \~english Inherit from this class to make your class non-trivially copyable.
|
||||||
//! \~russian Наследуйтесь от этого класса чтобы сделать свой класс нетривиально копируемым.
|
//! \~russian Наследуйтесь от этого класса чтобы сделать свой класс нетривиально копируемым.
|
||||||
struct PIP_EXPORT PINonTriviallyCopyable {
|
struct PIP_EXPORT PINonTriviallyCopyable {
|
||||||
PINonTriviallyCopyable() noexcept = default;
|
PINonTriviallyCopyable() = default;
|
||||||
PINonTriviallyCopyable(const PINonTriviallyCopyable &) noexcept = default;
|
PINonTriviallyCopyable(const PINonTriviallyCopyable &) = default;
|
||||||
PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept;
|
PINonTriviallyCopyable(PINonTriviallyCopyable &&) ;
|
||||||
PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) noexcept = default;
|
PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) = default;
|
||||||
PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) noexcept = default;
|
PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) = default;
|
||||||
~PINonTriviallyCopyable() = default;
|
~PINonTriviallyCopyable() = default;
|
||||||
};
|
};
|
||||||
inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept = default;
|
inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) = default;
|
||||||
|
|
||||||
|
|
||||||
//! \~\brief
|
//! \~\brief
|
||||||
|
|||||||
@@ -113,11 +113,11 @@ public:
|
|||||||
|
|
||||||
//! \~english Sets the callback that receives parsed requests and returns replies.
|
//! \~english Sets the callback that receives parsed requests and returns replies.
|
||||||
//! \~russian Устанавливает callback, который получает разобранные запросы и возвращает ответы.
|
//! \~russian Устанавливает callback, который получает разобранные запросы и возвращает ответы.
|
||||||
void setRequestCallback(std::function<PIHTTP::MessageMutable(const PIHTTP::MessageConst &)> c) { callback = c; }
|
void setRequestCallback(std::function<PIHTTP::MessageMutable(const PIHTTP::MessageConst &)> c) { callback = std::move(c); }
|
||||||
|
|
||||||
//! \~english Sets the credential validator used when HTTP Basic authentication is enabled.
|
//! \~english Sets the credential validator used when HTTP Basic authentication is enabled.
|
||||||
//! \~russian Устанавливает валидатор учетных данных, используемый при включенной HTTP Basic-аутентификации.
|
//! \~russian Устанавливает валидатор учетных данных, используемый при включенной HTTP Basic-аутентификации.
|
||||||
void setBasicAuthCallback(std::function<bool(const PIString &, const PIString &)> c) { callback_auth = c; }
|
void setBasicAuthCallback(std::function<bool(const PIString &, const PIString &)> c) { callback_auth = std::move(c); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void addFixedHeaders(PIHTTP::MessageMutable & msg);
|
static void addFixedHeaders(PIHTTP::MessageMutable & msg);
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ public:
|
|||||||
//! \~\param f
|
//! \~\param f
|
||||||
//! \~english The callback function returning the next file path, or nullptr to use the internal generator.
|
//! \~english The callback function returning the next file path, or nullptr to use the internal generator.
|
||||||
//! \~russian Функция обратного вызова, возвращающая путь к следующему файлу, или nullptr для использования внутреннего генератора.
|
//! \~russian Функция обратного вызова, возвращающая путь к следующему файлу, или nullptr для использования внутреннего генератора.
|
||||||
void setFuncGetNewFilePath(std::function<PIString()> f) { f_new_path = f; }
|
void setFuncGetNewFilePath(std::function<PIString()> f) { f_new_path = std::move(f); }
|
||||||
|
|
||||||
//! \~english Writes one record with explicit ID and payload.
|
//! \~english Writes one record with explicit ID and payload.
|
||||||
//! \~russian Записывает одну запись с явным идентификатором и данными.
|
//! \~russian Записывает одну запись с явным идентификатором и данными.
|
||||||
|
|||||||
@@ -527,6 +527,7 @@ bool PIEthernet::listen(bool threaded) {
|
|||||||
listen_threaded = true;
|
listen_threaded = true;
|
||||||
server_bounded = false;
|
server_bounded = false;
|
||||||
server_thread_.start(server_func);
|
server_thread_.start(server_func);
|
||||||
|
server_thread_.waitForStart();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
listen_threaded = server_bounded = false;
|
listen_threaded = server_bounded = false;
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ void PIIODevice::setReopenTimeout(PISystemTime timeout) {
|
|||||||
//! после каждого успешного потокового чтения. Метод должен быть
|
//! после каждого успешного потокового чтения. Метод должен быть
|
||||||
//! в формате "bool func(void * data, uchar * readed, int size)"
|
//! в формате "bool func(void * data, uchar * readed, int size)"
|
||||||
void PIIODevice::setThreadedReadSlot(ReadRetFunc func) {
|
void PIIODevice::setThreadedReadSlot(ReadRetFunc func) {
|
||||||
func_read = func;
|
func_read = std::move(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -103,15 +103,15 @@ public:
|
|||||||
|
|
||||||
//! \~english Sets custom header validation callback.
|
//! \~english Sets custom header validation callback.
|
||||||
//! \~russian Устанавливает пользовательский callback проверки заголовка.
|
//! \~russian Устанавливает пользовательский callback проверки заголовка.
|
||||||
void setHeaderCheckSlot(PacketExtractorHeaderFunc f) { func_header = f; }
|
void setHeaderCheckSlot(PacketExtractorHeaderFunc f) { func_header = std::move(f); }
|
||||||
|
|
||||||
//! \~english Sets custom payload validation callback.
|
//! \~english Sets custom payload validation callback.
|
||||||
//! \~russian Устанавливает пользовательский callback проверки полезной нагрузки.
|
//! \~russian Устанавливает пользовательский callback проверки полезной нагрузки.
|
||||||
void setPayloadCheckSlot(PacketExtractorPayloadFunc f) { func_payload = f; }
|
void setPayloadCheckSlot(PacketExtractorPayloadFunc f) { func_payload = std::move(f); }
|
||||||
|
|
||||||
//! \~english Sets custom footer validation callback.
|
//! \~english Sets custom footer validation callback.
|
||||||
//! \~russian Устанавливает пользовательский callback проверки окончания пакета.
|
//! \~russian Устанавливает пользовательский callback проверки окончания пакета.
|
||||||
void setFooterCheckSlot(PacketExtractorFooterFunc f) { func_footer = f; }
|
void setFooterCheckSlot(PacketExtractorFooterFunc f) { func_footer = std::move(f); }
|
||||||
|
|
||||||
|
|
||||||
//! \~english Switches packet extraction to mode "mode".
|
//! \~english Switches packet extraction to mode "mode".
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public:
|
|||||||
//! \~russian Связывает ключ "key" с callback-функцией "func" без аргументов полезной нагрузки.
|
//! \~russian Связывает ключ "key" с callback-функцией "func" без аргументов полезной нагрузки.
|
||||||
void assign(Key key, std::function<void()> func) {
|
void assign(Key key, std::function<void()> func) {
|
||||||
auto lf = [func](PIByteArray) { func(); };
|
auto lf = [func](PIByteArray) { func(); };
|
||||||
functions[key] << lf;
|
functions[key] << std::move(lf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ public:
|
|||||||
func(v);
|
func(v);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
functions[key] << lf;
|
functions[key] << std::move(lf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ struct PIP_EXPORT Function {
|
|||||||
identifier = name;
|
identifier = name;
|
||||||
arguments = args;
|
arguments = args;
|
||||||
type = bfCustom;
|
type = bfCustom;
|
||||||
handler = h;
|
handler = std::move(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
PIString identifier;
|
PIString identifier;
|
||||||
|
|||||||
@@ -805,11 +805,12 @@ public:
|
|||||||
|
|
||||||
//! \~english Applies \a f to every coordinate without modifying the vector.
|
//! \~english Applies \a f to every coordinate without modifying the vector.
|
||||||
//! \~russian Применяет \a f к каждой координате без изменения вектора.
|
//! \~russian Применяет \a f к каждой координате без изменения вектора.
|
||||||
void forEach(std::function<void(const Type &)> f) const { c.forEach(f); }
|
void forEach(std::function<void(const Type &)> f) const { c.forEach(std::move(f)); }
|
||||||
|
|
||||||
//! \~english Applies \a f to every coordinate and returns this vector.
|
//! \~english Applies \a f to every coordinate and returns this vector.
|
||||||
//! \~russian Применяет \a f к каждой координате и возвращает этот вектор.
|
//! \~russian Применяет \a f к каждой координате и возвращает этот вектор.
|
||||||
_CVector & forEach(std::function<void(Type &)> f) {
|
_CVector & forEach(std::function<void(Type &)> f) {
|
||||||
c.forEach(f);
|
c.forEach(std::move(f));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,255 +0,0 @@
|
|||||||
/*
|
|
||||||
PIP - Platform Independent Primitives
|
|
||||||
MQTT Client
|
|
||||||
Ivan Pelipenko peri4ko@yandex.ru
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "pimqttclient.h"
|
|
||||||
|
|
||||||
#include "MQTTClient.h"
|
|
||||||
|
|
||||||
|
|
||||||
STATIC_INITIALIZER_BEGIN
|
|
||||||
MQTTClient_init_options opts = MQTTClient_init_options_initializer;
|
|
||||||
MQTTClient_global_init(&opts);
|
|
||||||
STATIC_INITIALIZER_END
|
|
||||||
|
|
||||||
|
|
||||||
PRIVATE_DEFINITION_START(PIMQTT::Client)
|
|
||||||
MQTTClient client = nullptr;
|
|
||||||
bool connected = false;
|
|
||||||
|
|
||||||
static void connectionLost_callback(void * context, char *) {
|
|
||||||
((PIMQTT::Client *)context)->mqtt_connectionLost();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void deliveryComplete_callback(void * context, MQTTClient_deliveryToken dt) {
|
|
||||||
((PIMQTT::Client *)context)->mqtt_deliveryComplete(dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int messageArrived_callback(void * context, char * topicName, int topicLen, MQTTClient_message * message) {
|
|
||||||
PIMQTT::Message msg;
|
|
||||||
msg.topic = PIString::fromUTF8(topicName);
|
|
||||||
msg.payload = PIByteArray(message->payload, message->payloadlen);
|
|
||||||
msg.msg_id = message->msgid;
|
|
||||||
msg.is_duplicate = message->dup != 0;
|
|
||||||
msg.qos = static_cast<PIMQTT::QoS>(message->qos);
|
|
||||||
MQTTClient_freeMessage(&message);
|
|
||||||
MQTTClient_free(topicName);
|
|
||||||
((PIMQTT::Client *)context)->mqtt_messageArrived(msg);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
PRIVATE_DEFINITION_END(PIMQTT::Client)
|
|
||||||
|
|
||||||
|
|
||||||
PIMQTT::Client::Client() {
|
|
||||||
thread = new PIThread([this] { run(); });
|
|
||||||
thread->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PIMQTT::Client::~Client() {
|
|
||||||
thread->stop();
|
|
||||||
disconnect();
|
|
||||||
thread->waitForFinish();
|
|
||||||
piDeleteSafety(thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::connect(const PIString & address, const PIString & client, const PIString & username, const PIString & password) {
|
|
||||||
auto ci = connect_info.getRef();
|
|
||||||
ci->address = address;
|
|
||||||
ci->clientID = client;
|
|
||||||
ci->username = username;
|
|
||||||
ci->password = password;
|
|
||||||
changeStatus(Connecting);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::disconnect() {
|
|
||||||
changeStatus(Disconnecting);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::subscribe(const PIString & topic, QoS qos) {
|
|
||||||
sub_queue.getRef()->enqueue({topic, qos});
|
|
||||||
notifier.notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::unsubscribe(const PIString & topic) {
|
|
||||||
unsub_queue.getRef()->enqueue(topic);
|
|
||||||
notifier.notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::publish(const PIString & topic, const PIByteArray & msg, QoS qos) {
|
|
||||||
Message m;
|
|
||||||
m.topic = topic;
|
|
||||||
m.payload = msg;
|
|
||||||
m.qos = qos;
|
|
||||||
pub_queue.getRef()->enqueue(m);
|
|
||||||
notifier.notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::mqtt_connectionLost() {
|
|
||||||
piCoutObj << "mqtt_connectionLost";
|
|
||||||
PRIVATE->connected = false;
|
|
||||||
changeStatus(Idle);
|
|
||||||
disconnected(Error::ServerUnavailable);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::mqtt_deliveryComplete(int token) {}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::mqtt_messageArrived(const Message & msg) {
|
|
||||||
piCoutObj << "mqtt_messageArrived";
|
|
||||||
received(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::connectInternal() {
|
|
||||||
destroy();
|
|
||||||
PRIVATE->connected = false;
|
|
||||||
auto ci = connect_info.get();
|
|
||||||
PINetworkAddress net_addr = PINetworkAddress::resolve(ci.address);
|
|
||||||
if (net_addr.port() == 0) net_addr.setPort(1883);
|
|
||||||
PIString server_uri = "tcp://" + net_addr.toString();
|
|
||||||
MQTTClient_create(&PRIVATE->client, server_uri.dataAscii(), ci.clientID.dataUTF8(), MQTTCLIENT_PERSISTENCE_NONE, nullptr);
|
|
||||||
MQTTClient_setCallbacks(PRIVATE->client,
|
|
||||||
this,
|
|
||||||
PRIVATE->connectionLost_callback,
|
|
||||||
PRIVATE->messageArrived_callback,
|
|
||||||
PRIVATE->deliveryComplete_callback);
|
|
||||||
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
|
|
||||||
conn_opts.keepAliveInterval = 9;
|
|
||||||
conn_opts.cleansession = 1;
|
|
||||||
conn_opts.connectTimeout = piRound(connect_timeout.toSeconds());
|
|
||||||
if (ci.username.isNotEmpty()) conn_opts.username = ci.username.dataAscii();
|
|
||||||
if (ci.password.isNotEmpty()) conn_opts.password = ci.password.dataAscii();
|
|
||||||
int ret = MQTTClient_connect(PRIVATE->client, &conn_opts);
|
|
||||||
if (ret != MQTTCLIENT_SUCCESS) {
|
|
||||||
piCoutObj << "Failed to connect to " + server_uri << ", code" << ret;
|
|
||||||
changeStatus(Idle);
|
|
||||||
if (ret < 0) ret = static_cast<int>(Error::ServerUnavailable);
|
|
||||||
disconnected(static_cast<Error>(ret));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PRIVATE->connected = true;
|
|
||||||
changeStatus(Connected);
|
|
||||||
connected();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::publishInternal(const Message & m) {
|
|
||||||
if (!PRIVATE->client) return;
|
|
||||||
MQTTClient_message pubmsg = MQTTClient_message_initializer;
|
|
||||||
pubmsg.payload = const_cast<void *>(static_cast<const void *>(m.payload.data()));
|
|
||||||
pubmsg.payloadlen = m.payload.size_s();
|
|
||||||
pubmsg.qos = static_cast<int>(m.qos);
|
|
||||||
int ret = MQTTClient_publishMessage(PRIVATE->client, m.topic.dataAscii(), &pubmsg, nullptr);
|
|
||||||
if (ret != MQTTCLIENT_SUCCESS) {
|
|
||||||
piCoutObj << "Failed to publishMessage, code" << ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::subscribeInternal(const Subscribe & sub) {
|
|
||||||
if (!PRIVATE->client) return;
|
|
||||||
int ret = MQTTClient_subscribe(PRIVATE->client, sub.topic.dataUTF8(), static_cast<int>(sub.qos));
|
|
||||||
if (ret != MQTTCLIENT_SUCCESS) {
|
|
||||||
piCoutObj << "Failed to subscribe" << sub.topic << ", code" << ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::unsubscribeInternal(const PIString & topic) {
|
|
||||||
if (!PRIVATE->client) return;
|
|
||||||
int ret = MQTTClient_unsubscribe(PRIVATE->client, topic.dataUTF8());
|
|
||||||
if (ret != MQTTCLIENT_SUCCESS) {
|
|
||||||
piCoutObj << "Failed to unsubscribe" << topic << ", code" << ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::destroy() {
|
|
||||||
if (!PRIVATE->client) return;
|
|
||||||
if (PRIVATE->connected) MQTTClient_disconnect(PRIVATE->client, 1000);
|
|
||||||
MQTTClient_destroy(&PRIVATE->client);
|
|
||||||
PRIVATE->connected = false;
|
|
||||||
PRIVATE->client = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::changeStatus(Status s) {
|
|
||||||
m_status = s;
|
|
||||||
notifier.notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIMQTT::Client::run() {
|
|
||||||
notifier.wait();
|
|
||||||
piCoutObj << "run" << (int)m_status;
|
|
||||||
if (thread->isStopping() || m_status == Disconnecting) {
|
|
||||||
bool was_connected = PRIVATE->connected;
|
|
||||||
changeStatus(Idle);
|
|
||||||
destroy();
|
|
||||||
if (was_connected) disconnected(Error::Unknown);
|
|
||||||
PRIVATE->connected = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (m_status == Connecting) {
|
|
||||||
connectInternal();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (m_status == Connected) {
|
|
||||||
Subscribe sub;
|
|
||||||
{
|
|
||||||
auto ref = sub_queue.getRef();
|
|
||||||
if (!ref->isEmpty()) sub = ref->dequeue();
|
|
||||||
}
|
|
||||||
if (sub.topic.isNotEmpty()) {
|
|
||||||
subscribeInternal(sub);
|
|
||||||
notifier.notify();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PIString topic;
|
|
||||||
{
|
|
||||||
auto ref = unsub_queue.getRef();
|
|
||||||
if (!ref->isEmpty()) topic = ref->dequeue();
|
|
||||||
}
|
|
||||||
if (topic.isNotEmpty()) {
|
|
||||||
unsubscribeInternal(topic);
|
|
||||||
notifier.notify();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Message msg;
|
|
||||||
{
|
|
||||||
auto ref = pub_queue.getRef();
|
|
||||||
if (!ref->isEmpty()) msg = ref->dequeue();
|
|
||||||
}
|
|
||||||
if (msg.isValid()) {
|
|
||||||
publishInternal(msg);
|
|
||||||
notifier.notify();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,14 +27,16 @@
|
|||||||
|
|
||||||
#include "piliterals_time.h"
|
#include "piliterals_time.h"
|
||||||
#include "pimqtttypes.h"
|
#include "pimqtttypes.h"
|
||||||
|
#include "pip_mqtt_client_export.h"
|
||||||
#include "piprotectedvariable.h"
|
#include "piprotectedvariable.h"
|
||||||
#include "pithread.h"
|
#include "pithread.h"
|
||||||
|
#include "pithreadpoolworker.h"
|
||||||
|
|
||||||
|
|
||||||
namespace PIMQTT {
|
namespace PIMQTT {
|
||||||
|
|
||||||
|
|
||||||
class PIP_EXPORT Client: public PIObject {
|
class PIP_MQTT_CLIENT_EXPORT Client: public PIObject {
|
||||||
PIOBJECT(PIMQTT::Client)
|
PIOBJECT(PIMQTT::Client)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -103,6 +105,7 @@ private:
|
|||||||
std::atomic_int m_status = {Idle};
|
std::atomic_int m_status = {Idle};
|
||||||
PISystemTime connect_timeout = 10_s;
|
PISystemTime connect_timeout = 10_s;
|
||||||
PIThread * thread = nullptr;
|
PIThread * thread = nullptr;
|
||||||
|
// PIThreadPoolWorker * worker = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public:
|
|||||||
|
|
||||||
//! \~english Sets a callback invoked when the machine finishes.
|
//! \~english Sets a callback invoked when the machine finishes.
|
||||||
//! \~russian Задает callback-функцию, вызываемую при завершении машины.
|
//! \~russian Задает callback-функцию, вызываемую при завершении машины.
|
||||||
void setOnFinish(std::function<void()> f) { on_finish = f; }
|
void setOnFinish(std::function<void()> f) { on_finish = std::move(f); }
|
||||||
|
|
||||||
|
|
||||||
//! \~english Posts an event to active states and triggers the first matching transition.
|
//! \~english Posts an event to active states and triggers the first matching transition.
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public:
|
|||||||
template<typename Ret, typename... Args>
|
template<typename Ret, typename... Args>
|
||||||
FunctionBase * makeFunction(std::function<Ret(Args...)> func) {
|
FunctionBase * makeFunction(std::function<Ret(Args...)> func) {
|
||||||
auto * ret = new Function<Args...>();
|
auto * ret = new Function<Args...>();
|
||||||
ret->func = func;
|
ret->func = std::move(func);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -196,10 +196,10 @@ class PIP_EXPORT PIStateLambda: public PIStateBase {
|
|||||||
public:
|
public:
|
||||||
//! \~english Creates a state backed by enter and exit callbacks.
|
//! \~english Creates a state backed by enter and exit callbacks.
|
||||||
//! \~russian Создает состояние с callback-функциями входа и выхода.
|
//! \~russian Создает состояние с callback-функциями входа и выхода.
|
||||||
PIStateLambda(std::function<void()> on_enter, std::function<void()> on_exit = nullptr, const PIString & n = {}): PIStateBase(n) {
|
PIStateLambda(std::function<void()> on_enter, std::function<void()> on_exit = nullptr, const PIString & n = {})
|
||||||
enter = on_enter;
|
: PIStateBase(n)
|
||||||
exit = on_exit;
|
, enter(std::move(on_enter))
|
||||||
}
|
, exit(std::move(on_exit)) {}
|
||||||
|
|
||||||
|
|
||||||
//! \~english Executes the enter callback.
|
//! \~english Executes the enter callback.
|
||||||
@@ -228,9 +228,8 @@ class PIP_EXPORT PIStateFinal: public PIStateBase {
|
|||||||
public:
|
public:
|
||||||
//! \~english Creates a final state with an optional callback executed on entry.
|
//! \~english Creates a final state with an optional callback executed on entry.
|
||||||
//! \~russian Создает финальное состояние с необязательной callback-функцией, выполняемой при входе.
|
//! \~russian Создает финальное состояние с необязательной callback-функцией, выполняемой при входе.
|
||||||
PIStateFinal(std::function<void()> on_finish = nullptr, const PIString & n = {}): PIStateBase(n) {
|
PIStateFinal(std::function<void()> on_finish = nullptr, const PIString & n = {}): PIStateBase(n), enter(std::move(on_finish)) {
|
||||||
is_final = true;
|
is_final = true;
|
||||||
enter = on_finish;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ PITransitionBase::~PITransitionBase() {
|
|||||||
|
|
||||||
|
|
||||||
PITransitionBase * PITransitionBase::addAction(std::function<void()> a) {
|
PITransitionBase * PITransitionBase::addAction(std::function<void()> a) {
|
||||||
action = a;
|
action = std::move(a);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public:
|
|||||||
|
|
||||||
//! \~english Installs callback that receives grabbed signals.
|
//! \~english Installs callback that receives grabbed signals.
|
||||||
//! \~russian Устанавливает обратный вызов, получающий перехваченные сигналы.
|
//! \~russian Устанавливает обратный вызов, получающий перехваченные сигналы.
|
||||||
static void setSlot(SignalEvent slot) { ret_func = slot; }
|
static void setSlot(SignalEvent slot) { ret_func = std::move(slot); }
|
||||||
|
|
||||||
//! \~english Redirects selected signals to the slot set by \a setSlot().
|
//! \~english Redirects selected signals to the slot set by \a setSlot().
|
||||||
//! \~russian Перенаправляет выбранные сигналы в обработчик, заданный через \a setSlot().
|
//! \~russian Перенаправляет выбранные сигналы в обработчик, заданный через \a setSlot().
|
||||||
|
|||||||
@@ -86,11 +86,9 @@ void PIConditionVariable::wait(PIMutex & lk) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIConditionVariable::wait(PIMutex & lk, const std::function<bool()> & condition) {
|
void PIConditionVariable::wait(PIMutex & lk, std::function<bool()> condition) {
|
||||||
bool isCondition;
|
|
||||||
while (true) {
|
while (true) {
|
||||||
isCondition = condition();
|
if (condition()) break;
|
||||||
if (isCondition) break;
|
|
||||||
#if defined(WINDOWS)
|
#if defined(WINDOWS)
|
||||||
SleepConditionVariableCS(&PRIVATE->nativeHandle, (PCRITICAL_SECTION)lk.handle(), INFINITE);
|
SleepConditionVariableCS(&PRIVATE->nativeHandle, (PCRITICAL_SECTION)lk.handle(), INFINITE);
|
||||||
#elif defined(FREERTOS)
|
#elif defined(FREERTOS)
|
||||||
@@ -122,8 +120,7 @@ bool PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout, const std::function<bool()> & condition) {
|
bool PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout, std::function<bool()> condition) {
|
||||||
bool isCondition;
|
|
||||||
#if defined(WINDOWS) || defined(FREERTOS)
|
#if defined(WINDOWS) || defined(FREERTOS)
|
||||||
PITimeMeasurer measurer;
|
PITimeMeasurer measurer;
|
||||||
#else
|
#else
|
||||||
@@ -135,8 +132,7 @@ bool PIConditionVariable::waitFor(PIMutex & lk, PISystemTime timeout, const std:
|
|||||||
xEventGroupClearBits(PRIVATE->nativeHandle, 1);
|
xEventGroupClearBits(PRIVATE->nativeHandle, 1);
|
||||||
#endif
|
#endif
|
||||||
while (true) {
|
while (true) {
|
||||||
isCondition = condition();
|
if (condition()) break;
|
||||||
if (isCondition) break;
|
|
||||||
bool isTimeout;
|
bool isTimeout;
|
||||||
#if defined(WINDOWS)
|
#if defined(WINDOWS)
|
||||||
isTimeout = SleepConditionVariableCS(&PRIVATE->nativeHandle,
|
isTimeout = SleepConditionVariableCS(&PRIVATE->nativeHandle,
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ public:
|
|||||||
//! \param condition вызываемый объект или функция, не принимающая аргументов и возвращающая значение, которое может быть оценено как
|
//! \param condition вызываемый объект или функция, не принимающая аргументов и возвращающая значение, которое может быть оценено как
|
||||||
//! bool. Вызывается повторно, пока не примет значение true
|
//! bool. Вызывается повторно, пока не примет значение true
|
||||||
//!
|
//!
|
||||||
virtual void wait(PIMutex & lk, const std::function<bool()> & condition);
|
virtual void wait(PIMutex & lk, std::function<bool ()> condition);
|
||||||
|
|
||||||
|
|
||||||
//! \~english Waits for at most \a timeout and returns \c true if awakened before it expires.
|
//! \~english Waits for at most \a timeout and returns \c true if awakened before it expires.
|
||||||
@@ -167,7 +167,7 @@ public:
|
|||||||
//! bool. Вызывается повторно, пока не примет значение true
|
//! bool. Вызывается повторно, пока не примет значение true
|
||||||
//! \return false если достигнут таймаут, или true если условие пробуждения истинно
|
//! \return false если достигнут таймаут, или true если условие пробуждения истинно
|
||||||
//!
|
//!
|
||||||
virtual bool waitFor(PIMutex & lk, PISystemTime timeout, const std::function<bool()> & condition);
|
virtual bool waitFor(PIMutex & lk, PISystemTime timeout, std::function<bool()> condition);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PRIVATE_DECLARATION(PIP_EXPORT)
|
PRIVATE_DECLARATION(PIP_EXPORT)
|
||||||
|
|||||||
@@ -34,21 +34,29 @@
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
class PIP_EXPORT PIProtectedVariable {
|
class PIP_EXPORT PIProtectedVariable {
|
||||||
public:
|
public:
|
||||||
|
//! \~english Constructs %PIProtectedVariable and initialize variable by value `v`.
|
||||||
|
//! \~russian Создает %PIProtectedVariable и инициализирует переменную значением `v`.
|
||||||
|
PIProtectedVariable(T v = T()): var(std::move(v)) {}
|
||||||
|
|
||||||
//! \~\brief
|
//! \~\brief
|
||||||
//! \~english Pointer-like wrapper returned by \a getRef() while the protected value remains locked.
|
//! \~english Pointer-like wrapper returned by \a getRef() while the protected value remains locked.
|
||||||
//! \~russian Указателеподобная обертка, возвращаемая \a getRef(), пока защищенное значение остается заблокированным.
|
//! \~russian Указателеподобная обертка, возвращаемая \a getRef(), пока защищенное значение остается заблокированным.
|
||||||
class PIP_EXPORT Pointer {
|
class PIP_EXPORT Pointer {
|
||||||
friend class PIProtectedVariable<T>;
|
friend class PIProtectedVariable<T>;
|
||||||
|
NO_COPY_CLASS(Pointer);
|
||||||
|
Pointer & operator=(Pointer && other) = delete;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//! \~english Copies wrapper state for access to the same protected value.
|
//! \~english Move constructor - transfers ownership of the lock.
|
||||||
//! \~russian Копирует состояние обертки для доступа к тому же защищенному значению.
|
//! \~russian Конструктор перемещения - передает владение блокировкой.
|
||||||
Pointer(const Pointer & v): pv(v.pv), counter(v.counter + 1) {}
|
Pointer(Pointer && other): pv(other.pv) { other.can_unlock = false; };
|
||||||
|
|
||||||
//! \~english Destroys wrapper and releases the mutex when it owns the original lock.
|
//! \~english Destroys wrapper and releases the mutex.
|
||||||
//! \~russian Уничтожает обертку и освобождает мьютекс, когда она владеет исходной блокировкой.
|
//! \~russian Уничтожает обертку и освобождает мьютекс.
|
||||||
~Pointer() {
|
~Pointer() {
|
||||||
if (counter == 0) pv.mutex.unlock();
|
if (can_unlock) {
|
||||||
|
pv.mutex.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \~english Returns pointer access to the protected value.
|
//! \~english Returns pointer access to the protected value.
|
||||||
@@ -60,11 +68,11 @@ public:
|
|||||||
T & operator*() { return pv.var; }
|
T & operator*() { return pv.var; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Pointer() = delete;
|
explicit Pointer() = delete;
|
||||||
Pointer(PIProtectedVariable<T> & v): pv(v) {}
|
explicit Pointer(PIProtectedVariable<T> & v): pv(v) { pv.mutex.lock(); }
|
||||||
|
|
||||||
PIProtectedVariable<T> & pv;
|
PIProtectedVariable<T> & pv;
|
||||||
int counter = 0;
|
bool can_unlock = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
//! \~english Replaces the protected value with \a v.
|
//! \~english Replaces the protected value with \a v.
|
||||||
@@ -76,10 +84,7 @@ public:
|
|||||||
|
|
||||||
//! \~english Locks the value and returns wrapper-based access to it.
|
//! \~english Locks the value and returns wrapper-based access to it.
|
||||||
//! \~russian Блокирует значение и возвращает обертку для доступа к нему.
|
//! \~russian Блокирует значение и возвращает обертку для доступа к нему.
|
||||||
Pointer getRef() {
|
Pointer getRef() { return Pointer(*this); }
|
||||||
mutex.lock();
|
|
||||||
return Pointer(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! \~english Returns a copy of the protected value.
|
//! \~english Returns a copy of the protected value.
|
||||||
//! \~russian Возвращает копию защищенного значения.
|
//! \~russian Возвращает копию защищенного значения.
|
||||||
@@ -98,7 +103,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
mutable PIMutex mutex;
|
mutable PIMutex mutex;
|
||||||
T var;
|
T var = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -540,7 +540,7 @@ PRIVATE_DEFINITION_END(PIThread)
|
|||||||
PIThread::PIThread(void * data, ThreadFunc func, bool startNow, PISystemTime loop_delay): PIObject() {
|
PIThread::PIThread(void * data, ThreadFunc func, bool startNow, PISystemTime loop_delay): PIObject() {
|
||||||
PIINTROSPECTION_THREAD_NEW(this);
|
PIINTROSPECTION_THREAD_NEW(this);
|
||||||
data_ = data;
|
data_ = data;
|
||||||
ret_func = func;
|
ret_func = std::move(func);
|
||||||
terminating = running_ = lockRun = false;
|
terminating = running_ = lockRun = false;
|
||||||
priority_ = piNormal;
|
priority_ = piNormal;
|
||||||
delay_ = loop_delay;
|
delay_ = loop_delay;
|
||||||
@@ -609,13 +609,13 @@ bool PIThread::start(PISystemTime loop_delay) {
|
|||||||
|
|
||||||
|
|
||||||
bool PIThread::start(ThreadFunc func) {
|
bool PIThread::start(ThreadFunc func) {
|
||||||
ret_func = func;
|
ret_func = std::move(func);
|
||||||
return start();
|
return start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PIThread::start(ThreadFunc func, PISystemTime loop_delay) {
|
bool PIThread::start(ThreadFunc func, PISystemTime loop_delay) {
|
||||||
ret_func = func;
|
ret_func = std::move(func);
|
||||||
delay_ = loop_delay;
|
delay_ = loop_delay;
|
||||||
return start();
|
return start();
|
||||||
}
|
}
|
||||||
@@ -641,7 +641,7 @@ bool PIThread::startOnce() {
|
|||||||
|
|
||||||
|
|
||||||
bool PIThread::startOnce(ThreadFunc func) {
|
bool PIThread::startOnce(ThreadFunc func) {
|
||||||
ret_func = func;
|
ret_func = std::move(func);
|
||||||
return startOnce();
|
return startOnce();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,7 +738,7 @@ bool PIThread::_startThread(void * func) {
|
|||||||
#ifdef FREERTOS
|
#ifdef FREERTOS
|
||||||
|
|
||||||
auto name_ba = createThreadName();
|
auto name_ba = createThreadName();
|
||||||
if (xTaskCreate((__THREAD_FUNC_RET__ (*)(void *))func,
|
if (xTaskCreate((__THREAD_FUNC_RET__(*)(void *))func,
|
||||||
(const char *)name_ba.data(), // A name just for humans
|
(const char *)name_ba.data(), // A name just for humans
|
||||||
128, // This stack size can be checked & adjusted by reading the Stack Highwater
|
128, // This stack size can be checked & adjusted by reading the Stack Highwater
|
||||||
this,
|
this,
|
||||||
@@ -752,7 +752,7 @@ bool PIThread::_startThread(void * func) {
|
|||||||
|
|
||||||
if (PRIVATE->thread) CloseHandle(PRIVATE->thread);
|
if (PRIVATE->thread) CloseHandle(PRIVATE->thread);
|
||||||
# ifdef CC_GCC
|
# ifdef CC_GCC
|
||||||
PRIVATE->thread = (void *)_beginthreadex(0, 0, (__THREAD_FUNC_RET__ (*)(void *))func, this, CREATE_SUSPENDED, 0);
|
PRIVATE->thread = (void *)_beginthreadex(0, 0, (__THREAD_FUNC_RET__(*)(void *))func, this, CREATE_SUSPENDED, 0);
|
||||||
# else
|
# else
|
||||||
PRIVATE->thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)func, this, CREATE_SUSPENDED, 0);
|
PRIVATE->thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)func, this, CREATE_SUSPENDED, 0);
|
||||||
# endif
|
# endif
|
||||||
@@ -766,7 +766,7 @@ bool PIThread::_startThread(void * func) {
|
|||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
pthread_attr_init(&attr);
|
pthread_attr_init(&attr);
|
||||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
int ret = pthread_create(&PRIVATE->thread, &attr, (__THREAD_FUNC_RET__ (*)(void *))func, this);
|
int ret = pthread_create(&PRIVATE->thread, &attr, (__THREAD_FUNC_RET__(*)(void *))func, this);
|
||||||
pthread_attr_destroy(&attr);
|
pthread_attr_destroy(&attr);
|
||||||
// PICout(PICoutManipulators::DefaultControls) << "pthread_create" << PRIVATE->thread;
|
// PICout(PICoutManipulators::DefaultControls) << "pthread_create" << PRIVATE->thread;
|
||||||
// piCout << "started" << PRIVATE->thread;
|
// piCout << "started" << PRIVATE->thread;
|
||||||
@@ -1042,7 +1042,7 @@ void PIThread::runOnce(PIObject * object, const char * handler, const PIString &
|
|||||||
void PIThread::runOnce(std::function<void()> func, const PIString & name) {
|
void PIThread::runOnce(std::function<void()> func, const PIString & name) {
|
||||||
PIThread * t = new PIThread();
|
PIThread * t = new PIThread();
|
||||||
t->setName(name);
|
t->setName(name);
|
||||||
t->setSlot(func);
|
t->setSlot(std::move(func));
|
||||||
#ifndef MICRO_PIP
|
#ifndef MICRO_PIP
|
||||||
__PIThreadCollection::instance()->startedAuto(t);
|
__PIThreadCollection::instance()->startedAuto(t);
|
||||||
CONNECT0(void, t, stopped, __PIThreadCollection::instance(), stoppedAuto);
|
CONNECT0(void, t, stopped, __PIThreadCollection::instance(), stoppedAuto);
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ public:
|
|||||||
//! \~english Waits until the thread starts. Returns \b false if the timeout expires first.
|
//! \~english Waits until the thread starts. Returns \b false if the timeout expires first.
|
||||||
//! \~russian Ожидает запуска потока. Возвращает \b false, если таймаут истек раньше.
|
//! \~russian Ожидает запуска потока. Возвращает \b false, если таймаут истек раньше.
|
||||||
bool waitForStart(PISystemTime timeout = {});
|
bool waitForStart(PISystemTime timeout = {});
|
||||||
|
|
||||||
//! \~english Deprecated overload of \a waitForStart() that accepts milliseconds.
|
//! \~english Deprecated overload of \a waitForStart() that accepts milliseconds.
|
||||||
//! \~russian Устаревшая перегрузка \a waitForStart(), принимающая миллисекунды.
|
//! \~russian Устаревшая перегрузка \a waitForStart(), принимающая миллисекунды.
|
||||||
bool waitForStart(int timeout_msecs) DEPRECATEDM("use waitForStart(PISystemTime)") {
|
bool waitForStart(int timeout_msecs) DEPRECATEDM("use waitForStart(PISystemTime)") {
|
||||||
@@ -226,6 +227,7 @@ public:
|
|||||||
//! \~english Waits for thread completion. Returns \b false if the timeout expires first.
|
//! \~english Waits for thread completion. Returns \b false if the timeout expires first.
|
||||||
//! \~russian Ожидает завершения потока. Возвращает \b false, если таймаут истек раньше.
|
//! \~russian Ожидает завершения потока. Возвращает \b false, если таймаут истек раньше.
|
||||||
bool waitForFinish(PISystemTime timeout = {});
|
bool waitForFinish(PISystemTime timeout = {});
|
||||||
|
|
||||||
//! \~english Deprecated overload of \a waitForFinish() that accepts milliseconds.
|
//! \~english Deprecated overload of \a waitForFinish() that accepts milliseconds.
|
||||||
//! \~russian Устаревшая перегрузка \a waitForFinish(), принимающая миллисекунды.
|
//! \~russian Устаревшая перегрузка \a waitForFinish(), принимающая миллисекунды.
|
||||||
bool waitForFinish(int timeout_msecs) DEPRECATEDM("use waitForFinish(PISystemTime)") {
|
bool waitForFinish(int timeout_msecs) DEPRECATEDM("use waitForFinish(PISystemTime)") {
|
||||||
|
|||||||
@@ -71,8 +71,8 @@
|
|||||||
#include "pispinlock.h"
|
#include "pispinlock.h"
|
||||||
#include "pithread.h"
|
#include "pithread.h"
|
||||||
#include "pithreadnotifier.h"
|
#include "pithreadnotifier.h"
|
||||||
#include "pithreadpoolexecutor.h"
|
|
||||||
#include "pithreadpoolloop.h"
|
#include "pithreadpoolloop.h"
|
||||||
|
#include "pithreadpoolworker.h"
|
||||||
#include "pitimer.h"
|
#include "pitimer.h"
|
||||||
|
|
||||||
#endif // PITHREADMODULE_H
|
#endif // PITHREADMODULE_H
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
PIP - Platform Independent Primitives
|
|
||||||
|
|
||||||
Stephan Fomenko
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "pithreadpoolexecutor.h"
|
|
||||||
|
|
||||||
#include "piliterals_time.h"
|
|
||||||
|
|
||||||
/*! \class PIThreadPoolExecutor
|
|
||||||
* \brief Thread pools address two different problems: they usually provide improved performance when executing large
|
|
||||||
* numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and
|
|
||||||
* managing the resources, including threads, consumed when executing a collection of tasks.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
PIThreadPoolExecutor::PIThreadPoolExecutor(int corePoolSize): isShutdown_(false) {
|
|
||||||
for (int i = 0; i < corePoolSize; ++i) {
|
|
||||||
PIThread * thread = new PIThread([&, i]() {
|
|
||||||
auto runnable = taskQueue.poll(100_ms, std::function<void()>());
|
|
||||||
if (runnable) {
|
|
||||||
runnable();
|
|
||||||
}
|
|
||||||
if (isShutdown_ && taskQueue.size() == 0) threadPool[i]->stop();
|
|
||||||
});
|
|
||||||
threadPool.push_back(thread);
|
|
||||||
thread->start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool PIThreadPoolExecutor::awaitTermination(PISystemTime timeout) {
|
|
||||||
PITimeMeasurer measurer;
|
|
||||||
for (size_t i = 0; i < threadPool.size(); ++i) {
|
|
||||||
auto dif = timeout - measurer.elapsed();
|
|
||||||
if (dif.isNegative()) return false;
|
|
||||||
if (!threadPool[i]->waitForFinish(dif)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIThreadPoolExecutor::shutdownNow() {
|
|
||||||
isShutdown_ = true;
|
|
||||||
for (size_t i = 0; i < threadPool.size(); ++i)
|
|
||||||
threadPool[i]->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PIThreadPoolExecutor::~PIThreadPoolExecutor() {
|
|
||||||
shutdownNow();
|
|
||||||
while (threadPool.size() > 0)
|
|
||||||
delete threadPool.take_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIThreadPoolExecutor::execute(const std::function<void()> & runnable) {
|
|
||||||
if (!isShutdown_) taskQueue.offer(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool PIThreadPoolExecutor::isShutdown() const {
|
|
||||||
return isShutdown_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIThreadPoolExecutor::shutdown() {
|
|
||||||
isShutdown_ = true;
|
|
||||||
}
|
|
||||||
@@ -1,95 +1,23 @@
|
|||||||
//! \~\file pithreadpoolexecutor.h
|
|
||||||
//! \~\ingroup Thread
|
|
||||||
//! \brief
|
|
||||||
//! \~english Thread pool executor
|
|
||||||
//! \~russian Исполнитель пула потоков
|
|
||||||
//!
|
|
||||||
//! \details
|
|
||||||
//! \~english Executes tasks in a pool of worker threads.
|
|
||||||
//! \~russian Выполняет задачи в пуле рабочих потоков.
|
|
||||||
/*
|
|
||||||
PIP - Platform Independent Primitives
|
|
||||||
|
|
||||||
Stephan Fomenko
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PITHREADPOOLEXECUTOR_H
|
#ifndef PITHREADPOOLEXECUTOR_H
|
||||||
#define PITHREADPOOLEXECUTOR_H
|
#define PITHREADPOOLEXECUTOR_H
|
||||||
|
|
||||||
#include "piblockingqueue.h"
|
#include "pithreadpoolworker.h"
|
||||||
#include "pithread.h"
|
|
||||||
|
|
||||||
#include <atomic>
|
namespace {
|
||||||
|
DEPRECATEDM("Use pithreadpoolworker.h") const bool deprecated_header_is_used = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DEPRECATEDM("Use PIThreadPoolWorker") PIThreadPoolExecutor: public PIThreadPoolWorker {
|
||||||
//! \~\ingroup Thread
|
|
||||||
//! \~\brief
|
|
||||||
//! \~english Fixed-size pool of worker threads for fire-and-forget tasks.
|
|
||||||
//! \~russian Фиксированный пул рабочих потоков для задач без ожидания результата.
|
|
||||||
class PIP_EXPORT PIThreadPoolExecutor {
|
|
||||||
public:
|
public:
|
||||||
//! \~english Constructs executor with \a corePoolSize worker threads.
|
PIThreadPoolExecutor(int threads_count): PIThreadPoolWorker(threads_count) { start(); }
|
||||||
//! \~russian Создает исполнитель с \a corePoolSize рабочими потоками.
|
~PIThreadPoolExecutor() { stopAndWait(); }
|
||||||
explicit PIThreadPoolExecutor(int corePoolSize);
|
|
||||||
|
|
||||||
//! \~english Stops worker threads and destroys executor resources.
|
void execute(std::function<void()> runnable) DEPRECATEDM("Use enqueueTask()") { enqueueTask(runnable); }
|
||||||
//! \~russian Останавливает рабочие потоки и уничтожает ресурсы исполнителя.
|
void shutdownNow() DEPRECATEDM("Use stopAndWait()") { stopAndWait(); }
|
||||||
virtual ~PIThreadPoolExecutor();
|
void shutdown() DEPRECATEDM("Use stop()") { stop(); }
|
||||||
|
bool isShutdown() const DEPRECATEDM("Use !isRunning()") { return !isRunning(); }
|
||||||
//! \~\brief
|
bool awaitTermination(PISystemTime timeout) DEPRECATEDM("Use waitForFinish()") { return waitForFinish(timeout); }
|
||||||
//! \~english Submits \a runnable for asynchronous execution by a worker thread.
|
|
||||||
//! \~russian Передает \a runnable на асинхронное выполнение рабочим потоком.
|
|
||||||
//! \details
|
|
||||||
//! \~english
|
|
||||||
//! This is a best-effort fire-and-forget call and does not report whether the task was accepted.
|
|
||||||
//! \After shutdown requests new tasks are ignored.
|
|
||||||
//! \~russian
|
|
||||||
//! Это вызов по принципу best-effort без ожидания результата и без сообщения о том, была ли задача принята.
|
|
||||||
//! \После запроса на завершение новые задачи игнорируются.
|
|
||||||
void execute(const std::function<void()> & runnable);
|
|
||||||
|
|
||||||
//! \~english Requests immediate shutdown and stops worker threads without waiting for queued tasks to finish.
|
|
||||||
//! \~russian Запрашивает немедленное завершение и останавливает рабочие потоки без ожидания завершения задач в очереди.
|
|
||||||
void shutdownNow();
|
|
||||||
|
|
||||||
//! \~\brief
|
|
||||||
//! \~english Requests orderly shutdown: new tasks are rejected and workers stop after the current queue is drained.
|
|
||||||
//! \~russian Запрашивает упорядоченное завершение: новые задачи отклоняются, а рабочие потоки останавливаются после опустошения текущей
|
|
||||||
//! очереди.
|
|
||||||
//! \details
|
|
||||||
//! \~english This method does not wait for worker termination.
|
|
||||||
//! \~russian Этот метод не ожидает завершения рабочих потоков.
|
|
||||||
void shutdown();
|
|
||||||
|
|
||||||
//! \~english Returns \c true after \a shutdown() or \a shutdownNow() has been requested.
|
|
||||||
//! \~russian Возвращает \c true после запроса \a shutdown() или \a shutdownNow().
|
|
||||||
bool isShutdown() const;
|
|
||||||
|
|
||||||
//! \~\brief
|
|
||||||
//! \~english Waits up to \a timeout for all worker threads to finish.
|
|
||||||
//! \~russian Ожидает до \a timeout завершения всех рабочих потоков.
|
|
||||||
//! \return
|
|
||||||
//! \~english \c false if the timeout expires first.
|
|
||||||
//! \~russian \c false, если таймаут истек раньше.
|
|
||||||
bool awaitTermination(PISystemTime timeout);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::atomic_bool isShutdown_;
|
|
||||||
PIBlockingQueue<std::function<void()>> taskQueue;
|
|
||||||
PIVector<PIThread *> threadPool;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PITHREADPOOLEXECUTOR_H
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ PIThreadPoolLoop::~PIThreadPoolLoop() {
|
|||||||
|
|
||||||
|
|
||||||
void PIThreadPoolLoop::setFunction(std::function<void(int)> f) {
|
void PIThreadPoolLoop::setFunction(std::function<void(int)> f) {
|
||||||
func = f;
|
func = std::move(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -163,6 +163,6 @@ void PIThreadPoolLoop::exec(int index_start, int index_count) {
|
|||||||
|
|
||||||
|
|
||||||
void PIThreadPoolLoop::exec(int index_start, int index_count, std::function<void(int)> f) {
|
void PIThreadPoolLoop::exec(int index_start, int index_count, std::function<void(int)> f) {
|
||||||
setFunction(f);
|
setFunction(std::move(f));
|
||||||
exec(index_start, index_count);
|
exec(index_start, index_count);
|
||||||
}
|
}
|
||||||
|
|||||||
238
libs/main/thread/pithreadpoolworker.cpp
Normal file
238
libs/main/thread/pithreadpoolworker.cpp
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
/*
|
||||||
|
PIP - Platform Independent Primitives
|
||||||
|
|
||||||
|
Ivan Pelipenko, Stephan Fomenko
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pithreadpoolworker.h"
|
||||||
|
|
||||||
|
#include "pisysteminfo.h"
|
||||||
|
|
||||||
|
//! \addtogroup Thread
|
||||||
|
//! \{
|
||||||
|
//! \class PIThreadPoolWorker pithreadpoolworker.h
|
||||||
|
//! \~\details
|
||||||
|
//! \~english
|
||||||
|
//! \PIThreadPoolWorker is a class that implements a fixed-size pool of worker threads for general-purpose task execution. It
|
||||||
|
//! allows starting threads, enqueuing tasks as functors or class member methods, and managing their execution. The class provides methods
|
||||||
|
//! to wait for all tasks or specific tasks by ID to complete, as well as to gracefully stop the pool with timeouts. The lifecycle of each
|
||||||
|
//! task can be monitored using the taskStarted and taskFinished events. It is a versatile tool for multithreaded asynchronous operations.
|
||||||
|
//! \~russian
|
||||||
|
//! PIThreadPoolWorker — это класс, реализующий фиксированный пул рабочих потоков для выполнения задач общего назначения. Он
|
||||||
|
//! позволяет запускать потоки, добавлять в очередь задачи в виде функторов или методов класса, а также управлять их выполнением. Класс
|
||||||
|
//! предоставляет методы для ожидания завершения всех задач или отдельных задач по их идентификатору, а также для корректной остановки пула
|
||||||
|
//! с таймаутами. С помощью событий taskStarted и taskFinished можно отслеживать жизненный цикл каждой задачи. Это универсальный инструмент
|
||||||
|
//! для многопоточного асинхронного выполнения операций.
|
||||||
|
//!
|
||||||
|
//! \}
|
||||||
|
|
||||||
|
|
||||||
|
PIThreadPoolWorker::PIThreadPoolWorker(int threads_count) {
|
||||||
|
if (threads_count < 0) threads_count = PISystemInfo::instance()->processorsCount;
|
||||||
|
assertm(threads_count > 0, "Invalid threads count!");
|
||||||
|
for (int i = 0; i < threads_count; ++i) {
|
||||||
|
Worker * w = new Worker();
|
||||||
|
w->thread.setSlot([this, w]() { threadFunc(w); });
|
||||||
|
workers << w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PIThreadPoolWorker::~PIThreadPoolWorker() {
|
||||||
|
piDeleteAllAndClear(workers);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIThreadPoolWorker::start() {
|
||||||
|
for (auto w: workers) {
|
||||||
|
w->thread.start();
|
||||||
|
w->notifier.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIThreadPoolWorker::stop() {
|
||||||
|
for (auto w: workers) {
|
||||||
|
w->thread.stop();
|
||||||
|
w->notifier.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PIThreadPoolWorker::stopAndWait(PISystemTime timeout) {
|
||||||
|
stop();
|
||||||
|
return waitForFinish(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PIThreadPoolWorker::waitForStart(PISystemTime timeout) {
|
||||||
|
PITimeMeasurer tm;
|
||||||
|
for (auto w: workers) {
|
||||||
|
if (timeout.isNull())
|
||||||
|
w->thread.waitForStart();
|
||||||
|
else {
|
||||||
|
auto remains = timeout - tm.elapsed();
|
||||||
|
if (remains.isNegative()) return false;
|
||||||
|
if (!w->thread.waitForStart(remains)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PIThreadPoolWorker::waitForFinish(PISystemTime timeout) {
|
||||||
|
PITimeMeasurer tm;
|
||||||
|
for (auto w: workers) {
|
||||||
|
if (timeout.isNull())
|
||||||
|
w->thread.waitForFinish();
|
||||||
|
else {
|
||||||
|
auto remains = timeout - tm.elapsed();
|
||||||
|
if (remains.isNegative()) return false;
|
||||||
|
if (!w->thread.waitForFinish(remains)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PIThreadPoolWorker::isRunning() const {
|
||||||
|
return workers.every([](Worker * w) { return w->thread.isRunning(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PIThreadPoolWorker::waitForTasks(PISystemTime timeout) {
|
||||||
|
if (!isRunning()) return tasks_queue.getRef()->isEmpty();
|
||||||
|
auto checkWorking = [this] {
|
||||||
|
if (tasks_queue.getRef()->isNotEmpty()) return true;
|
||||||
|
for (auto * w: workers)
|
||||||
|
if (w->in_work > 0) return true;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (timeout.isNull()) {
|
||||||
|
for (;;) {
|
||||||
|
if (!checkWorking()) break;
|
||||||
|
piMinSleep();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
PITimeMeasurer tm;
|
||||||
|
while (tm.elapsed() < timeout) {
|
||||||
|
if (!checkWorking()) return true;
|
||||||
|
piMinSleep();
|
||||||
|
}
|
||||||
|
return tm.elapsed() < timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PIThreadPoolWorker::waitForTask(int64_t id, PISystemTime timeout) {
|
||||||
|
if (!isRunning()) return tasks_queue.getRef()->every([id](const Task & t) { return t.id != id; });
|
||||||
|
auto checkWorking = [this, id] {
|
||||||
|
if (tasks_queue.getRef()->any([id](const Task & t) { return t.id == id; })) return true;
|
||||||
|
for (auto * w: workers)
|
||||||
|
if (w->in_work == id) return true;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (timeout.isNull()) {
|
||||||
|
for (;;) {
|
||||||
|
if (!checkWorking()) break;
|
||||||
|
piMinSleep();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
PITimeMeasurer tm;
|
||||||
|
while (tm.elapsed() < timeout) {
|
||||||
|
if (!checkWorking()) return true;
|
||||||
|
piMinSleep();
|
||||||
|
}
|
||||||
|
return tm.elapsed() < timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIThreadPoolWorker::exec() {
|
||||||
|
start();
|
||||||
|
waitForStart();
|
||||||
|
waitForTasks();
|
||||||
|
stopAndWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int64_t PIThreadPoolWorker::enqueueTask(std::function<void(int64_t)> func, PIObject * context) {
|
||||||
|
if (context) {
|
||||||
|
if (!contexts[context]) {
|
||||||
|
contexts << context;
|
||||||
|
CONNECTL(context, deleted, ([this, context](PIObject *) {
|
||||||
|
contexts.remove(context);
|
||||||
|
auto qref = tasks_queue.getRef();
|
||||||
|
// auto prev_size = qref->size();
|
||||||
|
// piCout << "deleted" << (void *)context << qref->map<void *>([](const Task & t) { return t.context; });
|
||||||
|
qref->removeWhere([context](const Task & t) { return t.context == context; });
|
||||||
|
// piCout << prev_size << qref->size() << qref->map<void *>([](const Task & t) { return t.context; });
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int64_t id = ++next_task_id;
|
||||||
|
Task task;
|
||||||
|
task.id = id;
|
||||||
|
task.func = std::move(func);
|
||||||
|
task.context = context;
|
||||||
|
tasks_queue.getRef()->enqueue(std::move(task));
|
||||||
|
for (auto * w: workers)
|
||||||
|
w->notifier.notify();
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PIThreadPoolWorker::removeTask(int64_t id) {
|
||||||
|
auto qref = tasks_queue.getRef();
|
||||||
|
auto prev_size = qref->size();
|
||||||
|
qref->removeWhere([id](const Task & t) { return t.id == id; });
|
||||||
|
return prev_size != qref->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIThreadPoolWorker::clearTasks() {
|
||||||
|
tasks_queue.getRef()->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PIThreadPoolWorker::TaskStatus PIThreadPoolWorker::taskStatus(int64_t id) const {
|
||||||
|
if (id <= 0) return TaskStatus::Unknown;
|
||||||
|
auto qref = tasks_queue.getRef();
|
||||||
|
for (auto w: workers)
|
||||||
|
if (w->in_work == id) return TaskStatus::InProgress;
|
||||||
|
for (const auto & t: *qref)
|
||||||
|
if (t.id == id) return TaskStatus::Enqueued;
|
||||||
|
return id <= next_task_id ? TaskStatus::DoneOrCancelled : TaskStatus::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIThreadPoolWorker::threadFunc(Worker * w) {
|
||||||
|
w->notifier.wait();
|
||||||
|
if (w->thread.isStopping()) return;
|
||||||
|
Task task;
|
||||||
|
{
|
||||||
|
auto ref = tasks_queue.getRef();
|
||||||
|
if (ref->isEmpty()) return;
|
||||||
|
task = ref->dequeue();
|
||||||
|
}
|
||||||
|
if (!task.isValid()) return;
|
||||||
|
w->in_work = task.id;
|
||||||
|
taskStarted(task.id);
|
||||||
|
task.func(task.id);
|
||||||
|
w->in_work = -1;
|
||||||
|
taskFinished(task.id);
|
||||||
|
w->notifier.notify();
|
||||||
|
}
|
||||||
177
libs/main/thread/pithreadpoolworker.h
Normal file
177
libs/main/thread/pithreadpoolworker.h
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
//! \~\file pithreadpoolworker.h
|
||||||
|
//! \~\ingroup Thread
|
||||||
|
//! \brief
|
||||||
|
//! \~english Thread pool worker
|
||||||
|
//! \~russian Исполнитель пула потоков
|
||||||
|
/*
|
||||||
|
PIP - Platform Independent Primitives
|
||||||
|
|
||||||
|
Ivan Pelipenko, Stephan Fomenko
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PITHREADPOOLWORKER_H
|
||||||
|
#define PITHREADPOOLWORKER_H
|
||||||
|
|
||||||
|
#include "piprotectedvariable.h"
|
||||||
|
#include "pithread.h"
|
||||||
|
|
||||||
|
|
||||||
|
//! \~\ingroup Thread
|
||||||
|
//! \~\brief
|
||||||
|
//! \~english Fixed-size pool of worker threads for generic-purpose tasks.
|
||||||
|
//! \~russian Фиксированный пул рабочих потоков для задач общего назначения.
|
||||||
|
class PIP_EXPORT PIThreadPoolWorker: public PIObject {
|
||||||
|
PIOBJECT(PIThreadPoolWorker)
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! \~english Constructs executor with \a threads_count worker threads. If \a threads_count < 0 processor threads count used.
|
||||||
|
//! \~russian Создает исполнитель с \a threads_count рабочими потоками. Если \a threads_count < 0 используется количество потоков
|
||||||
|
//! процессора.
|
||||||
|
explicit PIThreadPoolWorker(int threads_count = -1);
|
||||||
|
|
||||||
|
//! \~english Destroy worker threads. Call \a stopAndWait() before.
|
||||||
|
//! \~russian Уничтожает рабочие потоки. Вызывайте перед этим \a stopAndWait().
|
||||||
|
virtual ~PIThreadPoolWorker();
|
||||||
|
|
||||||
|
|
||||||
|
//! \~english Task status.
|
||||||
|
//! \~russian Статус задачи.
|
||||||
|
enum class TaskStatus {
|
||||||
|
Unknown /** \~english ID <= 0 or not queued yet \~russian ID <= 0 или не поставлена в очередь */,
|
||||||
|
Enqueued /** \~english Wait for execution \~russian Ожидает выполнения */,
|
||||||
|
InProgress /** \~english In execution now \~russian В процессе выполнения */,
|
||||||
|
DoneOrCancelled /** \~english Done or cancelled \~russian Выполнена или отменена */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//! \~english Starts the threads.
|
||||||
|
//! \~russian Запускает потоки.
|
||||||
|
void start();
|
||||||
|
|
||||||
|
//! \~english Requests graceful threads shutdown.
|
||||||
|
//! \~russian Запрашивает корректное завершение потоков.
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
//! \~english Requests stop and waits for threads completion. Returns \b false if the timeout expires.
|
||||||
|
//! \~russian Запрашивает остановку и ожидает завершения потоков. Возвращает \b false, если таймаут истек.
|
||||||
|
bool stopAndWait(PISystemTime timeout = {});
|
||||||
|
|
||||||
|
//! \~english Waits until the threads starts. Returns \b false if the timeout expires first.
|
||||||
|
//! \~russian Ожидает запуска потоков. Возвращает \b false, если таймаут истек раньше.
|
||||||
|
bool waitForStart(PISystemTime timeout = {});
|
||||||
|
|
||||||
|
//! \~english Waits for threads completion. Returns \b false if the timeout expires first.
|
||||||
|
//! \~russian Ожидает завершения потоков. Возвращает \b false, если таймаут истек раньше.
|
||||||
|
bool waitForFinish(PISystemTime timeout = {});
|
||||||
|
|
||||||
|
//! \~english Returns whether the threads are currently running.
|
||||||
|
//! \~russian Возвращает, выполняются ли потоки в данный момент.
|
||||||
|
bool isRunning() const;
|
||||||
|
|
||||||
|
//! \~english Waits for all tasks completion. Returns \b false if the timeout expires first.
|
||||||
|
//! \~russian Ожидает завершения всех задач. Возвращает \b false, если таймаут истек раньше.
|
||||||
|
bool waitForTasks(PISystemTime timeout = {});
|
||||||
|
|
||||||
|
//! \~english Waits for task with id \a id completion. Returns \b false if the timeout expires first.
|
||||||
|
//! \~russian Ожидает завершения задачи с id \a id. Возвращает \b false, если таймаут истек раньше.
|
||||||
|
bool waitForTask(int64_t id, PISystemTime timeout = {});
|
||||||
|
|
||||||
|
//! \~english Starts threads, wait for all tasks complete and threads stop.
|
||||||
|
//! \~russian Запускает потоки, ожидает завершения всех задач и остановки потоков.
|
||||||
|
void exec();
|
||||||
|
|
||||||
|
|
||||||
|
//! \~english Queue functor to execution. Pass task ID in functor. Returns task ID.
|
||||||
|
//! \~russian Запланировать функтор на выполнение. В функтор передастся ID задачи. Возвращает ID задачи.
|
||||||
|
int64_t enqueueTask(std::function<void(int64_t)> func, PIObject * context = nullptr);
|
||||||
|
|
||||||
|
//! \~english Queue functor to execution. Returns task ID.
|
||||||
|
//! \~russian Запланировать функтор на выполнение. Возвращает ID задачи.
|
||||||
|
int64_t enqueueTask(std::function<void()> func, PIObject * context = nullptr) {
|
||||||
|
return enqueueTask([func](int64_t) { func(); }, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Queue class member method to execution. Pass task ID in method. Returns task ID.
|
||||||
|
//! \~russian Запланировать член-метод класса на выполнение. В метод передастся ID задачи. Возвращает ID задачи.
|
||||||
|
template<typename O>
|
||||||
|
int64_t enqueueTask(O * obj, void (O::*member_func)(int64_t)) {
|
||||||
|
return enqueueTask([obj, member_func](int64_t id) { (obj->*member_func)(id); },
|
||||||
|
PIObject::isPIObject(obj) ? dynamic_cast<PIObject *>(obj) : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Queue class member method to execution. Returns task ID.
|
||||||
|
//! \~russian Запланировать член-метод класса на выполнение. Возвращает ID задачи.
|
||||||
|
template<typename O>
|
||||||
|
int64_t enqueueTask(O * obj, void (O::*member_func)()) {
|
||||||
|
return enqueueTask([obj, member_func](int64_t) { (obj->*member_func)(); },
|
||||||
|
PIObject::isPIObject(obj) ? dynamic_cast<PIObject *>(obj) : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! \~english Remove task with id \a id from queue. Returns if task delete.
|
||||||
|
//! \~russian Удаляет задачу с id \a id из очереди. Возвращиает была ли задача удалена.
|
||||||
|
bool removeTask(int64_t id);
|
||||||
|
|
||||||
|
//! \~english Remove all queued tasks.
|
||||||
|
//! \~russian Удаляет все задачи из очереди.
|
||||||
|
void clearTasks();
|
||||||
|
|
||||||
|
//! \~english Returns task status with id \a id.
|
||||||
|
//! \~russian Возвращиает статус задачи с id \a id.
|
||||||
|
TaskStatus taskStatus(int64_t id) const;
|
||||||
|
|
||||||
|
|
||||||
|
//! \events
|
||||||
|
//! \{
|
||||||
|
|
||||||
|
//! \fn void taskStarted(int64_t id)
|
||||||
|
//! \brief
|
||||||
|
//! \~english Raised on start execution task with id \a id.
|
||||||
|
//! \~russian Вызывается при старте выполнения задачи с id \a id.
|
||||||
|
EVENT1(taskStarted, int64_t, id);
|
||||||
|
|
||||||
|
//! \fn void taskFinished(int64_t id)
|
||||||
|
//! \brief
|
||||||
|
//! \~english Raised on finish execution task with id \a id.
|
||||||
|
//! \~russian Вызывается при завершении выполнения задачи с id \a id.
|
||||||
|
EVENT1(taskFinished, int64_t, id);
|
||||||
|
|
||||||
|
//! \}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Worker {
|
||||||
|
PIThread thread;
|
||||||
|
PIThreadNotifier notifier;
|
||||||
|
std::atomic_int64_t in_work = {-1};
|
||||||
|
};
|
||||||
|
struct Task {
|
||||||
|
bool isValid() const { return func != nullptr; }
|
||||||
|
PIObject * context = nullptr;
|
||||||
|
std::function<void(int64_t)> func = nullptr;
|
||||||
|
int64_t id = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
void threadFunc(Worker * w);
|
||||||
|
|
||||||
|
mutable PIVector<Worker *> workers;
|
||||||
|
mutable PIProtectedVariable<PIQueue<Task>> tasks_queue;
|
||||||
|
|
||||||
|
PISet<PIObject *> contexts;
|
||||||
|
std::atomic_int64_t next_task_id = {0};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // PITHREADPOOLWORKER_H
|
||||||
@@ -122,13 +122,13 @@ PITimer::PITimer(): PIObject() {
|
|||||||
|
|
||||||
PITimer::PITimer(std::function<void(int)> func) {
|
PITimer::PITimer(std::function<void(int)> func) {
|
||||||
initFirst();
|
initFirst();
|
||||||
ret_func = func;
|
ret_func_delim = std::move(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PITimer::PITimer(std::function<void()> func) {
|
PITimer::PITimer(std::function<void()> func) {
|
||||||
initFirst();
|
initFirst();
|
||||||
ret_func = [func](int) { func(); };
|
ret_func = std::move(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -224,7 +224,8 @@ void PITimer::adjustTimes() {
|
|||||||
void PITimer::execTick() {
|
void PITimer::execTick() {
|
||||||
if (!isRunning()) return;
|
if (!isRunning()) return;
|
||||||
if (lockRun) lock();
|
if (lockRun) lock();
|
||||||
if (ret_func) ret_func(1);
|
if (ret_func) ret_func();
|
||||||
|
if (ret_func_delim) ret_func_delim(1);
|
||||||
tick(1);
|
tick(1);
|
||||||
tickEvent(1);
|
tickEvent(1);
|
||||||
if (callEvents) maybeCallQueuedEvents();
|
if (callEvents) maybeCallQueuedEvents();
|
||||||
@@ -233,8 +234,8 @@ void PITimer::execTick() {
|
|||||||
i.tick = 0;
|
i.tick = 0;
|
||||||
if (i.func)
|
if (i.func)
|
||||||
i.func(i.delim);
|
i.func(i.delim);
|
||||||
else if (ret_func)
|
else if (ret_func_delim)
|
||||||
ret_func(i.delim);
|
ret_func_delim(i.delim);
|
||||||
tick(i.delim);
|
tick(i.delim);
|
||||||
tickEvent(i.delim);
|
tickEvent(i.delim);
|
||||||
}
|
}
|
||||||
@@ -262,7 +263,7 @@ bool PITimer::start(PISystemTime interval) {
|
|||||||
bool PITimer::start(PISystemTime interval, std::function<void()> func) {
|
bool PITimer::start(PISystemTime interval, std::function<void()> func) {
|
||||||
if (isRunning()) stopAndWait();
|
if (isRunning()) stopAndWait();
|
||||||
setInterval(interval);
|
setInterval(interval);
|
||||||
setSlot(func);
|
setSlot(std::move(func));
|
||||||
return start();
|
return start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,8 +274,20 @@ void PITimer::stopAndWait(PISystemTime timeout) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PITimer::setSlot(std::function<void()> func) {
|
||||||
|
ret_func_delim = nullptr;
|
||||||
|
ret_func = std::move(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PITimer::setSlot(std::function<void(int)> func) {
|
||||||
|
ret_func = nullptr;
|
||||||
|
ret_func_delim = std::move(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void PITimer::addDelimiter(int delim, std::function<void(int)> func) {
|
void PITimer::addDelimiter(int delim, std::function<void(int)> func) {
|
||||||
delims << Delimiter(func, delim);
|
delims << Delimiter(std::move(func), delim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -110,11 +110,6 @@ public:
|
|||||||
//! \~english Deprecated overload of \a start() that accepts milliseconds.
|
//! \~english Deprecated overload of \a start() that accepts milliseconds.
|
||||||
//! \~russian Устаревшая перегрузка \a start(), принимающая миллисекунды.
|
//! \~russian Устаревшая перегрузка \a start(), принимающая миллисекунды.
|
||||||
bool start(double interval_ms) DEPRECATEDM("use start(PISystemTime)") { return start(PISystemTime::fromMilliseconds(interval_ms)); }
|
bool start(double interval_ms) DEPRECATEDM("use start(PISystemTime)") { return start(PISystemTime::fromMilliseconds(interval_ms)); }
|
||||||
EVENT_HANDLER0(bool, start);
|
|
||||||
|
|
||||||
EVENT_HANDLER0(bool, restart);
|
|
||||||
|
|
||||||
EVENT_HANDLER0(void, stop);
|
|
||||||
|
|
||||||
//! \~english Deprecated overload of \a stopAndWait() that accepts milliseconds.
|
//! \~english Deprecated overload of \a stopAndWait() that accepts milliseconds.
|
||||||
//! \~russian Устаревшая перегрузка \a stopAndWait(), принимающая миллисекунды.
|
//! \~russian Устаревшая перегрузка \a stopAndWait(), принимающая миллисекунды.
|
||||||
@@ -126,22 +121,19 @@ public:
|
|||||||
|
|
||||||
//! \~english Sets a tick callback that ignores the delimiter value.
|
//! \~english Sets a tick callback that ignores the delimiter value.
|
||||||
//! \~russian Устанавливает обратный вызов тика, игнорирующий значение делителя.
|
//! \~russian Устанавливает обратный вызов тика, игнорирующий значение делителя.
|
||||||
void setSlot(std::function<void()> func) {
|
void setSlot(std::function<void()> func);
|
||||||
ret_func = [func](int) { func(); };
|
|
||||||
}
|
|
||||||
|
|
||||||
//! \~english Sets a tick callback that receives the current delimiter value.
|
//! \~english Sets a tick callback that receives the current delimiter value.
|
||||||
//! \~russian Устанавливает обратный вызов тика, принимающий текущее значение делителя.
|
//! \~russian Устанавливает обратный вызов тика, принимающий текущее значение делителя.
|
||||||
void setSlot(std::function<void(int)> func) { ret_func = func; }
|
void setSlot(std::function<void(int)> func);
|
||||||
|
|
||||||
//! \~english Enables locking of the internal mutex around tick processing.
|
//! \~english Enables locking of the internal mutex around tick processing.
|
||||||
//! \~russian Включает блокировку внутреннего мьютекса вокруг обработки тиков.
|
//! \~russian Включает блокировку внутреннего мьютекса вокруг обработки тиков.
|
||||||
void needLockRun(bool need) { lockRun = need; }
|
void needLockRun(bool need) { lockRun = need; }
|
||||||
EVENT_HANDLER0(void, lock) { mutex_.lock(); }
|
|
||||||
EVENT_HANDLER0(void, unlock) { mutex_.unlock(); }
|
|
||||||
|
|
||||||
//! \~english Returns whether the timer drains queued delivery for itself as performer on each main tick. By default \b true.
|
//! \~english Returns whether the timer drains queued delivery for itself as performer on each main tick. By default \b true.
|
||||||
//! \~russian Возвращает, должен ли таймер обрабатывать отложенную доставку для себя как исполнителя на каждом основном тике. По умолчанию \b true.
|
//! \~russian Возвращает, должен ли таймер обрабатывать отложенную доставку для себя как исполнителя на каждом основном тике. По
|
||||||
|
//! умолчанию \b true.
|
||||||
bool isCallQueuedEvents() const { return callEvents; }
|
bool isCallQueuedEvents() const { return callEvents; }
|
||||||
|
|
||||||
//! \~english Enables or disables queued-delivery draining through \a maybeCallQueuedEvents() on each main tick.
|
//! \~english Enables or disables queued-delivery draining through \a maybeCallQueuedEvents() on each main tick.
|
||||||
@@ -164,9 +156,6 @@ public:
|
|||||||
//! \~russian Удаляет все делители со значением \a delim.
|
//! \~russian Удаляет все делители со значением \a delim.
|
||||||
void removeDelimiter(int delim);
|
void removeDelimiter(int delim);
|
||||||
|
|
||||||
EVENT_HANDLER0(void, clearDelimiters) { delims.clear(); }
|
|
||||||
|
|
||||||
EVENT1(tickEvent, int, delimiter);
|
|
||||||
|
|
||||||
//! \handlers
|
//! \handlers
|
||||||
//! \{
|
//! \{
|
||||||
@@ -175,31 +164,37 @@ public:
|
|||||||
//! \brief
|
//! \brief
|
||||||
//! \~english Starts the timer with the current \a interval().
|
//! \~english Starts the timer with the current \a interval().
|
||||||
//! \~russian Запускает таймер с текущим значением \a interval().
|
//! \~russian Запускает таймер с текущим значением \a interval().
|
||||||
|
EVENT_HANDLER0(bool, start);
|
||||||
|
|
||||||
//! \fn bool restart()
|
//! \fn bool restart()
|
||||||
//! \brief
|
//! \brief
|
||||||
//! \~english Stops the timer, then starts it again with the current \a interval().
|
//! \~english Stops the timer, then starts it again with the current \a interval().
|
||||||
//! \~russian Останавливает таймер, затем снова запускает его с текущим значением \a interval().
|
//! \~russian Останавливает таймер, затем снова запускает его с текущим значением \a interval().
|
||||||
|
EVENT_HANDLER0(bool, restart);
|
||||||
|
|
||||||
//! \fn bool stop()
|
//! \fn bool stop()
|
||||||
//! \brief
|
//! \brief
|
||||||
//! \~english Requests stop and wakes the timer thread, but does not wait for completion.
|
//! \~english Requests stop and wakes the timer thread, but does not wait for completion.
|
||||||
//! \~russian Запрашивает остановку и пробуждает поток таймера, но не ожидает завершения.
|
//! \~russian Запрашивает остановку и пробуждает поток таймера, но не ожидает завершения.
|
||||||
|
EVENT_HANDLER0(void, stop);
|
||||||
|
|
||||||
//! \fn void clearDelimiters()
|
//! \fn void clearDelimiters()
|
||||||
//! \brief
|
//! \brief
|
||||||
//! \~english Remove all frequency delimiters
|
//! \~english Remove all frequency delimiters
|
||||||
//! \~russian Удаляет все делители частоты
|
//! \~russian Удаляет все делители частоты
|
||||||
|
EVENT_HANDLER0(void, clearDelimiters) { delims.clear(); }
|
||||||
|
|
||||||
//! \fn void lock()
|
//! \fn void lock()
|
||||||
//! \brief
|
//! \brief
|
||||||
//! \~english Locks the internal timer mutex.
|
//! \~english Locks the internal timer mutex.
|
||||||
//! \~russian Блокирует внутренний мьютекс таймера.
|
//! \~russian Блокирует внутренний мьютекс таймера.
|
||||||
|
EVENT_HANDLER0(void, lock) { mutex_.lock(); }
|
||||||
|
|
||||||
//! \fn void unlock()
|
//! \fn void unlock()
|
||||||
//! \brief
|
//! \brief
|
||||||
//! \~english Unlocks the internal timer mutex.
|
//! \~english Unlocks the internal timer mutex.
|
||||||
//! \~russian Разблокирует внутренний мьютекс таймера.
|
//! \~russian Разблокирует внутренний мьютекс таймера.
|
||||||
|
EVENT_HANDLER0(void, unlock) { mutex_.unlock(); }
|
||||||
|
|
||||||
//! \}
|
//! \}
|
||||||
//! \events
|
//! \events
|
||||||
@@ -214,14 +209,14 @@ public:
|
|||||||
//! "delimiter" is the frequency delimiter, 1 for the main loop.
|
//! "delimiter" is the frequency delimiter, 1 for the main loop.
|
||||||
//! \~russian
|
//! \~russian
|
||||||
//! "delimiter" - делитель частоты, 1 для основного цикла.
|
//! "delimiter" - делитель частоты, 1 для основного цикла.
|
||||||
|
EVENT1(tickEvent, int, delimiter);
|
||||||
|
|
||||||
//! \}
|
//! \}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct PIP_EXPORT Delimiter {
|
struct PIP_EXPORT Delimiter {
|
||||||
Delimiter(std::function<void(int)> func_ = nullptr, int delim_ = 1) {
|
Delimiter(std::function<void(int)> func_ = nullptr, int delim_ = 1) {
|
||||||
func = func_;
|
func = std::move(func_);
|
||||||
delim = delim_;
|
delim = delim_;
|
||||||
}
|
}
|
||||||
std::function<void(int)> func;
|
std::function<void(int)> func;
|
||||||
@@ -245,7 +240,8 @@ protected:
|
|||||||
PIMutex mutex_;
|
PIMutex mutex_;
|
||||||
PISystemTime m_interval, m_interval_x5;
|
PISystemTime m_interval, m_interval_x5;
|
||||||
PISystemTime m_time_next;
|
PISystemTime m_time_next;
|
||||||
std::function<void(int)> ret_func = nullptr;
|
std::function<void()> ret_func = nullptr;
|
||||||
|
std::function<void(int)> ret_func_delim = nullptr;
|
||||||
PIVector<Delimiter> delims;
|
PIVector<Delimiter> delims;
|
||||||
PIConditionVariable event;
|
PIConditionVariable event;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ public:
|
|||||||
//! Метод возвращает **false** при любом условии для пустого массива.
|
//! Метод возвращает **false** при любом условии для пустого массива.
|
||||||
//! \~\details
|
//! \~\details
|
||||||
//! \~\sa \a every(), \a contains(), \a entries(), \a forEach()
|
//! \~\sa \a every(), \a contains(), \a entries(), \a forEach()
|
||||||
inline bool any(std::function<bool(uchar e)> test) const { return d.any(test); }
|
inline bool any(std::function<bool(uchar e)> test) const { return d.any(std::move(test)); }
|
||||||
|
|
||||||
//! \~english Tests whether all elements in the array passes the test
|
//! \~english Tests whether all elements in the array passes the test
|
||||||
//! implemented by the provided function `test`.
|
//! implemented by the provided function `test`.
|
||||||
@@ -213,7 +213,7 @@ public:
|
|||||||
//! Метод возвращает **true** при любом условии для пустого массива.
|
//! Метод возвращает **true** при любом условии для пустого массива.
|
||||||
//! \~\details
|
//! \~\details
|
||||||
//! \~\sa \a any(), \a contains(), \a entries(), \a forEach()
|
//! \~\sa \a any(), \a contains(), \a entries(), \a forEach()
|
||||||
inline bool every(std::function<bool(uchar e)> test) const { return d.every(test); }
|
inline bool every(std::function<bool(uchar e)> test) const { return d.every(std::move(test)); }
|
||||||
|
|
||||||
//! \~english Full access to element by `index`.
|
//! \~english Full access to element by `index`.
|
||||||
//! \~russian Полный доступ к элементу по индексу `index`.
|
//! \~russian Полный доступ к элементу по индексу `index`.
|
||||||
@@ -340,7 +340,7 @@ public:
|
|||||||
//! Обратите внимание: если индекс отрицателен, массив всё равно просматривается от начала к концу.
|
//! Обратите внимание: если индекс отрицателен, массив всё равно просматривается от начала к концу.
|
||||||
//! Значение по умолчанию равно 0, что означает, что просматривается весь массив.
|
//! Значение по умолчанию равно 0, что означает, что просматривается весь массив.
|
||||||
//! \~\sa \a every(), \a any(), \a contains(), \a indexWhere()
|
//! \~\sa \a every(), \a any(), \a contains(), \a indexWhere()
|
||||||
inline int entries(std::function<bool(uchar e)> test, ssize_t start = 0) const { return d.entries(test, start); }
|
inline int entries(std::function<bool(uchar e)> test, ssize_t start = 0) const { return d.entries(std::move(test), start); }
|
||||||
|
|
||||||
bool startsWith(const PIByteArray & o) const;
|
bool startsWith(const PIByteArray & o) const;
|
||||||
|
|
||||||
@@ -405,7 +405,9 @@ public:
|
|||||||
//! piCout << v.indexWhere([](const uchar & s){return s > 10;}); // -1
|
//! piCout << v.indexWhere([](const uchar & s){return s > 10;}); // -1
|
||||||
//! \endcode
|
//! \endcode
|
||||||
//! \~\sa \a indexOf(), \a lastIndexOf(), \a lastIndexWhere(), \a contains()
|
//! \~\sa \a indexOf(), \a lastIndexOf(), \a lastIndexWhere(), \a contains()
|
||||||
inline ssize_t indexWhere(std::function<bool(const uchar & e)> test, ssize_t start = 0) const { return d.indexWhere(test, start); }
|
inline ssize_t indexWhere(std::function<bool(const uchar & e)> test, ssize_t start = 0) const {
|
||||||
|
return d.indexWhere(std::move(test), start);
|
||||||
|
}
|
||||||
|
|
||||||
//! \~english Returns the last index at which a given element `e`
|
//! \~english Returns the last index at which a given element `e`
|
||||||
//! can be found in the array, or `-1` if it is not present.
|
//! can be found in the array, or `-1` if it is not present.
|
||||||
@@ -469,7 +471,7 @@ public:
|
|||||||
//! и означает, что просматривается весь массив.
|
//! и означает, что просматривается весь массив.
|
||||||
//! \~\sa \a indexOf(), \a lastIndexOf(), \a indexWhere(), \a contains()
|
//! \~\sa \a indexOf(), \a lastIndexOf(), \a indexWhere(), \a contains()
|
||||||
inline ssize_t lastIndexWhere(std::function<bool(const uchar & e)> test, ssize_t start = -1) const {
|
inline ssize_t lastIndexWhere(std::function<bool(const uchar & e)> test, ssize_t start = -1) const {
|
||||||
return d.lastIndexWhere(test, start);
|
return d.lastIndexWhere(std::move(test), start);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \~english Pointer to array
|
//! \~english Pointer to array
|
||||||
@@ -525,7 +527,7 @@ public:
|
|||||||
//! \~\details
|
//! \~\details
|
||||||
//! \~\sa \a resize()
|
//! \~\sa \a resize()
|
||||||
inline PIByteArray & fill(std::function<uchar(size_t i)> f) {
|
inline PIByteArray & fill(std::function<uchar(size_t i)> f) {
|
||||||
d.fill(f);
|
d.fill(std::move(f));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,7 +572,7 @@ public:
|
|||||||
//! лишние элементы удаляются с конца массива.
|
//! лишние элементы удаляются с конца массива.
|
||||||
//! \~\sa \a size(), \a clear()
|
//! \~\sa \a size(), \a clear()
|
||||||
inline PIByteArray & resize(size_t new_size, std::function<uchar(size_t i)> f) {
|
inline PIByteArray & resize(size_t new_size, std::function<uchar(size_t i)> f) {
|
||||||
d.resize(new_size, f);
|
d.resize(new_size, std::move(f));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,7 +740,7 @@ public:
|
|||||||
//! \~\details
|
//! \~\details
|
||||||
//! \~\sa \a remove(), \a removeOne(), \a removeWhere()
|
//! \~\sa \a remove(), \a removeOne(), \a removeWhere()
|
||||||
inline PIByteArray & removeWhere(std::function<bool(uchar e)> test) {
|
inline PIByteArray & removeWhere(std::function<bool(uchar e)> test) {
|
||||||
d.removeWhere(test);
|
d.removeWhere(std::move(test));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -950,7 +952,7 @@ public:
|
|||||||
//! piCout << v2; // {3, 5, 7}
|
//! piCout << v2; // {3, 5, 7}
|
||||||
//! \endcode
|
//! \endcode
|
||||||
//! \~\sa \a map(), \a any(), \a every()
|
//! \~\sa \a map(), \a any(), \a every()
|
||||||
inline PIByteArray filter(std::function<bool(const uchar & e)> test) const { return PIByteArray(d.filter(test)); }
|
inline PIByteArray filter(std::function<bool(const uchar & e)> test) const { return PIByteArray(d.filter(std::move(test))); }
|
||||||
|
|
||||||
//! \~english Execute function `void f(const uchar & e)` for every element in array.
|
//! \~english Execute function `void f(const uchar & e)` for every element in array.
|
||||||
//! \~russian Выполняет функцию `void f(const uchar & e)` для каждого элемента массива.
|
//! \~russian Выполняет функцию `void f(const uchar & e)` для каждого элемента массива.
|
||||||
@@ -966,7 +968,7 @@ public:
|
|||||||
//! piCout << s; // 15
|
//! piCout << s; // 15
|
||||||
//! \endcode
|
//! \endcode
|
||||||
//! \~\sa \a filter(), \a map(), \a reduce(), \a any(), \a every()
|
//! \~\sa \a filter(), \a map(), \a reduce(), \a any(), \a every()
|
||||||
inline void forEach(std::function<void(const uchar & e)> f) const { d.forEach(f); }
|
inline void forEach(std::function<void(const uchar & e)> f) const { d.forEach(std::move(f)); }
|
||||||
|
|
||||||
//! \~english Execute function `void f(uchar & e)` for every element in array.
|
//! \~english Execute function `void f(uchar & e)` for every element in array.
|
||||||
//! \~russian Выполняет функцию `void f(uchar & e)` для каждого элемента массива.
|
//! \~russian Выполняет функцию `void f(uchar & e)` для каждого элемента массива.
|
||||||
@@ -982,7 +984,7 @@ public:
|
|||||||
//! \endcode
|
//! \endcode
|
||||||
//! \~\sa \a filter(), \a map(), \a reduce(), \a any(), \a every()
|
//! \~\sa \a filter(), \a map(), \a reduce(), \a any(), \a every()
|
||||||
inline PIByteArray & forEach(std::function<void(uchar & e)> f) {
|
inline PIByteArray & forEach(std::function<void(uchar & e)> f) {
|
||||||
d.forEach(f);
|
d.forEach(std::move(f));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1005,7 +1007,7 @@ public:
|
|||||||
//! \~\sa \a forEach(), \a reduce()
|
//! \~\sa \a forEach(), \a reduce()
|
||||||
template<typename ST>
|
template<typename ST>
|
||||||
inline PIDeque<ST> map(std::function<ST(const uchar & e)> f) const {
|
inline PIDeque<ST> map(std::function<ST(const uchar & e)> f) const {
|
||||||
return d.map<ST>(f);
|
return d.map<ST>(std::move(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \~english Applies the function `ST f(const uchar & e, const ST & acc)`
|
//! \~english Applies the function `ST f(const uchar & e, const ST & acc)`
|
||||||
@@ -1051,7 +1053,7 @@ public:
|
|||||||
//! \~\sa \a forEach(), \a map()
|
//! \~\sa \a forEach(), \a map()
|
||||||
template<typename ST>
|
template<typename ST>
|
||||||
inline ST reduce(std::function<ST(const uchar & e, const ST & acc)> f, const ST & initial = ST()) const {
|
inline ST reduce(std::function<ST(const uchar & e, const ST & acc)> f, const ST & initial = ST()) const {
|
||||||
return d.reduce<ST>(f, initial);
|
return d.reduce<ST>(std::move(f), initial);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \~english Convert data to Base 64 and return this byte array
|
//! \~english Convert data to Base 64 and return this byte array
|
||||||
@@ -1103,9 +1105,18 @@ public:
|
|||||||
//! \~english Add to the end byte array "data"
|
//! \~english Add to the end byte array "data"
|
||||||
//! \~russian Добавляет в конец массива содержимое массива "data"
|
//! \~russian Добавляет в конец массива содержимое массива "data"
|
||||||
PIByteArray & append(const PIByteArray & data_) {
|
PIByteArray & append(const PIByteArray & data_) {
|
||||||
|
#ifdef CC_GCC
|
||||||
|
# pragma GCC diagnostic push
|
||||||
|
# pragma GCC diagnostic ignored "-Wstringop-overflow"
|
||||||
|
# pragma GCC diagnostic ignored "-Warray-bounds"
|
||||||
|
# pragma GCC diagnostic ignored "-Wrestrict"
|
||||||
|
#endif
|
||||||
const size_t ps = size();
|
const size_t ps = size();
|
||||||
enlarge(data_.size_s());
|
enlarge(data_.size_s());
|
||||||
memcpy(data(ps), data_.data(), data_.size());
|
memcpy(data(ps), data_.data(), data_.size());
|
||||||
|
#ifdef CC_GCC
|
||||||
|
# pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ extern clock_serv_t __pi_mac_clock;
|
|||||||
//! Используйте этот метод для ожидания разниц системных времен или своего времени.
|
//! Используйте этот метод для ожидания разниц системных времен или своего времени.
|
||||||
//! Если метод будет вызван для системного времени \a PISystemTime::current(), то
|
//! Если метод будет вызван для системного времени \a PISystemTime::current(), то
|
||||||
//! ожидание будет почти бесконечным
|
//! ожидание будет почти бесконечным
|
||||||
void PISystemTime::sleep() {
|
void PISystemTime::sleep() const {
|
||||||
piUSleep(piFloord(toMicroseconds()));
|
piUSleep(piFloord(toMicroseconds()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ void PISystemTime::toTimespec(void * ts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PISystemTime::Frequency PISystemTime::toFrequency() {
|
PISystemTime::Frequency PISystemTime::toFrequency() const {
|
||||||
return PISystemTime::Frequency::fromSystemTime(*this);
|
return PISystemTime::Frequency::fromSystemTime(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ public:
|
|||||||
|
|
||||||
//! \~english Sleep for this time
|
//! \~english Sleep for this time
|
||||||
//! \~russian Ожидать это время
|
//! \~russian Ожидать это время
|
||||||
void sleep();
|
void sleep() const;
|
||||||
|
|
||||||
//! \~english On *nix system assign current value to timespec struct
|
//! \~english On *nix system assign current value to timespec struct
|
||||||
//! \~russian На *nix системах присваивает время к timespec структуре
|
//! \~russian На *nix системах присваивает время к timespec структуре
|
||||||
@@ -261,7 +261,7 @@ public:
|
|||||||
|
|
||||||
//! \~english Returns \a Frequency that corresponds this time interval
|
//! \~english Returns \a Frequency that corresponds this time interval
|
||||||
//! \~russian Возвращает \a Frequency соответствующую этому временному интервалу
|
//! \~russian Возвращает \a Frequency соответствующую этому временному интервалу
|
||||||
PISystemTime::Frequency toFrequency();
|
PISystemTime::Frequency toFrequency() const;
|
||||||
|
|
||||||
//! \~english Returns "yyyy-MM-dd hh:mm:ss.zzz" for absolute time and "<V> <d|h|m|s|ms|us|ns> ..." for relative
|
//! \~english Returns "yyyy-MM-dd hh:mm:ss.zzz" for absolute time and "<V> <d|h|m|s|ms|us|ns> ..." for relative
|
||||||
//! \~russian Возвращает "yyyy-MM-dd hh:mm:ss.zzz" для абсолютного времени и "<V> <d|h|m|s|ms|us|ns> ..." для относительного
|
//! \~russian Возвращает "yyyy-MM-dd hh:mm:ss.zzz" для абсолютного времени и "<V> <d|h|m|s|ms|us|ns> ..." для относительного
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ PIValueTree & PIValueTree::remove(const PIString & name) {
|
|||||||
|
|
||||||
|
|
||||||
void PIValueTree::forEachRecursive(std::function<void(const PIValueTree &, const PIString &)> func) {
|
void PIValueTree::forEachRecursive(std::function<void(const PIValueTree &, const PIString &)> func) {
|
||||||
forEachRecursiveInternal(func);
|
forEachRecursiveInternal(std::move(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ FetchContent_Declare(
|
|||||||
)
|
)
|
||||||
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
||||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||||
|
set(BUILD_GMOCK OFF CACHE BOOL "Build Google Mock" FORCE)
|
||||||
FetchContent_MakeAvailable(googletest)
|
FetchContent_MakeAvailable(googletest)
|
||||||
|
|
||||||
enable_testing()
|
enable_testing()
|
||||||
@@ -25,15 +26,17 @@ macro(pip_test NAME)
|
|||||||
set(_target pip_${NAME}_test)
|
set(_target pip_${NAME}_test)
|
||||||
add_executable(${_target} ${_CPPS} ${_HDRS})
|
add_executable(${_target} ${_CPPS} ${_HDRS})
|
||||||
target_link_libraries(${_target} pip ${ARGN} gtest_main)
|
target_link_libraries(${_target} pip ${ARGN} gtest_main)
|
||||||
|
if (TESTS_RUN)
|
||||||
gtest_discover_tests(${_target})
|
gtest_discover_tests(${_target})
|
||||||
|
endif()
|
||||||
list(APPEND PIP_TESTS_LIST "${NAME}")
|
list(APPEND PIP_TESTS_LIST "${NAME}")
|
||||||
set(PIP_TESTS_LIST ${PIP_TESTS_LIST} PARENT_SCOPE)
|
set(PIP_TESTS_LIST ${PIP_TESTS_LIST} PARENT_SCOPE)
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
#pip_test(concurrent)
|
|
||||||
pip_test(math)
|
pip_test(math)
|
||||||
pip_test(core)
|
pip_test(core)
|
||||||
pip_test(piobject)
|
pip_test(piobject)
|
||||||
pip_test(client_server pip_client_server)
|
pip_test(client_server pip_client_server)
|
||||||
pip_test(io)
|
pip_test(io)
|
||||||
pip_test(system)
|
pip_test(system)
|
||||||
|
pip_test(thread)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ Client * createAndConnectClient() {
|
|||||||
TEST(ClientServer, OneClient) {
|
TEST(ClientServer, OneClient) {
|
||||||
auto const loop_timeout = 1000_ms;
|
auto const loop_timeout = 1000_ms;
|
||||||
auto s = createServer<false, true>();
|
auto s = createServer<false, true>();
|
||||||
|
piMinSleep();
|
||||||
auto c = createAndConnectClient<TestClient<false, false>>();
|
auto c = createAndConnectClient<TestClient<false, false>>();
|
||||||
|
|
||||||
waitLoop([s]() { return s->clientsCount() > 0; }, loop_timeout);
|
waitLoop([s]() { return s->clientsCount() > 0; }, loop_timeout);
|
||||||
@@ -102,10 +103,11 @@ int getClientsPings(const PIVector<ClientSendThread *> & clients) {
|
|||||||
|
|
||||||
|
|
||||||
TEST(ClientServer, ManyClients) {
|
TEST(ClientServer, ManyClients) {
|
||||||
auto const loop_timeout = 100_ms;
|
auto const loop_timeout = 1_s;
|
||||||
constexpr int clients_count = 20;
|
constexpr int clients_count = 20;
|
||||||
PIVector<ClientSendThread *> clients;
|
PIVector<ClientSendThread *> clients;
|
||||||
auto s = createServer<false, false, 100_KiB>();
|
auto s = createServer<false, false, 100_KiB>();
|
||||||
|
piMinSleep();
|
||||||
|
|
||||||
piForTimes(clients_count) {
|
piForTimes(clients_count) {
|
||||||
clients.append(new ClientSendThread());
|
clients.append(new ClientSendThread());
|
||||||
@@ -137,8 +139,9 @@ TEST(ClientServer, ManyClients) {
|
|||||||
for (const auto c: clients) {
|
for (const auto c: clients) {
|
||||||
c->startSend();
|
c->startSend();
|
||||||
}
|
}
|
||||||
(100_ms).sleep();
|
waitLoop([&clients]() { return getClientsPings(clients) > clients_count * 2; }, loop_timeout);
|
||||||
EXPECT_TRUE(getClientsPings(clients) > clients_count * 2);
|
EXPECT_TRUE(getClientsPings(clients) > clients_count * 2);
|
||||||
|
waitLoop([s]() { return getServerPongs(s) > clients_count * 2; }, loop_timeout);
|
||||||
EXPECT_TRUE(getServerPongs(s) > clients_count * 2);
|
EXPECT_TRUE(getServerPongs(s) > clients_count * 2);
|
||||||
piDeleteAllAndClear(clients);
|
piDeleteAllAndClear(clients);
|
||||||
waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout);
|
waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout);
|
||||||
@@ -147,7 +150,7 @@ TEST(ClientServer, ManyClients) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(ClientServer, DynamicClients) {
|
TEST(ClientServer, DynamicClients) {
|
||||||
auto const loop_timeout = 100_ms;
|
auto const loop_timeout = 3_s;
|
||||||
constexpr int clients_count = 20;
|
constexpr int clients_count = 20;
|
||||||
PIVector<ClientSendThread *> clients;
|
PIVector<ClientSendThread *> clients;
|
||||||
PIMutex clients_mutex;
|
PIMutex clients_mutex;
|
||||||
@@ -160,7 +163,6 @@ TEST(ClientServer, DynamicClients) {
|
|||||||
clients_mutex.lock();
|
clients_mutex.lock();
|
||||||
clients << c;
|
clients << c;
|
||||||
clients_mutex.unlock();
|
clients_mutex.unlock();
|
||||||
piCout << "new client" << clients.size();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
piForTimes(clients_count) {
|
piForTimes(clients_count) {
|
||||||
@@ -178,9 +180,8 @@ TEST(ClientServer, DynamicClients) {
|
|||||||
piForTimes(new_cnt) {
|
piForTimes(new_cnt) {
|
||||||
spawnClient();
|
spawnClient();
|
||||||
}
|
}
|
||||||
piCout << "+++++++";
|
|
||||||
},
|
},
|
||||||
12_Hz);
|
120_Hz);
|
||||||
|
|
||||||
deleteThread.start(
|
deleteThread.start(
|
||||||
[&clients, &clients_mutex]() {
|
[&clients, &clients_mutex]() {
|
||||||
@@ -194,26 +195,17 @@ TEST(ClientServer, DynamicClients) {
|
|||||||
clients_mutex.unlock();
|
clients_mutex.unlock();
|
||||||
if (c) {
|
if (c) {
|
||||||
delete c;
|
delete c;
|
||||||
piCout << "remove client" << clients.size();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
piCout << "----------";
|
|
||||||
},
|
},
|
||||||
13_Hz);
|
130_Hz);
|
||||||
|
|
||||||
(2_s).sleep();
|
|
||||||
|
|
||||||
|
waitLoop([s]() { return s->clientsCount() >= 10; }, loop_timeout);
|
||||||
EXPECT_GE(s->clientsCount(), 10);
|
EXPECT_GE(s->clientsCount(), 10);
|
||||||
|
|
||||||
piCout << "now clients" << clients.size();
|
|
||||||
|
|
||||||
|
|
||||||
deleteThread.stopAndWait();
|
deleteThread.stopAndWait();
|
||||||
spawnThread.stopAndWait();
|
spawnThread.stopAndWait();
|
||||||
|
|
||||||
|
|
||||||
piCout << "total clients" << clients.size();
|
|
||||||
|
|
||||||
piDeleteAllAndClear(clients);
|
piDeleteAllAndClear(clients);
|
||||||
waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout);
|
waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout);
|
||||||
EXPECT_EQ(0, s->clientsCount());
|
EXPECT_EQ(0, s->clientsCount());
|
||||||
|
|||||||
@@ -1,262 +0,0 @@
|
|||||||
#include "piblockingqueue.h"
|
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
|
|
||||||
class MockConditionVar: public PIConditionVariable {
|
|
||||||
public:
|
|
||||||
bool isWaitCalled = false;
|
|
||||||
bool isWaitForCalled = false;
|
|
||||||
bool isTrueCondition = false;
|
|
||||||
int timeout = -1;
|
|
||||||
|
|
||||||
void wait(PIMutex & lk) override { isWaitCalled = true; }
|
|
||||||
|
|
||||||
void wait(PIMutex & lk, const std::function<bool()> & condition) override {
|
|
||||||
isWaitCalled = true;
|
|
||||||
isTrueCondition = condition();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waitFor(PIMutex & lk, int timeoutMs) override {
|
|
||||||
isWaitForCalled = true;
|
|
||||||
timeout = timeoutMs;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waitFor(PIMutex & lk, int timeoutMs, const std::function<bool()> & condition) override {
|
|
||||||
isWaitForCalled = true;
|
|
||||||
isTrueCondition = condition();
|
|
||||||
timeout = timeoutMs;
|
|
||||||
return isTrueCondition;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, put_is_block_when_capacity_reach) {
|
|
||||||
size_t capacity = 0;
|
|
||||||
auto conditionVarAdd = new MockConditionVar();
|
|
||||||
auto conditionVarRem = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
|
|
||||||
dequeue.put(11);
|
|
||||||
ASSERT_TRUE(conditionVarRem->isWaitCalled);
|
|
||||||
ASSERT_FALSE(conditionVarRem->isTrueCondition);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, offer_timedout_is_false_when_capacity_reach) {
|
|
||||||
size_t capacity = 0;
|
|
||||||
int timeout = 11;
|
|
||||||
auto conditionVarAdd = new MockConditionVar();
|
|
||||||
auto conditionVarRem = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
|
|
||||||
ASSERT_FALSE(dequeue.offer(11, timeout));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, offer_timedout_is_block_when_capacity_reach) {
|
|
||||||
size_t capacity = 0;
|
|
||||||
int timeout = 11;
|
|
||||||
auto conditionVarAdd = new MockConditionVar();
|
|
||||||
auto conditionVarRem = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
|
|
||||||
dequeue.offer(11, timeout);
|
|
||||||
EXPECT_TRUE(conditionVarRem->isWaitForCalled);
|
|
||||||
EXPECT_EQ(timeout, conditionVarRem->timeout);
|
|
||||||
ASSERT_FALSE(conditionVarRem->isTrueCondition);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, offer_is_true_before_capacity_reach) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
PIBlockingQueue<int> dequeue(capacity);
|
|
||||||
ASSERT_TRUE(dequeue.offer(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, offer_is_false_when_capacity_reach) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
PIBlockingQueue<int> dequeue(capacity);
|
|
||||||
dequeue.offer(11);
|
|
||||||
ASSERT_FALSE(dequeue.offer(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO change take_is_block_when_empty to prevent segfault
|
|
||||||
TEST(DISABLED_BlockingDequeueUnitTest, take_is_block_when_empty) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
auto conditionVar = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVar);
|
|
||||||
// May cause segfault because take front of empty queue
|
|
||||||
dequeue.take();
|
|
||||||
EXPECT_TRUE(conditionVar->isWaitCalled);
|
|
||||||
ASSERT_FALSE(conditionVar->isTrueCondition);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, take_is_not_block_when_not_empty) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
auto conditionVar = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVar);
|
|
||||||
dequeue.offer(111);
|
|
||||||
dequeue.take();
|
|
||||||
|
|
||||||
EXPECT_TRUE(conditionVar->isWaitCalled);
|
|
||||||
ASSERT_TRUE(conditionVar->isTrueCondition);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, take_is_value_eq_to_offer_value) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
auto conditionVar = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVar);
|
|
||||||
|
|
||||||
dequeue.offer(111);
|
|
||||||
ASSERT_EQ(dequeue.take(), 111);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, take_is_last) {
|
|
||||||
size_t capacity = 10;
|
|
||||||
auto conditionVar = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVar);
|
|
||||||
EXPECT_TRUE(dequeue.offer(111));
|
|
||||||
EXPECT_TRUE(dequeue.offer(222));
|
|
||||||
ASSERT_EQ(dequeue.take(), 111);
|
|
||||||
ASSERT_EQ(dequeue.take(), 222);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, poll_is_not_block_when_empty) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
bool isOk;
|
|
||||||
auto conditionVar = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVar);
|
|
||||||
dequeue.poll(0, 111, &isOk);
|
|
||||||
EXPECT_FALSE(conditionVar->isWaitForCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, poll_is_default_value_when_empty) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
bool isOk;
|
|
||||||
auto conditionVar = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVar);
|
|
||||||
ASSERT_EQ(dequeue.poll(0, 111, &isOk), 111);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, poll_is_offer_value_when_not_empty) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
bool isOk;
|
|
||||||
auto conditionVar = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVar);
|
|
||||||
dequeue.offer(111);
|
|
||||||
ASSERT_EQ(dequeue.poll(0, -1, &isOk), 111);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, poll_timeouted_is_block_when_empty) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
int timeout = 11;
|
|
||||||
auto conditionVar = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVar);
|
|
||||||
dequeue.poll(timeout, 111);
|
|
||||||
EXPECT_TRUE(conditionVar->isWaitForCalled);
|
|
||||||
EXPECT_EQ(timeout, conditionVar->timeout);
|
|
||||||
ASSERT_FALSE(conditionVar->isTrueCondition);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, poll_timeouted_is_default_value_when_empty) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
int timeout = 11;
|
|
||||||
auto conditionVar = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVar);
|
|
||||||
ASSERT_EQ(dequeue.poll(timeout, 111), 111);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, poll_timeouted_is_not_block_when_not_empty) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
int timeout = 11;
|
|
||||||
auto conditionVar = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVar);
|
|
||||||
dequeue.offer(111);
|
|
||||||
dequeue.poll(timeout, -1);
|
|
||||||
|
|
||||||
EXPECT_TRUE(conditionVar->isWaitForCalled);
|
|
||||||
ASSERT_TRUE(conditionVar->isTrueCondition);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, poll_timeouted_is_offer_value_when_not_empty) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
int timeout = 11;
|
|
||||||
auto conditionVar = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVar);
|
|
||||||
dequeue.offer(111);
|
|
||||||
ASSERT_EQ(dequeue.poll(timeout, -1), 111);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, poll_timeouted_is_last) {
|
|
||||||
size_t capacity = 10;
|
|
||||||
auto conditionVar = new MockConditionVar();
|
|
||||||
PIBlockingQueue<int> dequeue(capacity, conditionVar);
|
|
||||||
dequeue.offer(111);
|
|
||||||
dequeue.offer(222);
|
|
||||||
ASSERT_EQ(dequeue.poll(10, -1), 111);
|
|
||||||
ASSERT_EQ(dequeue.poll(10, -1), 222);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, capacity_is_eq_constructor_capacity) {
|
|
||||||
size_t capacity = 10;
|
|
||||||
PIBlockingQueue<int> dequeue(capacity);
|
|
||||||
ASSERT_EQ(dequeue.capacity(), capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, remainingCapacity_is_dif_of_capacity_and_size) {
|
|
||||||
size_t capacity = 2;
|
|
||||||
PIBlockingQueue<int> dequeue(capacity);
|
|
||||||
ASSERT_EQ(dequeue.remainingCapacity(), capacity);
|
|
||||||
dequeue.offer(111);
|
|
||||||
ASSERT_EQ(dequeue.remainingCapacity(), capacity - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, remainingCapacity_is_zero_when_capacity_reach) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
PIBlockingQueue<int> dequeue(capacity);
|
|
||||||
dequeue.offer(111);
|
|
||||||
dequeue.offer(111);
|
|
||||||
ASSERT_EQ(dequeue.remainingCapacity(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, size_is_eq_to_num_of_elements) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
PIBlockingQueue<int> dequeue(capacity);
|
|
||||||
ASSERT_EQ(dequeue.size(), 0);
|
|
||||||
dequeue.offer(111);
|
|
||||||
ASSERT_EQ(dequeue.size(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, size_is_eq_to_capacity_when_capacity_reach) {
|
|
||||||
size_t capacity = 1;
|
|
||||||
PIBlockingQueue<int> dequeue(capacity);
|
|
||||||
dequeue.offer(111);
|
|
||||||
dequeue.offer(111);
|
|
||||||
ASSERT_EQ(dequeue.size(), capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, drainTo_is_elements_moved) {
|
|
||||||
size_t capacity = 10;
|
|
||||||
PIDeque<int> refDeque;
|
|
||||||
for (size_t i = 0; i < capacity / 2; ++i)
|
|
||||||
refDeque.push_back(i * 10);
|
|
||||||
PIBlockingQueue<int> blockingDequeue(refDeque);
|
|
||||||
PIDeque<int> deque;
|
|
||||||
blockingDequeue.drainTo(deque);
|
|
||||||
ASSERT_EQ(blockingDequeue.size(), 0);
|
|
||||||
ASSERT_TRUE(deque == refDeque);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, drainTo_is_ret_eq_to_size_when_all_moved) {
|
|
||||||
size_t capacity = 10;
|
|
||||||
PIDeque<int> refDeque;
|
|
||||||
for (size_t i = 0; i < capacity / 2; ++i)
|
|
||||||
refDeque.push_back(i * 10);
|
|
||||||
PIBlockingQueue<int> blockingDequeue(refDeque);
|
|
||||||
PIDeque<int> deque;
|
|
||||||
ASSERT_EQ(blockingDequeue.drainTo(deque), refDeque.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BlockingDequeueUnitTest, drainTo_is_ret_eq_to_maxCount) {
|
|
||||||
size_t capacity = 10;
|
|
||||||
PIDeque<int> refDeque;
|
|
||||||
for (size_t i = 0; i < capacity / 2; ++i)
|
|
||||||
refDeque.push_back(i * 10);
|
|
||||||
PIBlockingQueue<int> blockingDequeue(refDeque);
|
|
||||||
PIDeque<int> deque;
|
|
||||||
ASSERT_EQ(blockingDequeue.drainTo(deque, refDeque.size() - 1), refDeque.size() - 1);
|
|
||||||
}
|
|
||||||
@@ -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());
|
|
||||||
}
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
#include "piconditionvar.h"
|
|
||||||
#include "pithread.h"
|
|
||||||
#include "testutil.h"
|
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
|
|
||||||
class ConditionVariable
|
|
||||||
: public ::testing::Test
|
|
||||||
, public TestUtil {
|
|
||||||
public:
|
|
||||||
~ConditionVariable() { delete variable; }
|
|
||||||
PIMutex m;
|
|
||||||
PIConditionVariable * variable;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void SetUp() override {
|
|
||||||
variable = new PIConditionVariable();
|
|
||||||
adapterFunctionDefault = [&]() {
|
|
||||||
m.lock();
|
|
||||||
variable->wait(m);
|
|
||||||
m.unlock();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, wait_is_block) {
|
|
||||||
createThread();
|
|
||||||
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, wait_is_block_when_notifyOne_before_wait) {
|
|
||||||
variable->notifyOne();
|
|
||||||
createThread();
|
|
||||||
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, wait_is_block_when_notifyAll_before_wait) {
|
|
||||||
variable->notifyAll();
|
|
||||||
createThread();
|
|
||||||
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, wait_is_unblock_when_notifyOne_after_wait) {
|
|
||||||
createThread();
|
|
||||||
variable->notifyOne();
|
|
||||||
ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, wait_is_unblock_when_notifyAll_after_wait) {
|
|
||||||
PIVector<PIThread *> threads;
|
|
||||||
|
|
||||||
for (int i = 0; i < THREAD_COUNT; ++i) {
|
|
||||||
threads.push_back(new PIThread([=]() { adapterFunctionDefault(); }));
|
|
||||||
}
|
|
||||||
|
|
||||||
piForeach(PIThread * thread, threads)
|
|
||||||
thread->startOnce();
|
|
||||||
piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT);
|
|
||||||
variable->notifyAll();
|
|
||||||
PITimeMeasurer measurer;
|
|
||||||
piForeach(PIThread * thread, threads) {
|
|
||||||
int timeout = WAIT_THREAD_TIME_MS * THREAD_COUNT - (int)measurer.elapsed_m();
|
|
||||||
thread->waitForFinish(timeout > 0 ? timeout : 0);
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < threads.size(); ++i)
|
|
||||||
EXPECT_FALSE(threads[i]->isRunning()) << "Thread " << i << " still running";
|
|
||||||
piForeach(PIThread * thread, threads)
|
|
||||||
delete thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, wait_is_one_unblock_when_notifyOne) {
|
|
||||||
PIVector<PIThread *> threads;
|
|
||||||
|
|
||||||
for (int i = 0; i < THREAD_COUNT; ++i) {
|
|
||||||
threads.push_back(new PIThread(adapterFunctionDefault));
|
|
||||||
}
|
|
||||||
|
|
||||||
piForeach(PIThread * thread, threads)
|
|
||||||
thread->startOnce();
|
|
||||||
piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT);
|
|
||||||
variable->notifyOne();
|
|
||||||
piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT);
|
|
||||||
int runningThreadCount = 0;
|
|
||||||
piForeach(PIThread * thread, threads)
|
|
||||||
if (thread->isRunning()) runningThreadCount++;
|
|
||||||
ASSERT_EQ(runningThreadCount, THREAD_COUNT - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, wait_is_protected_unblock_when_notifyOne) {
|
|
||||||
createThread([&]() {
|
|
||||||
m.lock();
|
|
||||||
variable->wait(m);
|
|
||||||
piMSleep(2 * WAIT_THREAD_TIME_MS);
|
|
||||||
// Missing unlock
|
|
||||||
});
|
|
||||||
variable->notifyOne();
|
|
||||||
piMSleep(WAIT_THREAD_TIME_MS);
|
|
||||||
ASSERT_FALSE(m.tryLock());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, wait_condition_is_block) {
|
|
||||||
createThread([&]() {
|
|
||||||
m.lock();
|
|
||||||
variable->wait(m, []() { return false; });
|
|
||||||
m.unlock();
|
|
||||||
});
|
|
||||||
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, wait_condition_is_check_condition_before_block) {
|
|
||||||
bool isConditionChecked = false;
|
|
||||||
createThread([&]() {
|
|
||||||
m.lock();
|
|
||||||
variable->wait(m, [&]() {
|
|
||||||
isConditionChecked = true;
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
m.unlock();
|
|
||||||
});
|
|
||||||
m.lock();
|
|
||||||
ASSERT_TRUE(isConditionChecked);
|
|
||||||
m.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, wait_condition_is_check_condition_when_notifyOne) {
|
|
||||||
bool isConditionChecked;
|
|
||||||
createThread([&]() {
|
|
||||||
m.lock();
|
|
||||||
variable->wait(m, [&]() {
|
|
||||||
isConditionChecked = true;
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
m.unlock();
|
|
||||||
});
|
|
||||||
m.lock();
|
|
||||||
isConditionChecked = false;
|
|
||||||
m.unlock();
|
|
||||||
variable->notifyOne();
|
|
||||||
piMSleep(threadStartTime + 1);
|
|
||||||
m.lock();
|
|
||||||
ASSERT_TRUE(isConditionChecked);
|
|
||||||
m.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, wait_condition_is_unblock_when_condition_and_notifyOne) {
|
|
||||||
bool condition = false;
|
|
||||||
createThread([&]() {
|
|
||||||
m.lock();
|
|
||||||
variable->wait(m, [&]() { return condition; });
|
|
||||||
m.unlock();
|
|
||||||
});
|
|
||||||
m.lock();
|
|
||||||
condition = true;
|
|
||||||
m.unlock();
|
|
||||||
variable->notifyOne();
|
|
||||||
ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, DISABLED_waitFor_is_block_before_timeout) {
|
|
||||||
createThread([&]() {
|
|
||||||
PITimeMeasurer measurer;
|
|
||||||
m.lock();
|
|
||||||
variable->waitFor(m, WAIT_THREAD_TIME_MS * 2);
|
|
||||||
m.unlock();
|
|
||||||
// Not reliable because spurious wakeup may happen
|
|
||||||
ASSERT_GE(measurer.elapsed_m(), WAIT_THREAD_TIME_MS);
|
|
||||||
});
|
|
||||||
EXPECT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS * 3));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, waitFor_is_unblock_when_timeout) {
|
|
||||||
std::atomic_bool isUnblock(false);
|
|
||||||
createThread([&]() {
|
|
||||||
m.lock();
|
|
||||||
variable->waitFor(m, WAIT_THREAD_TIME_MS);
|
|
||||||
isUnblock = true;
|
|
||||||
m.unlock();
|
|
||||||
});
|
|
||||||
// Test failed if suspend forever
|
|
||||||
EXPECT_TRUE(thread->waitForFinish(2 * WAIT_THREAD_TIME_MS));
|
|
||||||
ASSERT_TRUE(isUnblock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, waitFor_is_false_when_timeout) {
|
|
||||||
bool waitRet = true;
|
|
||||||
createThread([&]() {
|
|
||||||
m.lock();
|
|
||||||
waitRet = variable->waitFor(m, WAIT_THREAD_TIME_MS);
|
|
||||||
m.unlock();
|
|
||||||
});
|
|
||||||
EXPECT_TRUE(thread->waitForFinish(2 * WAIT_THREAD_TIME_MS));
|
|
||||||
ASSERT_FALSE(waitRet);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ConditionVariable, waitFor_is_unblock_when_condition_and_notifyOne) {
|
|
||||||
bool condition = false;
|
|
||||||
createThread([&]() {
|
|
||||||
m.lock();
|
|
||||||
variable->waitFor(m, 3 * WAIT_THREAD_TIME_MS, [&]() { return condition; });
|
|
||||||
m.unlock();
|
|
||||||
});
|
|
||||||
EXPECT_TRUE(thread->isRunning());
|
|
||||||
m.lock();
|
|
||||||
condition = true;
|
|
||||||
m.unlock();
|
|
||||||
variable->notifyOne();
|
|
||||||
piMSleep(WAIT_THREAD_TIME_MS);
|
|
||||||
ASSERT_FALSE(thread->isRunning());
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
#ifndef AWRCANFLASHER_TESTUTIL_H
|
|
||||||
#define AWRCANFLASHER_TESTUTIL_H
|
|
||||||
|
|
||||||
#include "pithread.h"
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Minimum wait thread start, switch context or another interthread communication action time. Increase it if tests
|
|
||||||
* write "Start thread timeout reach!" message. You can reduce it if you want increase test performance.
|
|
||||||
*/
|
|
||||||
const int WAIT_THREAD_TIME_MS = 400;
|
|
||||||
|
|
||||||
const int THREAD_COUNT = 5;
|
|
||||||
|
|
||||||
class TestUtil: public PIObject {
|
|
||||||
PIOBJECT(TestUtil)
|
|
||||||
|
|
||||||
public:
|
|
||||||
double threadStartTime;
|
|
||||||
PIThread * thread = new PIThread();
|
|
||||||
std::atomic_bool isRunning;
|
|
||||||
std::function<void()> adapterFunctionDefault;
|
|
||||||
|
|
||||||
TestUtil(): isRunning(false) {}
|
|
||||||
|
|
||||||
bool createThread(const std::function<void()> & fun = nullptr, PIThread * thread_ = nullptr) {
|
|
||||||
std::function<void()> actualFun = fun == nullptr ? adapterFunctionDefault : fun;
|
|
||||||
if (thread_ == nullptr) thread_ = thread;
|
|
||||||
thread_->startOnce([=](void *) {
|
|
||||||
isRunning = true;
|
|
||||||
actualFun();
|
|
||||||
});
|
|
||||||
return waitThread(thread_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waitThread(PIThread * thread_, bool runningStatus = true) {
|
|
||||||
PITimeMeasurer measurer;
|
|
||||||
bool isTimeout = !thread_->waitForStart(WAIT_THREAD_TIME_MS);
|
|
||||||
while (!isRunning) {
|
|
||||||
isTimeout = WAIT_THREAD_TIME_MS <= measurer.elapsed_m();
|
|
||||||
if (isTimeout) break;
|
|
||||||
piUSleep(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
threadStartTime = measurer.elapsed_m();
|
|
||||||
|
|
||||||
if (isTimeout) piCout << "Start thread timeout reach!";
|
|
||||||
|
|
||||||
if (threadStartTime > 1) {
|
|
||||||
piCout << "Start time" << threadStartTime << "ms";
|
|
||||||
} else if (threadStartTime > 0.001) {
|
|
||||||
piCout << "Start time" << threadStartTime * 1000 << "mcs";
|
|
||||||
} else {
|
|
||||||
piCout << "Start time" << threadStartTime * 1000 * 1000 << "ns";
|
|
||||||
}
|
|
||||||
|
|
||||||
return !isTimeout;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // AWRCANFLASHER_TESTUTIL_H
|
|
||||||
@@ -376,7 +376,7 @@ TEST(PIMathMatrixT_Test, invert) {
|
|||||||
matrix3 = matrix1;
|
matrix3 = matrix1;
|
||||||
matrix1.invert();
|
matrix1.invert();
|
||||||
EXPECT_EQ(matrix1, matrix3);
|
EXPECT_EQ(matrix1, matrix3);
|
||||||
EXPECT_DOUBLE_EQ(std::abs(d1 - (1. / d2)), 0.);
|
EXPECT_NEAR(std::abs(d1 - (1. / d2)), 0., 1e-12);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PIMathMatrixT_Test, inverted) {
|
TEST(PIMathMatrixT_Test, inverted) {
|
||||||
|
|||||||
118
tests/thread/piprotectedvariable_test.cpp
Normal file
118
tests/thread/piprotectedvariable_test.cpp
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
#include "piprotectedvariable.h"
|
||||||
|
#include "pistring.h"
|
||||||
|
#include "pithread.h"
|
||||||
|
#include "piliterals_time.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
// Basic functionality tests
|
||||||
|
TEST(PIProtectedVariable_Basic, BasicFunctionality) {
|
||||||
|
// Test basic set/get with different types
|
||||||
|
PIProtectedVariable<int> pvInt;
|
||||||
|
PIProtectedVariable<double> pvDouble;
|
||||||
|
PIProtectedVariable<PIString> pvString;
|
||||||
|
|
||||||
|
pvInt.set(123);
|
||||||
|
pvDouble.set(3.14159);
|
||||||
|
pvString.set(PIString("Hello, World!"));
|
||||||
|
|
||||||
|
EXPECT_EQ(pvInt.get(), 123);
|
||||||
|
EXPECT_DOUBLE_EQ(pvDouble.get(), 3.14159);
|
||||||
|
EXPECT_EQ(pvString.get(), PIString("Hello, World!"));
|
||||||
|
|
||||||
|
// Test operator=
|
||||||
|
pvInt = 999;
|
||||||
|
EXPECT_EQ(pvInt.get(), 999);
|
||||||
|
|
||||||
|
// Test getRef() with Pointer
|
||||||
|
struct TestStruct {
|
||||||
|
int x = 10;
|
||||||
|
int y = 20;
|
||||||
|
int getValue() const { return x + y; }
|
||||||
|
};
|
||||||
|
|
||||||
|
PIProtectedVariable<TestStruct> pvStruct;
|
||||||
|
|
||||||
|
auto ptr = pvStruct.getRef();
|
||||||
|
EXPECT_EQ(ptr->x, 10);
|
||||||
|
EXPECT_EQ(ptr->y, 20);
|
||||||
|
EXPECT_EQ(ptr->getValue(), 30);
|
||||||
|
|
||||||
|
// Modify through pointer
|
||||||
|
*ptr = TestStruct();
|
||||||
|
ptr->x = 100;
|
||||||
|
EXPECT_EQ(pvStruct.get().x, 100);
|
||||||
|
|
||||||
|
// Test for Pointer
|
||||||
|
pvInt.set(42);
|
||||||
|
auto ptr1 = pvInt.getRef();
|
||||||
|
EXPECT_EQ(*ptr1, 42);
|
||||||
|
*ptr1 = 55;
|
||||||
|
EXPECT_EQ(*ptr1, 55);
|
||||||
|
auto ptr2 = std::move(ptr1);
|
||||||
|
EXPECT_EQ(*ptr2, 55);
|
||||||
|
*ptr2 = 100;
|
||||||
|
EXPECT_EQ(*ptr2, 100);
|
||||||
|
auto ptr3 = pvInt.getRef();
|
||||||
|
EXPECT_EQ(*ptr3, 100);
|
||||||
|
*ptr3 = 333;
|
||||||
|
EXPECT_EQ(*ptr3, 333);
|
||||||
|
EXPECT_EQ(pvInt.get(), 333);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread safety tests
|
||||||
|
TEST(PIProtectedVariable_ThreadSafety, ConcurrentReadWrite) {
|
||||||
|
PIProtectedVariable<int> pv;
|
||||||
|
|
||||||
|
std::atomic<int> writeCount(0);
|
||||||
|
std::atomic<int> readCount(0);
|
||||||
|
std::atomic<int> invalidReads(0);
|
||||||
|
const int NUM_ITERATIONS = 1000;
|
||||||
|
const int NUM_WRITERS = 10;
|
||||||
|
const int NUM_READERS = 20;
|
||||||
|
const int TOTAL_WRITES = NUM_WRITERS * NUM_ITERATIONS;
|
||||||
|
|
||||||
|
// Collect thread handles for joining
|
||||||
|
PIVector<PIThread *> threads;
|
||||||
|
|
||||||
|
// Create writer threads
|
||||||
|
for (int i = 0; i < NUM_WRITERS; ++i) {
|
||||||
|
threads.push_back(new PIThread([&pv, &writeCount]() {
|
||||||
|
for (int j = 0; j < NUM_ITERATIONS; ++j) {
|
||||||
|
auto val = pv.getRef();
|
||||||
|
(*val)++;
|
||||||
|
auto val2 = pv.getRef();
|
||||||
|
writeCount++;
|
||||||
|
ASSERT_EQ(writeCount, *val2);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create reader threads
|
||||||
|
for (int i = 0; i < NUM_READERS; ++i) {
|
||||||
|
threads.push_back(new PIThread([&pv, &invalidReads, &readCount]() {
|
||||||
|
for (int j = 0; j < NUM_ITERATIONS; ++j) {
|
||||||
|
auto val = pv.get();
|
||||||
|
readCount++;
|
||||||
|
// Value should always be in valid range [0, TOTAL_WRITES]
|
||||||
|
if (val < 0 || val > TOTAL_WRITES) {
|
||||||
|
invalidReads++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
threads.forEach([](PIThread * & t) {t->startOnce();});
|
||||||
|
threads.forEach([](PIThread * & t) {t->waitForFinish(2_s);});
|
||||||
|
piDeleteAll(threads);
|
||||||
|
|
||||||
|
// Verify results
|
||||||
|
EXPECT_EQ(writeCount, TOTAL_WRITES);
|
||||||
|
EXPECT_EQ(readCount, NUM_READERS * NUM_ITERATIONS);
|
||||||
|
EXPECT_EQ(invalidReads, 0) << "All reads should return valid values in range [0, " << TOTAL_WRITES << "]";
|
||||||
|
|
||||||
|
// Final value should be TOTAL_WRITES
|
||||||
|
int finalVal = pv.get();
|
||||||
|
EXPECT_EQ(finalVal, TOTAL_WRITES);
|
||||||
|
}
|
||||||
@@ -467,13 +467,22 @@ bool procDpkg(const PIString & l) {
|
|||||||
if (!vs.isEmpty()) {
|
if (!vs.isEmpty()) {
|
||||||
PIStringList lines = vs.split('\n').reverse();
|
PIStringList lines = vs.split('\n').reverse();
|
||||||
for (auto l: lines) {
|
for (auto l: lines) {
|
||||||
auto sl = l;
|
|
||||||
l = l.left(l.find(":"));
|
l = l.left(l.find(":"));
|
||||||
if (!l.isEmpty() && !l.endsWith("-cross") && !l.contains(' ')) all_deps << l;
|
if (!l.isEmpty() && !l.contains(' ') && !l.endsWith("-cross") && !l.endsWith("-dev")) {
|
||||||
// PICout(true) << "** found \"" << l << "\" in \"" << sl << "\"";
|
// PICout(true) << "** found \"" << l << "\" in \"" << sl << "\"";
|
||||||
|
all_deps << l;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto l: lines) {
|
||||||
|
l = l.left(l.find(":"));
|
||||||
|
if (!l.isEmpty() && !l.contains(' ') && !l.endsWith("-cross")) {
|
||||||
|
// PICout(true) << "** found \"" << l << "\" in \"" << sl << "\"";
|
||||||
|
all_deps << l;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// piCout << "No dep on" << l;
|
// piCout << "No dep on" << l;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user