summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
Diffstat (limited to 'zen')
-rw-r--r--zen/dir_watcher.cpp4
-rw-r--r--zen/file_access.cpp107
-rw-r--r--zen/file_io.h4
-rw-r--r--zen/format_unit.cpp113
-rw-r--r--zen/globals.h37
-rw-r--r--zen/i18n.h6
-rw-r--r--zen/scope_guard.h64
-rw-r--r--zen/string_tools.h10
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
diff --git a/zen/i18n.h b/zen/i18n.h
index 9e4db0e8..32b6ed80 100644
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -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;
}
bgstack15