19 Commits

Author SHA1 Message Date
4537e40832 Merge branch 'master' into pitimer_slot 2026-03-20 15:35:34 +03:00
fe3b30bd49 version 5.6.1
patch deploy_tool: procDpkg now prioritize "non-cross" and "non-dev" packages, then only "non-cross"
add PIP_MANUAL_TEST CMake option
2026-03-20 13:00:24 +03:00
a1be5be5a1 increase timeout and remove sleep 2026-03-18 15:01:17 +03:00
99c99c39c2 separate cmake opts for tests 2026-03-18 13:39:01 +03:00
ba63e72bfa Merge pull request 'disable exeptions in cmake' (#199) from disable_exeptions into master
Reviewed-on: #199
2026-03-18 11:48:54 +03:00
79fa549201 fix matrix test 2026-03-18 11:46:08 +03:00
35140ee002 Merge pull request 'some fixes' (#198) from some_fixes into master
Reviewed-on: #198
2026-03-18 11:08:48 +03:00
f0c8bfef0a Merge pull request 'simplify piprotectedvariable' (#197) from protected_var_refact into master
Reviewed-on: #197
2026-03-18 10:58:58 +03:00
ba57aa0144 disable exceptions on win 2026-03-18 09:40:32 +03:00
ccbf86f781 disable exeptions in cmake 2026-03-18 09:30:28 +03:00
ac415ebbb6 revert can_unlock flag 2026-03-18 08:54:06 +03:00
c02b627d47 some fixes
PIEthernet::listen
PIClientServer::Server::~Server
PISystemTime const sleep
ClientServer tests fast and stable
2026-03-17 20:14:22 +03:00
9dc1af921c more simplify Pointer 2026-03-17 19:18:44 +03:00
e761625eab using recursive mutex 2026-03-17 19:14:14 +03:00
449978bda0 revert main and more tests 2026-03-17 19:07:01 +03:00
fe01c353e6 simplify tests 2026-03-17 18:56:20 +03:00
9f57f0107e remove picout 2026-03-17 18:34:48 +03:00
ac877f1024 fixes 2026-03-17 18:13:23 +03:00
8ccc05ee78 simplify piprotectedvariable 2026-03-17 17:33:27 +03:00
15 changed files with 227 additions and 88 deletions

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ endif()
project(PIP) project(PIP)
set(PIP_MAJOR 5) set(PIP_MAJOR 5)
set(PIP_MINOR 6) set(PIP_MINOR 6)
set(PIP_REVISION 0) set(PIP_REVISION 1)
set(PIP_SUFFIX ) set(PIP_SUFFIX )
set(PIP_COMPANY SHS) set(PIP_COMPANY SHS)
set(PIP_DOMAIN org.SHS) set(PIP_DOMAIN org.SHS)
@@ -67,11 +67,13 @@ set(PIP_DLL_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE STRING "")
option(ICU "ICU support for convert codepages" ${_ICU_DEFAULT}) option(ICU "ICU support for convert codepages" ${_ICU_DEFAULT})
option(STD_IOSTREAM "Building with std iostream operators support" OFF) option(STD_IOSTREAM "Building with std iostream operators support" OFF)
option(INTROSPECTION "Build with introspection" OFF) option(INTROSPECTION "Build with introspection" OFF)
option(TESTS "Build tests and perform their before install step" OFF) option(TESTS "Build tests" OFF)
option(TESTS_RUN "Run tests before install step" OFF)
option(COVERAGE "Build project with coverage info" OFF) option(COVERAGE "Build project with coverage info" OFF)
option(PIP_FFTW_F "Support fftw module for float" ON) option(PIP_FFTW_F "Support fftw module for float" ON)
option(PIP_FFTW_L "Support fftw module for long double" ON) option(PIP_FFTW_L "Support fftw module for long double" ON)
option(PIP_FFTW_Q "Support fftw module for quad double" OFF) option(PIP_FFTW_Q "Support fftw module for quad double" OFF)
option(PIP_MANUAL_TEST "Build dev test (main.cpp)" OFF)
set(PIP_UTILS 1) set(PIP_UTILS 1)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
@@ -360,10 +362,10 @@ 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 -fno-exceptions")
if(DEFINED ENV{QNX_HOST} OR PIP_FREERTOS) if(DEFINED ENV{QNX_HOST} OR PIP_FREERTOS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-32") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-32")
endif() endif()
@@ -592,6 +594,7 @@ if (NOT CROSSTOOLS)
#target_link_libraries(pip_plugin pip) #target_link_libraries(pip_plugin pip)
if (NOT DEFINED ANDROID_PLATFORM) if (NOT DEFINED ANDROID_PLATFORM)
if (PIP_MANUAL_TEST)
if(microhttpd_FOUND AND curl_FOUND) if(microhttpd_FOUND AND curl_FOUND)
add_executable(pip_test "main.cpp") add_executable(pip_test "main.cpp")
target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client) target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client)
@@ -602,6 +605,7 @@ if (NOT CROSSTOOLS)
endif() endif()
endif() endif()
endif() endif()
endif()
else() else()
@@ -823,6 +827,9 @@ if (PIP_TESTS_LIST)
foreach(_test ${PIP_TESTS_LIST}) foreach(_test ${PIP_TESTS_LIST})
message(" * ${_test}") message(" * ${_test}")
endforeach() endforeach()
if (TESTS_RUN)
message("TESTS_RUN ON -> Run tests before install step")
endif()
else() else()
message(" Tests: skip (tests off)") message(" Tests: skip (tests off)")
endif() endif()

View File

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

View File

@@ -726,6 +726,7 @@ 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
@@ -758,8 +759,6 @@ public:
} }
private: private:
NO_COPY_CLASS(PIScopeExitCall)
std::function<void()> func; std::function<void()> func;
}; };
@@ -768,14 +767,14 @@ private:
//! \~english Inherit from this class to make your class non-trivially copyable. //! \~english Inherit from this class to make your class non-trivially copyable.
//! \~russian Наследуйтесь от этого класса чтобы сделать свой класс нетривиально копируемым. //! \~russian Наследуйтесь от этого класса чтобы сделать свой класс нетривиально копируемым.
struct PIP_EXPORT PINonTriviallyCopyable { struct PIP_EXPORT PINonTriviallyCopyable {
PINonTriviallyCopyable() noexcept = default; PINonTriviallyCopyable() = default;
PINonTriviallyCopyable(const PINonTriviallyCopyable &) noexcept = default; PINonTriviallyCopyable(const PINonTriviallyCopyable &) = default;
PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept; PINonTriviallyCopyable(PINonTriviallyCopyable &&) ;
PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) noexcept = default; PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) = default;
PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) noexcept = default; PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) = default;
~PINonTriviallyCopyable() = default; ~PINonTriviallyCopyable() = default;
}; };
inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept = default; inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) = default;
//! \~\brief //! \~\brief

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,7 +25,9 @@ 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()
@@ -37,3 +39,4 @@ pip_test(piobject)
pip_test(client_server pip_client_server) pip_test(client_server pip_client_server)
pip_test(io) pip_test(io)
pip_test(system) pip_test(system)
pip_test(thread)

View File

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

View File

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

View File

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

View File

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