diff options
Diffstat (limited to 'zen')
-rwxr-xr-x | zen/file_access.cpp | 13 | ||||
-rwxr-xr-x | zen/file_io.cpp | 57 | ||||
-rwxr-xr-x | zen/guid.h | 6 | ||||
-rwxr-xr-x | zen/scope_guard.h | 5 | ||||
-rwxr-xr-x | zen/serialize.h | 23 | ||||
-rwxr-xr-x | zen/thread.h | 52 |
6 files changed, 90 insertions, 66 deletions
diff --git a/zen/file_access.cpp b/zen/file_access.cpp index 7d57045d..69b6c388 100755 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -345,7 +345,7 @@ void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //thr const Zstring fileNameTrg = afterLast (pathTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); const Zstring parentPathSrc = beforeLast(pathSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); const Zstring parentPathTrg = beforeLast(pathTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); - //some (broken) devices may fail to rename case directly: + //some (broken) devices may fail to rename case directly: if (equalFilePath(parentPathSrc, parentPathTrg)) { if (fileNameSrc == fileNameTrg) @@ -385,12 +385,12 @@ void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTim if (procSl == ProcSymlink::FOLLOW) { //hell knows why files on gvfs-mounted Samba shares fail to open(O_WRONLY) returning EOPNOTSUPP: - //http://www.freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works + //http://www.freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works (but not for gvfs SFTP) if (::utimensat(AT_FDCWD, itemPath.c_str(), newTimes, 0) == 0) return; //in other cases utimensat() returns EINVAL for CIFS/NTFS drives, but open+futimens works: http://www.freefilesync.org/forum/viewtopic.php?t=387 - const int fdFile = ::open(itemPath.c_str(), O_WRONLY); + const int fdFile = ::open(itemPath.c_str(), O_WRONLY | O_APPEND); //2017-07-04: O_WRONLY | O_APPEND seems to avoid EOPNOTSUPP on gvfs SFTP! if (fdFile == -1) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"open"); ZEN_ON_SCOPE_EXIT(::close(fdFile)); @@ -515,7 +515,7 @@ void zen::createDirectory(const Zstring& dirPath) //throw FileError, ErrorTarget { const mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; //0777, default for newly created directories - if (::mkdir(dirPath.c_str(), mode) != 0) + if (::mkdir(dirPath.c_str(), mode) != 0) { const int lastError = errno; //copy before directly or indirectly making other system calls! const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(dirPath)); @@ -537,7 +537,7 @@ void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw File try { - createDirectory(dirPath); //throw FileError, ErrorTargetExisting + createDirectory(dirPath); //throw FileError, ErrorTargetExisting } catch (FileError&) { @@ -628,7 +628,10 @@ FileCopyResult copyFileOsSpecific(const Zstring& sourceFile, //throw FileError, FileOutput fileOut(fdTarget, targetFile, IOCallbackDivider(notifyUnbufferedIO, totalUnbufferedIO)); //pass ownership bufferedStreamCopy(fileIn, fileOut); //throw FileError, X + + //flush intermediate buffers before fiddling with the raw file handle fileOut.flushBuffers(); //throw FileError, X + struct ::stat targetInfo = {}; if (::fstat(fileOut.getHandle(), &targetInfo) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"fstat"); diff --git a/zen/file_io.cpp b/zen/file_io.cpp index d291e741..60023849 100755 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -130,22 +130,29 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError; m size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, X; return "bytesToRead" bytes unless end of stream! { const size_t blockSize = getBlockSize(); - - while (memBuf_.size() < bytesToRead) + assert(memBuf_.size() <= blockSize); + char* it = static_cast<char*>(buffer); + char* const itEnd = it + bytesToRead; + for (;;) { - memBuf_.resize(memBuf_.size() + blockSize); - const size_t bytesRead = tryRead(&*(memBuf_.end() - blockSize), blockSize); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0 - memBuf_.resize(memBuf_.size() - blockSize + bytesRead); //caveat: unsigned arithmetics + const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), memBuf_.size()); + std::copy (memBuf_.begin(), memBuf_.begin() + junkSize, it); + memBuf_.erase(memBuf_.begin(), memBuf_.begin() + junkSize); + it += junkSize; + + if (it == itEnd) + break; + //-------------------------------------------------------------------- + memBuf_.resize(blockSize); + const size_t bytesRead = tryRead(&memBuf_[0], blockSize); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0 + memBuf_.resize(bytesRead); if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesRead); //throw X if (bytesRead == 0) //end of file - bytesToRead = std::min(bytesToRead, memBuf_.size()); + break; } - - std::copy(memBuf_.begin(), memBuf_.begin() + bytesToRead, static_cast<char*>(buffer)); - memBuf_.erase(memBuf_.begin(), memBuf_.begin() + bytesToRead); - return bytesToRead; + return it - static_cast<char*>(buffer); } //---------------------------------------------------------------------------------------------------- @@ -224,16 +231,21 @@ size_t FileOutput::tryWrite(const void* buffer, size_t bytesToWrite) //throw Fil void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileError, X { - auto bufFirst = static_cast<const char*>(buffer); - memBuf_.insert(memBuf_.end(), bufFirst, bufFirst + bytesToWrite); - const size_t blockSize = getBlockSize(); - size_t bytesRemaining = memBuf_.size(); - ZEN_ON_SCOPE_EXIT(memBuf_.erase(memBuf_.begin(), memBuf_.end() - bytesRemaining)); - while (bytesRemaining >= blockSize) + assert(memBuf_.size() <= blockSize); + const char* it = static_cast<const char*>(buffer); + const char* const itEnd = it + bytesToWrite; + for (;;) { - const size_t bytesWritten = tryWrite(&*(memBuf_.end() - bytesRemaining), blockSize); //throw FileError; may return short! CONTRACT: bytesToWrite > 0 - bytesRemaining -= bytesWritten; + const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), blockSize - memBuf_.size()); + memBuf_.insert(memBuf_.end(), it, it + junkSize); + it += junkSize; + + if (it == itEnd) + return; + //-------------------------------------------------------------------- + const size_t bytesWritten = tryWrite(&memBuf_[0], blockSize); //throw FileError; may return short! CONTRACT: bytesToWrite > 0 + memBuf_.erase(memBuf_.begin(), memBuf_.begin() + bytesWritten); if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesWritten); //throw X! } } @@ -241,12 +253,11 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro void FileOutput::flushBuffers() //throw FileError, X { - size_t bytesRemaining = memBuf_.size(); - ZEN_ON_SCOPE_EXIT(memBuf_.erase(memBuf_.begin(), memBuf_.end() - bytesRemaining)); - while (bytesRemaining > 0) + assert(memBuf_.size() <= getBlockSize()); + while (!memBuf_.empty()) { - const size_t bytesWritten = tryWrite(&*(memBuf_.end() - bytesRemaining), bytesRemaining); //throw FileError; may return short! CONTRACT: bytesToWrite > 0 - bytesRemaining -= bytesWritten; + const size_t bytesWritten = tryWrite(&memBuf_[0], memBuf_.size()); //throw FileError; may return short! CONTRACT: bytesToWrite > 0 + memBuf_.erase(memBuf_.begin(), memBuf_.begin() + bytesWritten); if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesWritten); //throw X! } } @@ -20,11 +20,11 @@ namespace zen inline std::string generateGUID() //creates a 16-byte GUID { - //perf: generator: 0.38ms per creation; - // retrieve GUID: 0.13µs per call + //perf: generator: 0.38ms per creation; + // retrieve GUID: 0.13µs per call //generator is only thread-safe like an int => keep thread-local thread_local boost::uuids::random_generator gen; - const boost::uuids::uuid nativeRep = gen(); + const boost::uuids::uuid nativeRep = gen(); return std::string(nativeRep.begin(), nativeRep.end()); } } diff --git a/zen/scope_guard.h b/zen/scope_guard.h index b336ad53..f4ffc92b 100755 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -13,7 +13,7 @@ //std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP - static_assert(__GNUC__ < 6 || (__GNUC__ == 6 && (__GNUC_MINOR__ < 3 || (__GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); + static_assert(__GNUC__ < 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ < 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); namespace __cxxabiv1 { @@ -21,7 +21,8 @@ struct __cxa_eh_globals; extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept; } -inline int getUncaughtExceptionCount() +inline +int getUncaughtExceptionCount() { return *(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*))); } diff --git a/zen/serialize.h b/zen/serialize.h index 81d2d1ef..8f7e813c 100755 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -36,20 +36,20 @@ public: using iterator = std::vector<char>::iterator; using const_iterator = std::vector<char>::const_iterator; - iterator begin() { return buffer->begin(); } - iterator end () { return buffer->end (); } + iterator begin() { return buffer_->begin(); } + iterator end () { return buffer_->end (); } - const_iterator begin() const { return buffer->begin(); } - const_iterator end () const { return buffer->end (); } + const_iterator begin() const { return buffer_->begin(); } + const_iterator end () const { return buffer_->end (); } - void resize(size_t len) { buffer->resize(len); } - size_t size() const { return buffer->size(); } - bool empty() const { return buffer->empty(); } + void resize(size_t len) { buffer_->resize(len); } + size_t size() const { return buffer_->size(); } + bool empty() const { return buffer_->empty(); } - inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return *lhs.buffer == *rhs.buffer; } + inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return *lhs.buffer_ == *rhs.buffer_; } private: - std::shared_ptr<std::vector<char>> buffer { std::make_shared<std::vector<char>>() }; //always bound! + std::shared_ptr<std::vector<char>> buffer_ { std::make_shared<std::vector<char>>() }; //always bound! //perf: shared_ptr indirection irrelevant: less than 1% slower! }; @@ -148,9 +148,8 @@ struct MemoryStreamOut void write(const void* buffer, size_t bytesToWrite) { static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes - const size_t oldSize = buffer_.size(); - buffer_.resize(oldSize + bytesToWrite); - std::copy(static_cast<const char*>(buffer), static_cast<const char*>(buffer) + bytesToWrite, buffer_.begin() + oldSize); + buffer_.resize(buffer_.size() + bytesToWrite); + std::copy(static_cast<const char*>(buffer), static_cast<const char*>(buffer) + bytesToWrite, buffer_.end() - bytesToWrite); } const BinContainer& ref() const { return buffer_; } diff --git a/zen/thread.h b/zen/thread.h index 41261f1e..85e64493 100755 --- a/zen/thread.h +++ b/zen/thread.h @@ -12,6 +12,7 @@ #include "scope_guard.h" #include "type_traits.h" #include "optional.h" + #include <sys/prctl.h> namespace zen @@ -46,7 +47,7 @@ public: private: std::thread stdThread_; - std::shared_ptr<InterruptionStatus> intStatus_; + std::shared_ptr<InterruptionStatus> intStatus_{ std::make_shared<InterruptionStatus>() }; std::future<void> threadCompleted_; }; @@ -59,6 +60,7 @@ void interruptibleWait(std::condition_variable& cv, std::unique_lock<std::mutex> template <class Rep, class Period> void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime); //throw ThreadInterruption +void setCurrentThreadName(const char* threadName); uint64_t getThreadId(); //simple integer thread id, unlike boost::thread::id: https://svn.boost.org/trac/boost/ticket/5754 @@ -121,7 +123,7 @@ public: template <class Function> void access(Function fun) { - std::lock_guard<std::mutex> dummy(lockValue); + std::lock_guard<std::mutex> dummy(lockValue_); fun(value_); } @@ -129,7 +131,7 @@ private: Protected (const Protected&) = delete; Protected& operator=(const Protected&) = delete; - std::mutex lockValue; + std::mutex lockValue_; T value_{}; }; @@ -267,24 +269,24 @@ public: //context of InterruptibleThread instance: void interrupt() { - interrupted = true; + interrupted_ = true; { - std::lock_guard<std::mutex> dummy(lockSleep); //needed! makes sure the following signal is not lost! + std::lock_guard<std::mutex> dummy(lockSleep_); //needed! makes sure the following signal is not lost! //usually we'd make "interrupted" non-atomic, but this is already given due to interruptibleWait() handling } - conditionSleepInterruption.notify_all(); + conditionSleepInterruption_.notify_all(); - std::lock_guard<std::mutex> dummy(lockConditionPtr); - if (activeCondition) - activeCondition->notify_all(); //signal may get lost! + std::lock_guard<std::mutex> dummy(lockConditionPtr_); + if (activeCondition_) + activeCondition_->notify_all(); //signal may get lost! //alternative design locking the cv's mutex here could be dangerous: potential for dead lock! } //context of worker thread: void checkInterruption() //throw ThreadInterruption { - if (interrupted) + if (interrupted_) throw ThreadInterruption(); } @@ -296,7 +298,7 @@ public: ZEN_ON_SCOPE_EXIT(setConditionVar(nullptr)); //"interrupted" is not protected by cv's mutex => signal may get lost!!! => add artifical time out to mitigate! CPU: 0.25% vs 0% for longer time out! - while (!cv.wait_for(lock, std::chrono::milliseconds(1), [&] { return this->interrupted || pred(); })) + while (!cv.wait_for(lock, std::chrono::milliseconds(1), [&] { return this->interrupted_ || pred(); })) ; checkInterruption(); //throw ThreadInterruption @@ -306,26 +308,26 @@ public: template <class Rep, class Period> void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime) //throw ThreadInterruption { - std::unique_lock<std::mutex> lock(lockSleep); - if (conditionSleepInterruption.wait_for(lock, relTime, [this] { return static_cast<bool>(this->interrupted); })) + std::unique_lock<std::mutex> lock(lockSleep_); + if (conditionSleepInterruption_.wait_for(lock, relTime, [this] { return static_cast<bool>(this->interrupted_); })) throw ThreadInterruption(); } private: void setConditionVar(std::condition_variable* cv) { - std::lock_guard<std::mutex> dummy(lockConditionPtr); - activeCondition = cv; + std::lock_guard<std::mutex> dummy(lockConditionPtr_); + activeCondition_ = cv; } - std::atomic<bool> interrupted{ false }; //std:atomic is uninitialized by default!!! + std::atomic<bool> interrupted_{ false }; //std:atomic is uninitialized by default!!! //"The default constructor is trivial: no initialization takes place other than zero initialization of static and thread-local objects." - std::condition_variable* activeCondition = nullptr; - std::mutex lockConditionPtr; //serialize pointer access (only!) + std::condition_variable* activeCondition_ = nullptr; + std::mutex lockConditionPtr_; //serialize pointer access (only!) - std::condition_variable conditionSleepInterruption; - std::mutex lockSleep; + std::condition_variable conditionSleepInterruption_; + std::mutex lockSleep_; }; @@ -373,7 +375,7 @@ void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime) //thr template <class Function> inline -InterruptibleThread::InterruptibleThread(Function&& f) : intStatus_(std::make_shared<InterruptionStatus>()) +InterruptibleThread::InterruptibleThread(Function&& f) { std::promise<void> pFinished; threadCompleted_ = pFinished.get_future(); @@ -403,6 +405,14 @@ void InterruptibleThread::interrupt() { intStatus_->interrupt(); } inline +void setCurrentThreadName(const char* threadName) +{ + ::prctl(PR_SET_NAME, threadName, 0, 0, 0); + +} + + +inline uint64_t getThreadId() { //obviously "gettid()" is not available on Ubuntu/Debian/Suse => use the OpenSSL approach: |