diff options
author | Daniel Wilhelm <shieldwed@outlook.com> | 2016-10-29 11:35:33 +0200 |
---|---|---|
committer | Daniel Wilhelm <shieldwed@outlook.com> | 2016-10-29 11:35:33 +0200 |
commit | 8dd4a066ca0312ff03595b96a75abc8c6123f576 (patch) | |
tree | cf6aac6897f1ae4244b4b309627fc28902da2df9 /zen | |
parent | 8.4 (diff) | |
download | FreeFileSync-8dd4a066ca0312ff03595b96a75abc8c6123f576.tar.gz FreeFileSync-8dd4a066ca0312ff03595b96a75abc8c6123f576.tar.bz2 FreeFileSync-8dd4a066ca0312ff03595b96a75abc8c6123f576.zip |
8.5
Diffstat (limited to 'zen')
-rw-r--r-- | zen/dir_watcher.cpp | 4 | ||||
-rw-r--r-- | zen/file_access.cpp | 107 | ||||
-rw-r--r-- | zen/file_io.h | 4 | ||||
-rw-r--r-- | zen/format_unit.cpp | 113 | ||||
-rw-r--r-- | zen/globals.h | 37 | ||||
-rw-r--r-- | zen/i18n.h | 6 | ||||
-rw-r--r-- | zen/scope_guard.h | 64 | ||||
-rw-r--r-- | zen/string_tools.h | 10 |
8 files changed, 174 insertions, 171 deletions
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 0c55a963..769aa4f2 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -344,9 +344,9 @@ DirWatcher::~DirWatcher() if (pimpl_->worker.joinable()) //= thread::detach() precondition! -> may already be joined by HandleVolumeRemoval::onRequestRemoval() { pimpl_->worker.interrupt(); - pimpl_->worker.detach(); //we don't have time to wait... will take ~50ms anyway: + pimpl_->worker.detach(); //we don't have time to wait... would take ~50ms + //Windows caveat: exitting the app will kill the thread and leak memory! } - //caveat: exitting the app may simply kill this thread! } diff --git a/zen/file_access.cpp b/zen/file_access.cpp index d8a1f3b7..2e9d93f8 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -695,7 +695,6 @@ void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //thr namespace { - #ifdef ZEN_WIN void setFileTimeByHandle(HANDLE hFile, //throw FileError const FILETIME* creationTime, //optional @@ -721,16 +720,6 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError //function may fail if file is read-only: https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430 if (ec == ERROR_ACCESS_DENIED) { - auto setFileInfo = [&](FILE_BASIC_INFO basicInfo) //throw FileError; no const& since SetFileInformationByHandle() requires non-const parameter! - { - if (!::SetFileInformationByHandle(hFile, //__in HANDLE hFile, - FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass, - &basicInfo, //__in LPVOID lpFileInformation, - sizeof(basicInfo))) //__in DWORD dwBufferSize - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(filePath)), L"SetFileInformationByHandle"); - }; - //--------------------------------------------------------------------------- - BY_HANDLE_FILE_INFORMATION fileInfo = {}; if (::GetFileInformationByHandle(hFile, &fileInfo)) if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) @@ -741,16 +730,20 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError if (creationTime) basicInfo.CreationTime = toLargeInteger(*creationTime); - //set file time + attributes - setFileInfo(basicInfo); //throw FileError - - try //... to restore original file attributes - { - FILE_BASIC_INFO basicInfo2 = {}; - basicInfo2.FileAttributes = fileInfo.dwFileAttributes; - setFileInfo(basicInfo2); //throw FileError - } - catch (FileError&) {} + //set file time + attributes + if (!::SetFileInformationByHandle(hFile, //__in HANDLE hFile, + FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass, + &basicInfo, //__in LPVOID lpFileInformation, + sizeof(basicInfo))) //__in DWORD dwBufferSize + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(filePath)), L"SetFileInformationByHandle"); + + //(try to) restore original file attributes + FILE_BASIC_INFO basicInfo2 = {}; + basicInfo2.FileAttributes = fileInfo.dwFileAttributes; + ::SetFileInformationByHandle(hFile, //__in HANDLE hFile, + FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass, + &basicInfo2, //__in LPVOID lpFileInformation, + sizeof(basicInfo2)); //__in DWORD dwBufferSize return; } } @@ -836,26 +829,26 @@ void setWriteTimeNative(const Zstring& itemPath, } */ //temporarily reset read-only flag if required - DWORD attribs = INVALID_FILE_ATTRIBUTES; + DWORD attribsToRestore = INVALID_FILE_ATTRIBUTES; ZEN_ON_SCOPE_EXIT( - if (attribs != INVALID_FILE_ATTRIBUTES) - ::SetFileAttributes(applyLongPathPrefix(itemPath).c_str(), attribs); + if (attribsToRestore != INVALID_FILE_ATTRIBUTES) + ::SetFileAttributes(applyLongPathPrefix(itemPath).c_str(), attribsToRestore); ); auto removeReadonly = [&]() -> bool //throw FileError; may need to remove the readonly-attribute (e.g. on FAT usb drives) { - if (attribs == INVALID_FILE_ATTRIBUTES) + if (attribsToRestore == INVALID_FILE_ATTRIBUTES) { - const DWORD tmpAttr = ::GetFileAttributes(applyLongPathPrefix(itemPath).c_str()); - if (tmpAttr == INVALID_FILE_ATTRIBUTES) + const DWORD attribs = ::GetFileAttributes(applyLongPathPrefix(itemPath).c_str()); + if (attribs == INVALID_FILE_ATTRIBUTES) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"GetFileAttributes"); - if (tmpAttr & FILE_ATTRIBUTE_READONLY) + if (attribs & FILE_ATTRIBUTE_READONLY) { if (!::SetFileAttributes(applyLongPathPrefix(itemPath).c_str(), FILE_ATTRIBUTE_NORMAL)) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(itemPath)), L"SetFileAttributes"); - attribs = tmpAttr; //reapplied on scope exit + attribsToRestore = attribs; //reapplied on scope exit return true; } } @@ -866,7 +859,7 @@ void setWriteTimeNative(const Zstring& itemPath, { return ::CreateFile(applyLongPathPrefix(itemPath).c_str(), //_In_ LPCTSTR lpFileName, (conservativeApproach ? - //some NAS seem to have issues with FILE_WRITE_ATTRIBUTES, even worse, they may fail silently! + //some NAS seem to have issues with FILE_WRITE_ATTRIBUTES: they silently fail later during SetFileTime()! //http://sourceforge.net/tracker/?func=detail&atid=1093081&aid=3536680&group_id=234430 //Citrix shares seem to have this issue, too, but at least fail with "access denied" => try generic access first: GENERIC_READ | GENERIC_WRITE : @@ -881,7 +874,6 @@ void setWriteTimeNative(const Zstring& itemPath, FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes, nullptr); //_In_opt_ HANDLE hTemplateFile }; - { //extra scope for debug check below @@ -889,7 +881,7 @@ void setWriteTimeNative(const Zstring& itemPath, for (int i = 0; i < 2; ++i) //we will get this handle, no matter what! :) { //1. be conservative - hFile = openFile(true); + hFile = openFile(true /*GENERIC_WRITE*/); if (hFile == INVALID_HANDLE_VALUE) { if (::GetLastError() == ERROR_ACCESS_DENIED) //fails if file is read-only (or for "other" reasons) @@ -897,7 +889,7 @@ void setWriteTimeNative(const Zstring& itemPath, continue; //2. be a *little* fancy - hFile = openFile(false); + hFile = openFile(false /*FILE_WRITE_ATTRIBUTES*/); if (hFile == INVALID_HANDLE_VALUE) { const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! @@ -913,6 +905,25 @@ void setWriteTimeNative(const Zstring& itemPath, } ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); +#if 0 //waiting for user feedback... +#ifdef ZEN_WIN_VISTA_AND_LATER + //bugs, bugs, bugs.... on "SharePoint" SetFileAttributes() seems to affect file modification time: http://www.freefilesync.org/forum/viewtopic.php?t=3699 + //on Vista we can avoid reopening the file (and the SharePoint bug) +ZEN_ON_SCOPE_EXIT( + if (attribsToRestore != INVALID_FILE_ATTRIBUTES) + { + FILE_BASIC_INFO basicInfo = {}; + basicInfo.FileAttributes = attribsToRestore; + ::SetFileInformationByHandle(hFile, //__in HANDLE hFile, + FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass, + &basicInfo, //__in LPVOID lpFileInformation, + sizeof(basicInfo)); //__in DWORD dwBufferSize + attribsToRestore = INVALID_FILE_ATTRIBUTES; + } + ); +#endif +#endif + setFileTimeByHandle(hFile, creationTime, lastWriteTime, itemPath); //throw FileError } #ifndef NDEBUG //verify written data @@ -951,8 +962,8 @@ void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTim [2015-03-09] - cannot reproduce issues with NTFS and utimensat() on Ubuntu - utimensat() is supposed to obsolete utime/utimes and is also used by "cp" and "touch" - => let's give utimensat another chance: - using open()/futimens() for regular files and utimensat(AT_SYMLINK_NOFOLLOW) for symlinks is consistent with "cp" and "touch"! + => let's give utimensat another chance: + using open()/futimens() for regular files and utimensat(AT_SYMLINK_NOFOLLOW) for symlinks is consistent with "cp" and "touch"! */ struct ::timespec newTimes[2] = {}; newTimes[0].tv_sec = ::time(nullptr); //access time; using UTIME_OMIT for tv_nsec would trigger even more bugs: http://www.freefilesync.org/forum/viewtopic.php?t=1701 @@ -960,12 +971,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 + //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 if (::utimensat(AT_FDCWD, itemPath.c_str(), newTimes, 0) == 0) - return; + return; - //in other cases utimensat() returns EINVAL for CIFS/NTFS drives, but open+futimens works: http://www.freefilesync.org/forum/viewtopic.php?t=387 + //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); if (fdFile == -1) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"open"); @@ -1659,7 +1670,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool if (::lstat(sourceLink.c_str(), &sourceInfo) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"lstat"); -#ifdef ZEN_LINUX +#ifdef ZEN_LINUX setWriteTimeNative(targetLink, sourceInfo.st_mtim, ProcSymlink::DIRECT); //throw FileError #elif defined ZEN_MAC if (hasNativeSupportForExtendedAtrributes(targetLink)) //throw FileError @@ -1899,14 +1910,9 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw throw FileError(errorMsg, errorDescr); } -#ifndef ZEN_WIN_VISTA_AND_LATER - ZEN_ON_SCOPE_FAIL(try { removeFile(targetFile); } - catch (FileError&) {} ); //transactional behavior: guard just after opening target and before managing hFileTarget -#endif - +#ifdef ZEN_WIN_VISTA_AND_LATER ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileTarget)); -#ifdef ZEN_WIN_VISTA_AND_LATER //no need for ::DeleteFile(), we already have an open handle! Maybe this also prevents needless buffer-flushing in ::CloseHandle()??? Anyway, same behavior like ::CopyFileEx() ZEN_ON_SCOPE_FAIL ( @@ -1918,6 +1924,11 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw sizeof(di))) //_In_ DWORD dwBufferSize assert(false); ); +#else + ZEN_ON_SCOPE_FAIL(try { removeFile(targetFile); } + catch (FileError&) {} ); //transactional behavior: guard just after opening target and before managing hFileTarget + + ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileTarget)); #endif //---------------------------------------------------------------------- @@ -1985,8 +1996,8 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw LPVOID contextWrite = nullptr; // ZEN_ON_SCOPE_EXIT( - if (contextRead ) ::BackupRead (0, nullptr, 0, nullptr, true, false, &contextRead); //MSDN: "lpContext must be passed [...] all other parameters are ignored." - if (contextWrite) ::BackupWrite(0, nullptr, 0, nullptr, true, false, &contextWrite); ); + if (contextRead ) ::BackupRead (0, nullptr, 0, nullptr, true, false, &contextRead); //MSDN: "lpContext must be passed [...] all other parameters are ignored." + if (contextWrite) ::BackupWrite(0, nullptr, 0, nullptr, true, false, &contextWrite); ); // //stream-copy sourceFile to targetFile bool eof = false; diff --git a/zen/file_io.h b/zen/file_io.h index e6da486d..89cf77d5 100644 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -18,9 +18,9 @@ namespace zen { #ifdef ZEN_WIN - static const char LINE_BREAK[] = "\r\n"; + const char LINE_BREAK[] = "\r\n"; #elif defined ZEN_LINUX || defined ZEN_MAC - static const char LINE_BREAK[] = "\n"; //since OS X apple uses newline, too + const char LINE_BREAK[] = "\n"; //since OS X apple uses newline, too #endif //OS-buffered file IO optimized for sequential read/write accesses + better error reporting + long path support + following symlinks diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index c7b2504d..71bb8688 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -5,12 +5,13 @@ // ***************************************************************************** #include "format_unit.h" -#include "basic_math.h" -#include "i18n.h" -#include "time.h" #include <cwchar> //swprintf #include <ctime> #include <cstdio> +#include "basic_math.h" +#include "i18n.h" +#include "time.h" +#include "globals.h" #ifdef ZEN_WIN #include "int64.h" @@ -163,49 +164,18 @@ std::wstring zen::fractionToString(double fraction) #ifdef ZEN_WIN namespace { -bool getUserSetting(LCTYPE lt, UINT& setting) -{ - return ::GetLocaleInfo(LOCALE_USER_DEFAULT, //__in LCID Locale, - lt | LOCALE_RETURN_NUMBER, //__in LCTYPE LCType, - reinterpret_cast<LPTSTR>(&setting), //__out LPTSTR lpLCData, - sizeof(setting) / sizeof(TCHAR)) > 0; //__in int cchData -} - - -bool getUserSetting(LCTYPE lt, std::wstring& setting) -{ - int bufferSize = ::GetLocaleInfo(LOCALE_USER_DEFAULT, lt, nullptr, 0); - if (bufferSize > 0) - { - std::vector<wchar_t> buffer(bufferSize); - if (::GetLocaleInfo(LOCALE_USER_DEFAULT, //__in LCID Locale, - lt, //__in LCTYPE LCType, - &buffer[0], //__out LPTSTR lpLCData, - bufferSize) > 0) //__in int cchData - { - setting = &buffer[0]; //GetLocaleInfo() returns char count *including* 0-termination! - return true; - } - } - return false; -} - class IntegerFormat { public: - static const NUMBERFMT& get() { return getInst().fmt; } - static bool isValid() { return getInst().valid; } - -private: - static const IntegerFormat& getInst() + static std::shared_ptr<const IntegerFormat> instance() { -#if defined _MSC_VER && _MSC_VER < 1900 -#error function scope static initialization is not yet thread-safe! -#endif - static const IntegerFormat inst; - return inst; + static Global<const IntegerFormat> inst(std::make_unique<const IntegerFormat>()); + return inst.get(); } + bool isValid() const { return valid; } + const NUMBERFMT& get() const { return fmt; } + IntegerFormat() { //all we want is default NUMBERFMT, but set NumDigits to 0 @@ -219,7 +189,7 @@ private: getUserSetting(LOCALE_STHOUSAND, thousandSep) && getUserSetting(LOCALE_INEGNUMBER, fmt.NegativeOrder)) { - fmt.lpDecimalSep = &decimalSep[0]; //not used + fmt.lpDecimalSep = &decimalSep[0]; //don't need it fmt.lpThousandSep = &thousandSep[0]; //convert LOCALE_SGROUPING to Grouping: https://blogs.msdn.microsoft.com/oldnewthing/20060418-11/?p=31493/ @@ -233,6 +203,36 @@ private: } } +private: + IntegerFormat (const IntegerFormat&) = delete; + IntegerFormat& operator=(const IntegerFormat&) = delete; + + static bool getUserSetting(LCTYPE lt, UINT& setting) + { + return ::GetLocaleInfo(LOCALE_USER_DEFAULT, //__in LCID Locale, + lt | LOCALE_RETURN_NUMBER, //__in LCTYPE LCType, + reinterpret_cast<LPTSTR>(&setting), //__out LPTSTR lpLCData, + sizeof(setting) / sizeof(TCHAR)) > 0; //__in int cchData + } + + static bool getUserSetting(LCTYPE lt, std::wstring& setting) + { + const int bufferSize = ::GetLocaleInfo(LOCALE_USER_DEFAULT, lt, nullptr, 0); + if (bufferSize > 0) + { + std::vector<wchar_t> buffer(bufferSize); + if (::GetLocaleInfo(LOCALE_USER_DEFAULT, //__in LCID Locale, + lt, //__in LCTYPE LCType, + &buffer[0], //__out LPTSTR lpLCData, + bufferSize) > 0) //__in int cchData + { + setting = &buffer[0]; //GetLocaleInfo() returns char count *including* 0-termination! + return true; + } + } + return false; + } + NUMBERFMT fmt = {}; std::wstring thousandSep; std::wstring decimalSep; @@ -245,21 +245,23 @@ private: std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number) { #ifdef ZEN_WIN - if (IntegerFormat::isValid()) - { - int bufferSize = ::GetNumberFormat(LOCALE_USER_DEFAULT, 0, number.c_str(), &IntegerFormat::get(), nullptr, 0); - if (bufferSize > 0) + if (std::shared_ptr<const IntegerFormat> fmt = IntegerFormat::instance()) + if (fmt->isValid()) { - std::vector<wchar_t> buffer(bufferSize); - if (::GetNumberFormat(LOCALE_USER_DEFAULT, //__in LCID Locale, - 0, //__in DWORD dwFlags, - number.c_str(), //__in LPCTSTR lpValue, - &IntegerFormat::get(), //__in_opt const NUMBERFMT *lpFormat, - &buffer[0], //__out_opt LPTSTR lpNumberStr, - bufferSize) > 0) //__in int cchNumber - return &buffer[0]; //GetNumberFormat() returns char count *including* 0-termination! + const int bufferSize = ::GetNumberFormat(LOCALE_USER_DEFAULT, 0, number.c_str(), &fmt->get(), nullptr, 0); + if (bufferSize > 0) + { + std::vector<wchar_t> buffer(bufferSize); + if (::GetNumberFormat(LOCALE_USER_DEFAULT, //__in LCID Locale, + 0, //__in DWORD dwFlags, + number.c_str(), //__in LPCTSTR lpValue, + &fmt->get(), //__in_opt const NUMBERFMT *lpFormat, + &buffer[0], //__out_opt LPTSTR lpNumberStr, + bufferSize) > 0) //__in int cchNumber + return &buffer[0]; //GetNumberFormat() returns char count *including* 0-termination! + } } - } + assert(false); //what's the problem? return number; #elif defined ZEN_LINUX || defined ZEN_MAC @@ -297,9 +299,6 @@ std::wstring zen::utcToLocalTimeString(std::int64_t utcTime) SYSTEMTIME systemTimeLocal = {}; -#if defined _MSC_VER && _MSC_VER < 1900 -#error function scope static initialization is not yet thread-safe! -#endif static const bool useNewLocalTimeCalculation = zen::vistaOrLater(); //https://msdn.microsoft.com/en-us/library/ms724277 diff --git a/zen/globals.h b/zen/globals.h index 5f3dd64a..ff8c890d 100644 --- a/zen/globals.h +++ b/zen/globals.h @@ -13,21 +13,21 @@ namespace zen { -//solve static destruction order fiasco by providing scoped ownership and serialized access to global variables +//solve static destruction order fiasco by providing shared ownership and serialized access to global variables template <class T> class Global { public: - Global() {} + Global() { static_assert(std::is_trivially_destructible<Pod>::value, "this memory needs to live forever"); } explicit Global(std::unique_ptr<T>&& newInst) { set(std::move(newInst)); } ~Global() { set(nullptr); } std::shared_ptr<T> get() //=> return std::shared_ptr to let instance life time be handled by caller (MT usage!) { - while (spinLock.exchange(true)) ; - ZEN_ON_SCOPE_EXIT(spinLock = false); - if (inst) - return *inst; + while (pod.spinLock.exchange(true)) ; + ZEN_ON_SCOPE_EXIT(pod.spinLock = false); + if (pod.inst) + return *pod.inst; return nullptr; } @@ -37,21 +37,28 @@ public: if (newInst) tmpInst = new std::shared_ptr<T>(std::move(newInst)); { - while (spinLock.exchange(true)) ; - ZEN_ON_SCOPE_EXIT(spinLock = false); - std::swap(inst, tmpInst); + while (pod.spinLock.exchange(true)) ; + ZEN_ON_SCOPE_EXIT(pod.spinLock = false); + std::swap(pod.inst, tmpInst); } delete tmpInst; } private: - //avoid static destruction order fiasco: there may be accesses to "Global<T>::get()" during process shutdown - //e.g. show message in debug_minidump.cpp or some detached thread assembling an error message! - //=> use trivially-destructible POD only!!! - std::shared_ptr<T>* inst = nullptr; - //serialize access: can't use std::mutex because of non-trival destructor - std::atomic<bool> spinLock { false }; + //avoid static destruction order fiasco: there may be accesses to "Global<T>::get()" during process shutdown + //e.g. _("") used by message in debug_minidump.cpp or by some detached thread assembling an error message! + //=> use trivially-destructible POD only!!! + struct Pod + { + std::shared_ptr<T>* inst = nullptr; + //serialize access; can't use std::mutex: has non-trival destructor + std::atomic<bool> spinLock { false }; + } pod; }; + +#if defined _MSC_VER && _MSC_VER < 1900 +#error function scope static initialization is not yet thread-safe! +#endif } #endif //GLOBALS_H_8013740213748021573485 @@ -98,7 +98,7 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural, inline -Global<const TranslationHandler>& refTranslationGlobals() +Global<const TranslationHandler>& getGlobalTranslationHandler() { //getTranslator() may be called even after static objects of this translation unit are destroyed! static Global<const TranslationHandler> inst; //external linkage even in header! @@ -110,14 +110,14 @@ Global<const TranslationHandler>& refTranslationGlobals() inline void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler) { - implementation::refTranslationGlobals().set(std::move(newHandler)); + implementation::getGlobalTranslationHandler().set(std::move(newHandler)); } inline std::shared_ptr<const TranslationHandler> getTranslator() { - return implementation::refTranslationGlobals().get(); + return implementation::getGlobalTranslationHandler().get(); } } diff --git a/zen/scope_guard.h b/zen/scope_guard.h index 69d4b060..8ab58901 100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -9,9 +9,7 @@ #include <cassert> #include <exception> -#include <type_traits> //std::decay - -//best of Zen, Loki and C++17 +#include "type_tools.h" #ifdef ZEN_WIN @@ -20,9 +18,9 @@ inline int getUncaughtExceptionCount() { return std::uncaught_exceptions(); } #elif defined ZEN_LINUX || defined ZEN_MAC //std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP #ifdef ZEN_LINUX - static_assert(__GNUC__ < 6 || (__GNUC__ == 6 && (__GNUC_MINOR__ < 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); -#else - static_assert(__clang_major__ < 7 || (__clang_major__ == 7 && __clang_minor__ <= 3), "check std::uncaught_exceptions support"); + static_assert(__GNUC__ < 6 || (__GNUC__ == 6 && (__GNUC_MINOR__ < 2 || (__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); +#else //std::uncaught_exceptions() requires "mmacosx-version-min=10.12" + static_assert(__clang_major__ < 8 || (__clang_major__ == 8 && __clang_minor__ <= 0), "check std::uncaught_exceptions support"); #endif namespace __cxxabiv1 @@ -37,6 +35,7 @@ inline int getUncaughtExceptionCount() } #endif +//best of Zen, Loki and C++17 namespace zen { @@ -60,45 +59,32 @@ enum class ScopeGuardRunMode }; -template <ScopeGuardRunMode runMode, typename F> -struct ScopeGuardDestructor; - -//specialize scope guard destructor code and get rid of those pesky MSVC "4127 conditional expression is constant" -template <typename F> -struct ScopeGuardDestructor<ScopeGuardRunMode::ON_EXIT, F> +//partially specialize scope guard destructor code and get rid of those pesky MSVC "4127 conditional expression is constant" +template <typename F> inline +void runScopeGuardDestructor(F& fun, int /*exeptionCountOld*/, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_EXIT>) { - static void run(F& fun, int exeptionCountOld) - { - (void)exeptionCountOld; //silence unused parameter warning - try { fun(); } - catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"! - } -}; + try { fun(); } + catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"! +} -template <typename F> -struct ScopeGuardDestructor<ScopeGuardRunMode::ON_SUCCESS, F> +template <typename F> inline +void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_SUCCESS>) { - static void run(F& fun, int exeptionCountOld) - { - const bool failed = getUncaughtExceptionCount() > exeptionCountOld; - if (!failed) - fun(); //throw X - } -}; + const bool failed = getUncaughtExceptionCount() > exeptionCountOld; + if (!failed) + fun(); //throw X +} -template <typename F> -struct ScopeGuardDestructor<ScopeGuardRunMode::ON_FAIL, F> +template <typename F> inline +void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_FAIL>) { - static void run(F& fun, int exeptionCountOld) - { - const bool failed = getUncaughtExceptionCount() > exeptionCountOld; - if (failed) - try { fun(); } - catch (...) { assert(false); } - } -}; + const bool failed = getUncaughtExceptionCount() > exeptionCountOld; + if (failed) + try { fun(); } + catch (...) { assert(false); } +} template <ScopeGuardRunMode runMode, typename F> @@ -115,7 +101,7 @@ public: ~ScopeGuard() noexcept(runMode != ScopeGuardRunMode::ON_SUCCESS) { if (!dismissed) - ScopeGuardDestructor<runMode, F>::run(fun_, exeptionCount); + runScopeGuardDestructor(fun_, exeptionCount, StaticEnum<ScopeGuardRunMode, runMode>()); } void dismiss() { dismissed = true; } diff --git a/zen/string_tools.h b/zen/string_tools.h index 3bda665f..525227d6 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -229,12 +229,11 @@ template <class S, class T> inline std::vector<S> split(const S& str, const T& delimiter) { static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, ""); - std::vector<S> output; const size_t delimLen = strLength(delimiter); if (delimLen == 0) - output.push_back(str); + return { str }; else { const auto* const delimFirst = strBegin(delimiter); @@ -242,6 +241,8 @@ std::vector<S> split(const S& str, const T& delimiter) const auto* blockStart = strBegin(str); const auto* const strLast = blockStart + strLength(str); + + std::vector<S> output; for (;;) { @@ -249,12 +250,11 @@ std::vector<S> split(const S& str, const T& delimiter) delimFirst, delimLast); output.emplace_back(blockStart, blockEnd - blockStart); - if (blockEnd == strLast) - break; + if (blockEnd == strLast) //clients expect: if delimiter not found, return str + return output; blockStart = blockEnd + delimLen; } } - return output; } |