summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
Diffstat (limited to 'zen')
-rw-r--r--zen/FindFilePlus/dll_main.cpp38
-rw-r--r--zen/IFileOperation/dll_main.cpp25
-rw-r--r--zen/basic_math.h2
-rw-r--r--zen/debug_memory_leaks.cpp7
-rw-r--r--zen/debug_minidump.cpp2
-rw-r--r--zen/debug_minidump.h7
-rw-r--r--zen/dir_watcher.cpp6
-rw-r--r--zen/file_handling.cpp27
-rw-r--r--zen/file_io.cpp73
-rw-r--r--zen/file_traverser.cpp28
-rw-r--r--zen/privilege.cpp6
-rw-r--r--zen/recycler.cpp8
-rw-r--r--zen/scope_guard.h15
-rw-r--r--zen/string_base.h20
-rw-r--r--zen/string_tools.h6
-rw-r--r--zen/thread.h16
-rw-r--r--zen/tick_count.h28
-rw-r--r--zen/zstring.cpp19
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(
bgstack15