The loop may run in a thread that belongs to another device (the picloud
dispatcher forwards data from one connection's read thread to another
connection's socket), so isThreadedReadStopping() alone cannot stop it:
it sees the wrong device's flag. Stalling for at most writeTimeout()
without progress makes every write finite; the receiver resynchronizes
on the next packet signature.
Socket is non-blocking while BlockingWrite is set for every PIEthernet
(construct()), so a stalled peer made ::send return EAGAIN and the
retry loop spun forever. The thread could no longer be stopped by
stop()/stopAndWait() (interrupt only wakes event waits), which turned
into a permanent hang for PIStreamPacker-based users such as the
picloud dispatcher. Break out of the loop when the device is
stopping and return -1.
dirent** list was uninitialized, so scandir failure (returning -1)
left it with indeterminate value. The unconditional free(list) at
the end was then UB — typically heap corruption or crash.
When mmap() returned MAP_FAILED, data stayed as (void*)-1, causing
subsequent read/write/close to pass the null guard and crash.
Now resets data=nullptr, and calls shm_unlink when owner=true to
avoid orphaned shared memory objects.
In the blocking read path (timeout_ms <= 0), the while loop had no
exit condition for readDevice() returning 0 (EOF) or -1 (error).
This caused an infinite busy-loop at 100% CPU. Added 'else break'
to exit the loop on read failure, matching the timeout branch
which already handles this case.
readDevice() did pos += size instead of pos += ret. The clamping
on the next line (pos = min(pos, data_->size_s())) masked the
issue in practice, but the semantics were wrong. Aligns with
writeDevice() which correctly uses pos += rs.size_s().
open() returns -1 on error. The check fd != 0 treated -1 as a
valid fd (since -1 != 0 is true), causing devctl(-1, ...) and
close(-1) on QNX. Fixed to fd >= 0.
PIThread::stopAndWait now returns bool
PIKbdListener on Linux now use piwaitevent_p and can immediately stop
new base method piZeroMemory, also migrate all "memset 0" to piZeroMemory
apply some code analyzer recommendations
ICU flag now check if libicu exists
prepare for more accurate growth of containers (limited PoT, then constantly increase size)
move toStdFunction() to pibase.h
refactor PIParseHelper, now it much more abstract and useful
fix PIIODevice::createFromFullPath() when whitespaces at start or end are presence
PIStreamPacker add events for start and end packet receive
PIClientServer::ClientBase add virtual methods for start and end packet receive. also one can enable diagnostics with enableDiagnostics() method
PICout now call flush() on each end of output
add PIString::entries(const PIString & str)
* renamed private members for more clear code
* registerExternalBufferID() method to obtain unique ID for withExternalBuffer()
* PICoutManipulators::PICoutStdStream enum for select stream (stdout or stderr)
* Constructors now accept optional stream
* piCerr and piCerrObj macros
PIDir::temporary() moved to "mkdtemp"
PILog:
* now 4 levels
* you can set max level
* Error writes to piCerr
* PIThread::~PIThread() now unregister itself from introspection, if terminates than show warning
* PISystemMonitor now correctly stops
* PIPeer now can correctly stopAndWait
* PIPeer::destroy(), protected method for close all eths and threads
* new PIINTROSPECTION_STOP macro
* Introspection now can be correctly stopped by macro, more safety
ClientServer:
* ClientBase::close() stop and disconnect channel
* Server clients clean-up now event-based
* No warnings on client destructor