diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:22:36 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:22:36 +0200 |
commit | ecb1524f8da7901338b263384fed3c612f117b4c (patch) | |
tree | e7e06423fe27ea5ab45f27fc4b39ae597ba72490 /zen | |
parent | 5.10 (diff) | |
download | FreeFileSync-ecb1524f8da7901338b263384fed3c612f117b4c.tar.gz FreeFileSync-ecb1524f8da7901338b263384fed3c612f117b4c.tar.bz2 FreeFileSync-ecb1524f8da7901338b263384fed3c612f117b4c.zip |
5.11
Diffstat (limited to 'zen')
-rw-r--r-- | zen/FindFilePlus/dll_main.cpp | 38 | ||||
-rw-r--r-- | zen/IFileOperation/dll_main.cpp | 25 | ||||
-rw-r--r-- | zen/basic_math.h | 2 | ||||
-rw-r--r-- | zen/debug_memory_leaks.cpp | 7 | ||||
-rw-r--r-- | zen/debug_minidump.cpp | 2 | ||||
-rw-r--r-- | zen/debug_minidump.h | 7 | ||||
-rw-r--r-- | zen/dir_watcher.cpp | 6 | ||||
-rw-r--r-- | zen/file_handling.cpp | 27 | ||||
-rw-r--r-- | zen/file_io.cpp | 73 | ||||
-rw-r--r-- | zen/file_traverser.cpp | 28 | ||||
-rw-r--r-- | zen/privilege.cpp | 6 | ||||
-rw-r--r-- | zen/recycler.cpp | 8 | ||||
-rw-r--r-- | zen/scope_guard.h | 15 | ||||
-rw-r--r-- | zen/string_base.h | 20 | ||||
-rw-r--r-- | zen/string_tools.h | 6 | ||||
-rw-r--r-- | zen/thread.h | 16 | ||||
-rw-r--r-- | zen/tick_count.h | 28 | ||||
-rw-r--r-- | zen/zstring.cpp | 19 |
18 files changed, 209 insertions, 124 deletions
diff --git a/zen/FindFilePlus/dll_main.cpp b/zen/FindFilePlus/dll_main.cpp index ab3b25a3..caa5930d 100644 --- a/zen/FindFilePlus/dll_main.cpp +++ b/zen/FindFilePlus/dll_main.cpp @@ -4,12 +4,48 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** - #define WIN32_LEAN_AND_MEAN #include <zen/win.h> #include "init_dll_binding.h" +/* +http://www.microsoft.com/whdc/driver/kernel/DLL_bestprac.mspx +"DllMain is called while the loader-lock is held. [...] You cannot call any function in +DllMain that directly or indirectly tries to acquire the loader lock. Otherwise, you will +introduce the possibility that your application deadlocks or crashes." + +it's even worse: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx +"If your DLL is linked with the C run-time library (CRT), the entry point provided by the CRT calls the constructors +and destructors for global and static C++ objects. Therefore, these restrictions for DllMain also apply to constructors +and destructors and any code that is called from them." + +Example: http://blog.barthe.ph/2009/07/30/no-stdlib-in-dllmai/ + +Empirical study on DLL initialization order +------------------------------------------- +I. statically linked DLL: + DLL, static object constructors + DLL, DllMain(): DLL_PROCESS_ATTACH + main thread, static object constructors + main thread, enter main() + DLL, DllMain(): DLL_THREAD_ATTACH + DLL, DllMain(): DLL_THREAD_DETACH + main thread, exit main() + main thread, static object destructors + DLL, DllMain(): DLL_PROCESS_DETACH + DLL, static object destructors + +II. dynamically linked DLL (living in main()): + main thread, static object constructors + main thread, main(): LoadLibrary + DLL, static object constructors + DLL, DllMain(): DLL_PROCESS_ATTACH + main thread, main(): FreeLibrary + DLL, DllMain(): DLL_PROCESS_DETACH + DLL, static object destructors + main thread, static object destructors +*/ //optional: add init/teardown logic here BOOL APIENTRY DllMain(HINSTANCE hinstDLL, diff --git a/zen/IFileOperation/dll_main.cpp b/zen/IFileOperation/dll_main.cpp deleted file mode 100644 index 4665154a..00000000 --- a/zen/IFileOperation/dll_main.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - - -#define WIN32_LEAN_AND_MEAN -#include <windows.h> - -//optional: add init/teardown logic here -BOOL APIENTRY DllMain(HINSTANCE hinstDLL, - DWORD fdwReason, - LPVOID lpvReserved) -{ - switch (fdwReason) - { - case DLL_PROCESS_ATTACH: - case DLL_PROCESS_DETACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - break; - } - return TRUE; -} diff --git a/zen/basic_math.h b/zen/basic_math.h index bd416d19..f8a7affd 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -142,7 +142,7 @@ T confineCpy(const T& val, const T& minVal, const T& maxVal) return minVal; else if (val > maxVal) return maxVal; - return val; + return val; } template <class T> inline diff --git a/zen/debug_memory_leaks.cpp b/zen/debug_memory_leaks.cpp index 8774d16f..990f2ec7 100644 --- a/zen/debug_memory_leaks.cpp +++ b/zen/debug_memory_leaks.cpp @@ -5,7 +5,7 @@ // ************************************************************************** //-----------Memory Leak Detection-------------------------- -//Usage: just include this file into a Visual Studio project +//Usage: just include this file into a Visual Studio project #ifndef NDEBUG @@ -20,10 +20,9 @@ struct OnStartup { OnStartup() { - //note: wxWidgets also "activates" leak detection in the usual buggy way: it sets incomplete flags and incorrectly overwrites them rather than appending -> luckily it still seems to work! + //note: wxWidgets also activates leak detection in "src/common/init.cpp" using a macro that is equivalent to: int flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); - flags |= _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF; - _CrtSetDbgFlag(flags); + _CrtSetDbgFlag(flags | _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); } } dummy; diff --git a/zen/debug_minidump.cpp b/zen/debug_minidump.cpp index 9429819f..011e1bf2 100644 --- a/zen/debug_minidump.cpp +++ b/zen/debug_minidump.cpp @@ -39,7 +39,7 @@ LONG WINAPI writeDumpOnException(EXCEPTION_POINTERS* pExceptionInfo) ::CloseHandle(hFile); } - assert(false); + assert(false); return EXCEPTION_EXECUTE_HANDLER; } diff --git a/zen/debug_minidump.h b/zen/debug_minidump.h index 4ef0106e..2ef43039 100644 --- a/zen/debug_minidump.h +++ b/zen/debug_minidump.h @@ -14,13 +14,12 @@ /* Better std::bad_alloc --------------------- -overwrite "operator new" to automatically write mini dump and get info about bytes requested - -1. Compile "debug_new.cpp" +overwrite "operator new" to automatically write mini dump and get info about bytes requested: +1. Compile "debug_minidump.cpp" Minidumps http://msdn.microsoft.com/en-us/library/windows/desktop/ee416349(v=vs.85).aspx ---------------------------------------------------------------------------------------- -1. Compile "debug_new.cpp" +1. Compile "debug_minidump.cpp" 2. Compile "release" build with: - C/C++ -> General: Debug Information Format: "Program Database" (/Zi). - C/C++ -> Optimization: Omit Frame Pointers: No (/Oy-) - avoid call stack mess up! diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 2d249af8..7d5e4697 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -490,12 +490,12 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() if (evt.len != 0) //exclude case: deletion of "self", already reported by parent directory watch { - auto iter = pimpl_->watchDescrs.find(evt.wd); - if (iter != pimpl_->watchDescrs.end()) + auto it = pimpl_->watchDescrs.find(evt.wd); + if (it != pimpl_->watchDescrs.end()) { //Note: evt.len is NOT the size of the evt.name c-string, but the array size including all padding 0 characters! //It may be even 0 in which case evt.name must not be used! - const Zstring fullname = iter->second + evt.name; + const Zstring fullname = it->second + evt.name; if ((evt.mask & IN_CREATE) || (evt.mask & IN_MOVED_TO)) diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp index fce85bcd..c052435a 100644 --- a/zen/file_handling.cpp +++ b/zen/file_handling.cpp @@ -217,7 +217,7 @@ UInt64 zen::getFreeDiskSpace(const Zstring& path) //throw FileError namespace { #ifdef FFS_WIN -//(try to) enhance error messages by showing which processed lock the file +//(try to) enhance error messages by showing which processes lock the file Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string if none found or error occurred { if (vistaOrLater()) @@ -316,8 +316,6 @@ bool zen::removeFile(const Zstring& filename) //throw FileError if (errorCodeForNotExisting(lastError)) //no error situation if file is not existing! manual deletion relies on it! return false; - const std::wstring shortMsg = replaceCpy(_("Cannot delete file %x."), L"%x", fmtFileName(filename)); - #ifdef FFS_WIN if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only { @@ -327,7 +325,15 @@ bool zen::removeFile(const Zstring& filename) //throw FileError return true; lastError = ::GetLastError(); } +#endif + //after "lastError" evaluation it *may* be redundant to check existence again, but better be safe than sorry: + if (!somethingExists(filename)) //warning: changes global error code!! + return false; //neither file nor any other object (e.g. broken symlink) with that name existing + //begin of "regular" error reporting + const std::wstring shortMsg = replaceCpy(_("Cannot delete file %x."), L"%x", fmtFileName(filename)); + +#ifdef FFS_WIN if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message! lastError == ERROR_LOCK_VIOLATION) { @@ -336,10 +342,6 @@ bool zen::removeFile(const Zstring& filename) //throw FileError throw FileError(shortMsg + L"\n\n" + _("The file is locked by another process:") + L"\n" + procList); } #endif - //after "lastError" evaluation it *may* be redundant to check existence again, but better be safe than sorry: - if (!somethingExists(filename)) //warning: changes global error code!! - return false; //neither file nor any other object (e.g. broken symlink) with that name existing - throw FileError(shortMsg + L"\n\n" + getLastErrorFormatted(lastError)); } return true; @@ -636,15 +638,15 @@ void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) } //delete directories recursively - for (auto iter = dirList.begin(); iter != dirList.end(); ++iter) - removeDirectory(*iter, callback); //call recursively to correctly handle symbolic links + for (auto it = dirList.begin(); it != dirList.end(); ++it) + removeDirectory(*it, callback); //call recursively to correctly handle symbolic links //delete files - for (auto iter = fileList.begin(); iter != fileList.end(); ++iter) + for (auto it = fileList.begin(); it != fileList.end(); ++it) { - const bool workDone = removeFile(*iter); + const bool workDone = removeFile(*it); if (callback && workDone) - callback->notifyFileDeletion(*iter); //call once per file + callback->notifyFileDeletion(*it); //call once per file } //parent directory is deleted last @@ -1923,6 +1925,7 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize, CopyFileEx() processes multiple streams one after another, stream 1 is the file data stream and always available! Each stream is initialized with CALLBACK_STREAM_SWITCH and provides *new* hSourceFile, hDestinationFile. Calling GetFileInformationByHandle() on hDestinationFile for stream > 1 results in ERROR_ACCESS_DENIED! + totalBytesTransferred contains size of *all* streams and so can be larger than the "file size" file attribute */ CallbackData& cbd = *static_cast<CallbackData*>(lpData); diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 4880f6cc..788288ce 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -8,6 +8,10 @@ #ifdef FFS_WIN #include "long_path_prefix.h" +#include "IFileOperation/file_op.h" +#include "win_ver.h" +#include "dll.h" + #elif defined FFS_LINUX #include <fcntl.h> //open, close #include <unistd.h> //read, write @@ -16,9 +20,32 @@ using namespace zen; -FileInput::FileInput(FileHandle handle, const Zstring& filename) : FileInputBase(filename), fileHandle(handle) {} +namespace +{ +#ifdef FFS_WIN +//(try to) enhance error messages by showing which processes lock the file +Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string if none found or error occurred +{ + if (vistaOrLater()) + { + using namespace fileop; + const DllFun<FunType_getLockingProcesses> getLockingProcesses(getDllName(), funName_getLockingProcesses); + const DllFun<FunType_freeString> freeString (getDllName(), funName_freeString); -#ifdef FFS_LINUX + if (getLockingProcesses && freeString) + { + const wchar_t* procList = nullptr; + if (getLockingProcesses(filename.c_str(), procList)) + { + ZEN_ON_SCOPE_EXIT(freeString(procList)); + return procList; + } + } + } + return Zstring(); +} + +#elif defined FFS_LINUX //"filename" could be a named pipe which *blocks* forever during "open()"! https://sourceforge.net/p/freefilesync/bugs/221/ void checkForUnsupportedType(const Zstring& filename) //throw FileError { @@ -44,6 +71,10 @@ void checkForUnsupportedType(const Zstring& filename) //throw FileError } } #endif +} + + +FileInput::FileInput(FileHandle handle, const Zstring& filename) : FileInputBase(filename), fileHandle(handle) {} FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExisting @@ -89,11 +120,25 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExis #endif { const ErrorCode lastError = getLastError(); + const std::wstring shortMsg = errorCodeForNotExisting(lastError) ? + replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(filename)) : + replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(filename)); + std::wstring errorMsg = shortMsg + L"\n\n" + zen::getLastErrorFormatted(lastError); + +#ifdef FFS_WIN + if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message! + lastError == ERROR_LOCK_VIOLATION) + { + const Zstring procList = getLockingProcessNames(filename); //throw() + if (!procList.empty()) + errorMsg = shortMsg + L"\n\n" + _("The file is locked by another process:") + L"\n" + procList; + } +#endif if (errorCodeForNotExisting(lastError)) - throw ErrorNotExisting(replaceCpy(_("Cannot find file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted(lastError)); + throw ErrorNotExisting(errorMsg); - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted(lastError) + L" (open)"); + throw FileError(errorMsg + L" (open)"); } } @@ -186,19 +231,29 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil lastError = ::GetLastError(); } } - //"regular" error handling + + //begin of "regular" error reporting if (fileHandle == INVALID_HANDLE_VALUE) { - const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + zen::getLastErrorFormatted(lastError); + const std::wstring shortMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)); + std::wstring errorMsg = shortMsg + L"\n\n" + zen::getLastErrorFormatted(lastError); + + if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message! + lastError == ERROR_LOCK_VIOLATION) + { + const Zstring procList = getLockingProcessNames(filename); //throw() + if (!procList.empty()) + errorMsg = shortMsg + L"\n\n" + _("The file is locked by another process:") + L"\n" + procList; + } if (lastError == ERROR_FILE_EXISTS || //confirmed to be used lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 - throw ErrorTargetExisting(errorMessage); + throw ErrorTargetExisting(errorMsg); if (lastError == ERROR_PATH_NOT_FOUND) - throw ErrorTargetPathMissing(errorMessage); + throw ErrorTargetPathMissing(errorMsg); - throw FileError(errorMessage); + throw FileError(errorMsg); } } diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index 2cea74d8..9a02eb1e 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -195,12 +195,17 @@ struct Win32Traverser hnd.searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &hnd.data); //no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH if (hnd.searchHandle == INVALID_HANDLE_VALUE) + { + hnd.haveData = false; + if (::GetLastError() == ERROR_FILE_NOT_FOUND) + { + //1. directory may not exist *or* 2. it is completely empty: not all directories contain "., .." entries, e.g. a drive's root directory; NetDrive + // -> FindFirstFile() is a nice example of violation of API design principle of single responsibility + if (dirExists(directory)) //yes, a race-condition, still the best we can do + return; + } throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted()); - - //::GetLastError() == ERROR_FILE_NOT_FOUND -> *usually* NOT okay: - //directory may not exist *or* it is completely empty: not all directories contain "., .." entries, e.g. a drive's root directory - //usually a directory is never completely empty due to "sync.ffs_lock", so we assume it's not existing and let the error propagate - // -> FindFirstFile() is a nice example of violation of API design principle of single responsibility + } } static void destroy(const DirHandle& hnd) { ::FindClose(hnd.searchHandle); } //throw() @@ -208,6 +213,9 @@ struct Win32Traverser template <class FallbackFun> static bool getEntry(DirHandle& hnd, const Zstring& directory, FindData& fileInfo, FallbackFun) //throw FileError { + if (hnd.searchHandle == INVALID_HANDLE_VALUE) //handle special case of "truly empty directories" + return false; + if (hnd.haveData) { hnd.haveData = false; @@ -441,17 +449,17 @@ private: int failedAttempts = 0; int filesToValidate = 50; //don't let data verification become a performance issue - for (auto iter = markForDstHack.begin(); iter != markForDstHack.end(); ++iter) + for (auto it = markForDstHack.begin(); it != markForDstHack.end(); ++it) { if (failedAttempts >= 10) //some cloud storages don't support changing creation/modification times => don't waste (a lot of) time trying to return; - dstCallback.requestUiRefresh(iter->first); + dstCallback.requestUiRefresh(it->first); try { //set modification time including DST hack: this function is too clever to not introduce this dependency - setFileTime(iter->first, iter->second, SYMLINK_FOLLOW); //throw FileError + setFileTime(it->first, it->second, SYMLINK_FOLLOW); //throw FileError } catch (FileError&) { @@ -463,11 +471,11 @@ private: //even at this point it's not sure whether data was written correctly, again cloud storages tend to lie about success status if (filesToValidate-- > 0) { - const dst::RawTime encodedTime = dst::fatEncodeUtcTime(tofiletime(iter->second)); //throw std::runtime_error + const dst::RawTime encodedTime = dst::fatEncodeUtcTime(tofiletime(it->second)); //throw std::runtime_error //dst hack: verify data written; attention: this check may fail for "sync.ffs_lock" WIN32_FILE_ATTRIBUTE_DATA debugeAttr = {}; - ::GetFileAttributesEx(zen::applyLongPathPrefix(iter->first).c_str(), //__in LPCTSTR lpFileName, + ::GetFileAttributesEx(zen::applyLongPathPrefix(it->first).c_str(), //__in LPCTSTR lpFileName, GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, &debugeAttr); //__out LPVOID lpFileInformation diff --git a/zen/privilege.cpp b/zen/privilege.cpp index 9d8f12a0..288a1480 100644 --- a/zen/privilege.cpp +++ b/zen/privilege.cpp @@ -103,11 +103,11 @@ private: ~Privileges() //clean up: deactivate all privileges that have been activated by this application { - for (auto iter = activePrivileges.begin(); iter != activePrivileges.end(); ++iter) - if (iter->second) + for (auto it = activePrivileges.begin(); it != activePrivileges.end(); ++it) + if (it->second) try { - setPrivilege(iter->first.c_str(), false); //throw FileError + setPrivilege(it->first.c_str(), false); //throw FileError } catch (...) {} } diff --git a/zen/recycler.cpp b/zen/recycler.cpp index c35dca56..07803e50 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -94,8 +94,8 @@ void zen::recycleOrDelete(const std::vector<Zstring>& filenames, CallbackRecycli replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName()))); std::vector<const wchar_t*> cNames; - for (auto iter = filenames.begin(); iter != filenames.end(); ++iter) //caution to not create temporary strings here!! - cNames.push_back(iter->c_str()); + for (auto it = filenames.begin(); it != filenames.end(); ++it) //caution to not create temporary strings here!! + cNames.push_back(it->c_str()); CallbackData cbd(callback); if (!moveToRecycler(&cNames[0], cNames.size(), recyclerCallback, &cbd)) @@ -114,9 +114,9 @@ void zen::recycleOrDelete(const std::vector<Zstring>& filenames, CallbackRecycli else //regular recycle bin usage: available since XP { Zstring filenamesDoubleNull; - for (auto iter = filenames.begin(); iter != filenames.end(); ++iter) + for (auto it = filenames.begin(); it != filenames.end(); ++it) { - filenamesDoubleNull += *iter; + filenamesDoubleNull += *it; filenamesDoubleNull += L'\0'; } diff --git a/zen/scope_guard.h b/zen/scope_guard.h index 7d79e115..81f47f87 100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -28,19 +28,20 @@ namespace zen class ScopeGuardBase { public: - void dismiss() const { dismissed_ = true; } + void dismiss() { dismissed_ = true; } protected: ScopeGuardBase() : dismissed_(false) {} - ScopeGuardBase(const ScopeGuardBase& other) : dismissed_(other.dismissed_) { other.dismissed_ = true; } //take over responsibility + ScopeGuardBase(ScopeGuardBase&& other) : dismissed_(other.dismissed_) { other.dismiss(); } //take over responsibility ~ScopeGuardBase() {} bool isDismissed() const { return dismissed_; } private: + ScopeGuardBase(const ScopeGuardBase&); //delete ScopeGuardBase& operator=(const ScopeGuardBase&); // = delete; - mutable bool dismissed_; + bool dismissed_; }; @@ -48,7 +49,9 @@ template <typename F> class ScopeGuardImpl : public ScopeGuardBase { public: - ScopeGuardImpl(F fun) : fun_(fun) {} + explicit ScopeGuardImpl(const F& fun) : fun_(fun) {} + explicit ScopeGuardImpl(F&& fun) : fun_(std::move(fun)) {} + ScopeGuardImpl(ScopeGuardImpl&& other) : ScopeGuardBase(std::move(other)), fun_(std::move(other.fun_)) {} ~ScopeGuardImpl() { @@ -64,10 +67,10 @@ private: F fun_; }; -typedef const ScopeGuardBase& ScopeGuard; +typedef ScopeGuardBase&& ScopeGuard; template <class F> inline -ScopeGuardImpl<F> makeGuard(F fun) { return ScopeGuardImpl<F>(fun); } +ScopeGuardImpl<typename std::decay<F>::type> makeGuard(F&& fun) { return ScopeGuardImpl<typename std::decay<F>::type>(std::forward<F>(fun)); } } #define ZEN_CONCAT_SUB(X, Y) X ## Y diff --git a/zen/string_base.h b/zen/string_base.h index 05e5935e..bfe573e9 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -385,9 +385,9 @@ size_t Zbase<Char, SP, AP>::find(const Zbase& str, size_t pos) const { assert(pos <= length()); const Char* thisEnd = end(); //respect embedded 0 - const Char* iter = std::search(begin() + pos, thisEnd, + const Char* it = std::search(begin() + pos, thisEnd, str.begin(), str.end()); - return iter == thisEnd ? npos : iter - begin(); + return it == thisEnd ? npos : it - begin(); } @@ -396,9 +396,9 @@ size_t Zbase<Char, SP, AP>::find(const Char* str, size_t pos) const { assert(pos <= length()); const Char* thisEnd = end(); //respect embedded 0 - const Char* iter = std::search(begin() + pos, thisEnd, + const Char* it = std::search(begin() + pos, thisEnd, str, str + strLength(str)); - return iter == thisEnd ? npos : iter - begin(); + return it == thisEnd ? npos : it - begin(); } @@ -407,8 +407,8 @@ size_t Zbase<Char, SP, AP>::find(Char ch, size_t pos) const { assert(pos <= length()); const Char* thisEnd = end(); - const Char* iter = std::find(begin() + pos, thisEnd, ch); //respect embedded 0 - return iter == thisEnd ? npos : iter - begin(); + const Char* it = std::find(begin() + pos, thisEnd, ch); //respect embedded 0 + return it == thisEnd ? npos : it - begin(); } @@ -419,8 +419,8 @@ size_t Zbase<Char, SP, AP>::rfind(Char ch, size_t pos) const const Char* currEnd = pos == npos ? end() : begin() + std::min(pos + 1, length()); - const Char* iter = find_last(begin(), currEnd, ch); - return iter == currEnd ? npos : iter - begin(); + const Char* it = find_last(begin(), currEnd, ch); + return it == currEnd ? npos : it - begin(); } @@ -432,9 +432,9 @@ size_t Zbase<Char, SP, AP>::rfind(const Char* str, size_t pos) const const size_t strLen = strLength(str); const Char* currEnd = pos == npos ? end() : begin() + std::min(pos + strLen, length()); - const Char* iter = search_last(begin(), currEnd, + const Char* it = search_last(begin(), currEnd, str, str + strLen); - return iter == currEnd ? npos : iter - begin(); + return it == currEnd ? npos : it - begin(); } diff --git a/zen/string_tools.h b/zen/string_tools.h index c0bb1039..990c823a 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -468,8 +468,8 @@ S formatInteger(Num n, bool hasMinus) { assert(n >= 0); typedef typename GetCharType<S>::Type CharType; - CharType buffer[2 + 5 * sizeof(Num) / 2]; //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency - //minimum required chars (+ sign char): 1 + ceil(ln_10 (256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) < 2 + floor(sizeof(n) * 2.5) + CharType buffer[2 + sizeof(Num) * 5 / 2]; //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency + //required chars (+ sign char): 1 + ceil(ln_10 (256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) < 2 + floor(sizeof(n) * 2.5) auto iter = std::end(buffer); do @@ -556,7 +556,7 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i } else { - //rest of string should contain whitespace only + //rest of string should contain whitespace only, it's NOT a bug if there is some! //assert(std::all_of(iter, last, &isWhiteSpace<CharType>)); -> this is NO assert situation break; } diff --git a/zen/thread.h b/zen/thread.h index f00c0298..31d762c7 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -18,8 +18,8 @@ #pragma GCC diagnostic ignored "-Wshadow" #endif #ifdef _MSC_VER -#pragma warning(disable : 4702) //unreachable code -#pragma warning(disable : 4913) //user defined binary operator ',' exists but no overload could convert all operands, default built-in binary operator ',' used +#pragma warning(push) +#pragma warning(disable : 4702 4913) //unreachable code; user defined binary operator ',' exists but no overload could convert all operands, default built-in binary operator ',' used #endif #include <boost/thread.hpp> @@ -28,8 +28,7 @@ #pragma GCC diagnostic pop #endif #ifdef _MSC_VER -#pragma warning(default : 4702) -#pragma warning(default : 4913) +#pragma warning(pop) #endif namespace zen @@ -94,7 +93,7 @@ private: template <class T, class Function> inline auto async2(Function fun) -> boost::unique_future<T> //support for workaround of VS2010 bug: bool (*fun)(); decltype(fun()) == int! { - boost::packaged_task<T> pt(fun); + boost::packaged_task<T> pt(std::move(fun)); //packaged task seems to even require r-value reference: https://sourceforge.net/p/freefilesync/bugs/234/ auto fut = pt.get_future(); boost::thread t(std::move(pt)); t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!! @@ -110,12 +109,9 @@ template<class InputIterator, class Duration> inline bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& wait_duration) { const boost::system_time endTime = boost::get_system_time() + wait_duration; - while (first != last) - { + for (; first != last; ++first) if (!first->timed_wait_until(endTime)) return false; //time elapsed - ++first; - } return true; } @@ -139,7 +135,7 @@ public: if (!result_) result_ = std::move(result); } - conditionJobDone.notify_one(); + conditionJobDone.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref } diff --git a/zen/tick_count.h b/zen/tick_count.h index 98e59ae5..04ac0902 100644 --- a/zen/tick_count.h +++ b/zen/tick_count.h @@ -8,10 +8,17 @@ #define ZEN_TICK_COUNT_HEADER_3807326 #include <cstdint> -#include <algorithm> +//#include <algorithm> #include "type_traits.h" -#include "assert_static.h" -#include <cmath> +#include "basic_math.h" +//#include "assert_static.h" +//#include <cmath> +//template <class T> inline +//T dist(T a, T b) +//{ +// return a > b ? a - b : b - a; +//} + #ifdef FFS_WIN #include "win.h" //includes "windows.h" @@ -59,12 +66,15 @@ public: std::int64_t dist(const TickVal& lhs, const TickVal& rhs) { #ifdef FFS_WIN - assert_static(IsSignedInt<decltype(lhs.val_.QuadPart)>::value); - return std::abs(lhs.val_.QuadPart - rhs.val_.QuadPart); + return numeric::dist(lhs.val_.QuadPart, rhs.val_.QuadPart); //std::abs(a - b) can lead to overflow! + #elif defined FFS_LINUX - assert_static(IsSignedInt<decltype(lhs.val_.tv_sec)>::value); - assert_static(IsSignedInt<decltype(lhs.val_.tv_nsec)>::value); - return std::abs(static_cast<std::int64_t>(lhs.val_.tv_sec - rhs.val_.tv_sec) * 1000000000.0 + (lhs.val_.tv_nsec - rhs.val_.tv_nsec)); + const auto distSec = numeric::dist(lhs.val_.tv_sec, rhs.val_.tv_sec); + const auto distNsec = numeric::dist(lhs.val_.tv_nsec, rhs.val_.tv_nsec); + + if (distSec > (std::numeric_limits<std::int64_t>::max() - distNsec) / 1000000000) //truncate instead of overflow! + return std::numeric_limits<std::int64_t>::max(); + return distSec * 1000000000 + distNsec; #endif } @@ -94,7 +104,7 @@ std::int64_t ticksPerSec() //return 0 on error LARGE_INTEGER frequency = {}; if (!::QueryPerformanceFrequency(&frequency)) //MSDN promises: "The frequency cannot change while the system is running." return 0; - assert_static(sizeof(std::int64_t) >= sizeof(frequency.QuadPart)); + static_assert(sizeof(std::int64_t) >= sizeof(frequency.QuadPart), ""); return frequency.QuadPart; #elif defined FFS_LINUX diff --git a/zen/zstring.cpp b/zen/zstring.cpp index a1913755..7f4e79db 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -29,8 +29,8 @@ LeakChecker::~LeakChecker() std::string leakingStrings; int items = 0; - for (auto iter = activeStrings.begin(); iter != activeStrings.end() && items < 20; ++iter, ++items) - leakingStrings += "\"" + rawMemToString(iter->first, iter->second) + "\"\n"; + for (auto it = activeStrings.begin(); it != activeStrings.end() && items < 20; ++it, ++items) + leakingStrings += "\"" + rawMemToString(it->first, it->second) + "\"\n"; const std::string message = std::string("Memory leak detected!") + "\n\n" + "Candidates:\n" + leakingStrings; @@ -114,7 +114,7 @@ int z_impl::compareFilenamesWin(const wchar_t* lhs, const wchar_t* rhs, size_t s rhs, //__in LPCWSTR lpString2, static_cast<int>(sizeRhs), //__in int cchCount2, true); //__in BOOL bIgnoreCase - if (rv == 0) + if (rv <= 0) throw std::runtime_error("Error comparing strings (ordinal)!"); else return rv - 2; //convert to C-style string compare result @@ -126,7 +126,6 @@ int z_impl::compareFilenamesWin(const wchar_t* lhs, const wchar_t* rhs, size_t s const auto minSize = static_cast<unsigned int>(std::min(sizeLhs, sizeRhs)); - int rv = 0; if (minSize > 0) //LCMapString does not allow input sizes of 0! { if (minSize <= MAX_PATH) //performance optimization: stack @@ -146,7 +145,9 @@ int z_impl::compareFilenamesWin(const wchar_t* lhs, const wchar_t* rhs, size_t s if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, rhs, minSize, bufferB, MAX_PATH) == 0) throw std::runtime_error("Error comparing strings! (LCMapString)"); - rv = ::wmemcmp(bufferA, bufferB, minSize); + const int rv = ::wmemcmp(bufferA, bufferB, minSize); + if (rv != 0) + return rv; } else //use freestore { @@ -159,13 +160,13 @@ int z_impl::compareFilenamesWin(const wchar_t* lhs, const wchar_t* rhs, size_t s if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, rhs, minSize, &bufferB[0], minSize) == 0) throw std::runtime_error("Error comparing strings! (LCMapString: FS)"); - rv = ::wmemcmp(&bufferA[0], &bufferB[0], minSize); + const int rv = ::wmemcmp(&bufferA[0], &bufferB[0], minSize); + if (rv != 0) + return rv; } } - return rv == 0 ? - static_cast<int>(sizeLhs) - static_cast<int>(sizeRhs) : - rv; + return static_cast<int>(sizeLhs) - static_cast<int>(sizeRhs); } // const int rv = CompareString( |