summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2015-10-02 14:56:27 +0200
committerDaniel Wilhelm <daniel@wili.li>2015-10-02 14:56:27 +0200
commit19eb73ff543c81c6886725a20dea0060cb0c0c26 (patch)
tree91442c0fdb9cac90c75dfb6bb84def9df297cd77 /zen
parent7.2 (diff)
downloadFreeFileSync-19eb73ff543c81c6886725a20dea0060cb0c0c26.tar.gz
FreeFileSync-19eb73ff543c81c6886725a20dea0060cb0c0c26.tar.bz2
FreeFileSync-19eb73ff543c81c6886725a20dea0060cb0c0c26.zip
7.3
Diffstat (limited to 'zen')
-rw-r--r--zen/async_task.h26
-rw-r--r--zen/dir_watcher.cpp41
-rw-r--r--zen/file_access.cpp31
-rw-r--r--zen/format_unit.cpp18
-rw-r--r--zen/guid.h2
-rw-r--r--zen/long_path_prefix.h1
-rw-r--r--zen/process_priority.cpp1
-rw-r--r--zen/recycler.cpp2
-rw-r--r--zen/scope_guard.h3
-rw-r--r--zen/stl_tools.h13
-rw-r--r--zen/string_base.h27
-rw-r--r--zen/string_tools.h6
-rw-r--r--zen/string_traits.h14
-rw-r--r--zen/symlink_target.h5
-rw-r--r--zen/thread.h285
-rw-r--r--zen/zstring.cpp26
16 files changed, 339 insertions, 162 deletions
diff --git a/zen/async_task.h b/zen/async_task.h
index 5c6f7f6e..d8f489a3 100644
--- a/zen/async_task.h
+++ b/zen/async_task.h
@@ -11,7 +11,6 @@
#include <functional>
#include "thread.h"
#include "scope_guard.h"
-//#include "type_tools.h"
namespace zen
{
@@ -19,25 +18,25 @@ namespace zen
class AsyncTasks
{
public:
- AsyncTasks() : inRecursion(false) {}
+ AsyncTasks() {}
template <class Fun, class Fun2>
- void add(Fun doAsync, Fun2 evalOnGui)
- //equivalent to "evalOnGui(doAsync())"
- // -> doAsync: the usual thread-safety requirements apply!
+ void add(Fun runAsync, Fun2 evalOnGui)
+ //equivalent to "evalOnGui(runAsync())"
+ // -> runAsync: the usual thread-safety requirements apply!
// -> evalOnGui: no thread-safety concerns, but must only reference variables with greater-equal lifetime than the AsyncTask instance!
{
tasks.push_back(zen::runAsync([=]() -> std::function<void()>
{
- auto result = doAsync();
+ auto result = runAsync();
return [=]{ evalOnGui(result); };
}));
}
template <class Fun, class Fun2>
- void add2(Fun doAsync, Fun2 evalOnGui) //for evalOnGui taking no parameters
+ void add2(Fun runAsync, Fun2 evalOnGui) //for evalOnGui taking no parameters
{
- tasks.push_back(zen::runAsync([doAsync, evalOnGui]() -> std::function<void()> { doAsync(); return [evalOnGui]{ evalOnGui(); }; }));
+ tasks.push_back(zen::runAsync([runAsync, evalOnGui]() -> std::function<void()> { runAsync(); return [evalOnGui]{ evalOnGui(); }; }));
}
void evalResults() //call from gui thread repreatedly
@@ -47,9 +46,9 @@ public:
inRecursion = true;
ZEN_ON_SCOPE_EXIT(inRecursion = false);
- tasks.remove_if([](boost::unique_future<std::function<void()>>& ft) -> bool
+ tasks.remove_if([](std::future<std::function<void()>>& ft) -> bool
{
- if (ft.is_ready())
+ if (isReady(ft))
{
(ft.get())();
return true;
@@ -62,8 +61,11 @@ public:
bool empty() const { return tasks.empty(); }
private:
- bool inRecursion;
- std::list<boost::unique_future<std::function<void()>>> tasks;
+ AsyncTasks (const AsyncTasks&) = delete;
+ AsyncTasks& operator=(const AsyncTasks&) = delete;
+
+ bool inRecursion = false;
+ std::list<std::future<std::function<void()>>> tasks;
};
}
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index e93e2b06..4abf3c0a 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -7,7 +7,7 @@
#include "dir_watcher.h"
#include <algorithm>
#include <set>
-#include "thread.h" //includes <boost/thread.hpp>
+#include "thread.h"
#include "scope_guard.h"
#ifdef ZEN_WIN
@@ -16,8 +16,11 @@
#include "long_path_prefix.h"
#elif defined ZEN_LINUX
+ #include <map>
#include <sys/inotify.h>
- #include <fcntl.h>
+ #include <fcntl.h> //fcntl
+ #include <unistd.h> //close
+ #include <limits.h> //NAME_MAX
#include "file_traverser.h"
#elif defined ZEN_MAC
@@ -37,7 +40,7 @@ public:
//context of worker thread
void addChanges(const char* buffer, DWORD bytesWritten, const Zstring& dirpath) //throw ()
{
- boost::lock_guard<boost::mutex> dummy(lockAccess);
+ std::lock_guard<std::mutex> dummy(lockAccess);
if (bytesWritten == 0) //according to docu this may happen in case of internal buffer overflow: report some "dummy" change
changedFiles.emplace_back(DirWatcher::ACTION_CREATE, L"Overflow.");
@@ -89,7 +92,7 @@ public:
////context of main thread
//void addChange(const Zstring& dirpath) //throw ()
//{
- // boost::lock_guard<boost::mutex> dummy(lockAccess);
+ // std::lock_guard<std::mutex> dummy(lockAccess);
// changedFiles.insert(dirpath);
//}
@@ -97,7 +100,7 @@ public:
//context of main thread
void fetchChanges(std::vector<DirWatcher::Entry>& output) //throw FileError
{
- boost::lock_guard<boost::mutex> dummy(lockAccess);
+ std::lock_guard<std::mutex> dummy(lockAccess);
//first check whether errors occurred in thread
if (errorInfo)
@@ -115,7 +118,7 @@ public:
//context of worker thread
void reportError(const std::wstring& msg, const std::wstring& description, DWORD errorCode) //throw()
{
- boost::lock_guard<boost::mutex> dummy(lockAccess);
+ std::lock_guard<std::mutex> dummy(lockAccess);
ErrorInfo newInfo = { copyStringTo<BasicWString>(msg), copyStringTo<BasicWString>(description), errorCode };
errorInfo = make_unique<ErrorInfo>(newInfo);
@@ -124,7 +127,7 @@ public:
private:
typedef Zbase<wchar_t> BasicWString; //thread safe string class for UI texts
- boost::mutex lockAccess;
+ std::mutex lockAccess;
std::vector<DirWatcher::Entry> changedFiles;
struct ErrorInfo
@@ -174,13 +177,13 @@ public:
::CloseHandle(hDir);
}
- void operator()() //thread entry
+ void operator()() const //thread entry
{
std::vector<char> buffer(64 * 1024); //needs to be aligned on a DWORD boundary; maximum buffer size restricted by some networks protocols (according to docu)
for (;;)
{
- boost::this_thread::interruption_point();
+ interruptionPoint(); //throw ThreadInterruption
//actual work
OVERLAPPED overlapped = {};
@@ -244,7 +247,7 @@ public:
::SleepEx(50, // __in DWORD dwMilliseconds,
true); // __in BOOL bAlertable
- boost::this_thread::interruption_point();
+ interruptionPoint(); //throw ThreadInterruption
}
guardAio.dismiss();
@@ -271,7 +274,7 @@ class HandleVolumeRemoval
public:
HandleVolumeRemoval(HANDLE hDir,
const Zstring& displayPath,
- boost::thread& worker) :
+ InterruptibleThread& worker) :
notificationHandle(registerFolderRemovalNotification(hDir, //throw FileError
displayPath,
[this] { this->onRequestRemoval (); }, //noexcept!
@@ -307,7 +310,7 @@ private:
void onRemovalFinished() { operationComplete = true; } //noexcept!
DeviceNotificationHandle* notificationHandle;
- boost::thread& worker_;
+ InterruptibleThread& worker_;
bool removalRequested;
bool operationComplete;
};
@@ -316,7 +319,7 @@ private:
struct DirWatcher::Pimpl
{
- boost::thread worker;
+ InterruptibleThread worker;
std::shared_ptr<SharedData> shared;
std::unique_ptr<HandleVolumeRemoval> volRemoval;
};
@@ -330,7 +333,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError
ReadChangesAsync reader(dirPath, pimpl_->shared); //throw FileError
pimpl_->volRemoval = zen::make_unique<HandleVolumeRemoval>(reader.getDirHandle(), dirPath, pimpl_->worker); //throw FileError
- pimpl_->worker = boost::thread(std::move(reader));
+ pimpl_->worker = InterruptibleThread(std::move(reader));
}
@@ -339,10 +342,8 @@ DirWatcher::~DirWatcher()
if (pimpl_->worker.joinable()) //= thread::detach() precondition! -> may already be joined by HandleVolumeRemoval::onRequestRemoval()
{
pimpl_->worker.interrupt();
- //if (pimpl_->worker.joinable()) pimpl_->worker.join(); -> we don't have time to wait... will take ~50ms anyway
- pimpl_->worker.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
+ pimpl_->worker.detach(); //we don't have time to wait... will take ~50ms anyway:
}
-
//caveat: exitting the app may simply kill this thread!
}
@@ -355,13 +356,13 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
//wait until device removal is confirmed, to prevent locking hDir again by some new watch!
if (pimpl_->volRemoval->requestReceived())
{
- const boost::chrono::steady_clock::time_point endTime = boost::chrono::steady_clock::now() + boost::chrono::seconds(15);
+ const std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now() + std::chrono::seconds(15);
//HandleVolumeRemoval::finished() not guaranteed! note: Windows gives unresponsive applications ca. 10 seconds until unmounting the usb stick in worst case
- while (!pimpl_->volRemoval->finished() && boost::chrono::steady_clock::now() < endTime)
+ while (!pimpl_->volRemoval->finished() && std::chrono::steady_clock::now() < endTime)
{
processGuiMessages(); //DBT_DEVICEREMOVECOMPLETE message is sent here!
- boost::this_thread::sleep_for(boost::chrono::milliseconds(50)); //throw boost::thread_interrupted
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
output.emplace_back(ACTION_DELETE, baseDirPath); //report removal as change to main directory
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index 09a1eb07..84d3b264 100644
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -434,26 +434,29 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro
if (lastError == ERROR_NOT_SAME_DEVICE)
throw ErrorDifferentVolume(errorMsg, errorDescr);
- else if (lastError == ERROR_ALREADY_EXISTS || //-> used on Win7 x64
+ if (lastError == ERROR_ALREADY_EXISTS || //-> used on Win7 x64
lastError == ERROR_FILE_EXISTS) //-> used by XP???
throw ErrorTargetExisting(errorMsg, errorDescr);
- else
- throw FileError(errorMsg, errorDescr);
+ throw FileError(errorMsg, errorDescr);
}
#elif defined ZEN_LINUX || defined ZEN_MAC
- if (::rename(pathSource.c_str(), pathTarget.c_str()) != 0)
+ //rename() will never fail with EEXIST, but always overwrite!
+ //=> OS X: no solution
+ //=> Linux: renameat2() with RENAME_NOREPLACE -> still new, probably buggy
+ const bool alreadyExists = somethingExists(pathTarget); //we have to let go of atomicity!
+
+ if (alreadyExists || ::rename(pathSource.c_str(), pathTarget.c_str()) != 0)
{
- const int lastError = errno; //copy before directly or indirectly making other system calls!
+ const int lastError = alreadyExists ? EEXIST : errno; //copy before directly or indirectly making other system calls!
const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtPath(pathSource)), L"%y", L"\n" + fmtPath(pathTarget));
const std::wstring errorDescr = formatSystemError(L"rename", lastError);
if (lastError == EXDEV)
throw ErrorDifferentVolume(errorMsg, errorDescr);
- else if (lastError == EEXIST)
- throw ErrorTargetExisting(errorMsg, errorDescr);
- else
- throw FileError(errorMsg, errorDescr);
+ if (lastError == EEXIST)
+ throw ErrorTargetExisting(errorMsg, errorDescr);
+ throw FileError(errorMsg, errorDescr);
}
#endif
}
@@ -2039,12 +2042,6 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
return PROGRESS_CONTINUE;
}
-#if defined _MSC_VER && _MSC_VER > 1800
- #error get rid!
-#endif
-const bool supportNonEncryptedDestination = winXpOrLater(); //encrypted destination is not supported with Windows 2000
-//caveat: function scope static initialization is not thread-safe in VS 2010! -> still not sufficient if multiple threads access during static init!!!
-
InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorFallbackToCopyAsBackupStream
const Zstring& targetFile,
@@ -2062,8 +2059,8 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE
DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS;
- if (supportNonEncryptedDestination)
- copyFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION; //allow copying from encrypted to non-encrypted location
+ //encrypted destination is not supported with Windows 2000! -> whatever
+ copyFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION; //allow copying from encrypted to non-encrypted location
//if (vistaOrLater()) //see http://blogs.technet.com/b/askperf/archive/2007/05/08/slow-large-file-copy-issues.aspx
// copyFlags |= COPY_FILE_NO_BUFFERING; //no perf difference at worst, improvement for large files (20% in test NTFS -> NTFS)
diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp
index 5f529f9c..9624458c 100644
--- a/zen/format_unit.cpp
+++ b/zen/format_unit.cpp
@@ -190,7 +190,10 @@ public:
private:
static const IntegerFormat& getInst()
{
- static IntegerFormat inst; //not threadsafe in MSVC until C++11, but not required right now
+#if defined _MSC_VER && _MSC_VER < 1900
+#error function scope static initialization is not yet thread-safe!
+#endif
+ static IntegerFormat inst;
return inst;
}
@@ -276,14 +279,6 @@ std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number)
}
-#ifdef ZEN_WIN
-namespace
-{
-const bool useNewLocalTimeCalculation = zen::vistaOrLater();
-}
-#endif
-
-
std::wstring zen::utcToLocalTimeString(std::int64_t utcTime)
{
auto errorMsg = [&] { return _("Error") + L" (time_t: " + numberTo<std::wstring>(utcTime) + L")"; };
@@ -293,6 +288,11 @@ 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();
+
//http://msdn.microsoft.com/en-us/library/ms724277(VS.85).aspx
if (useNewLocalTimeCalculation) //DST conversion like in Windows 7: NTFS stays fixed, but FAT jumps by one hour
{
diff --git a/zen/guid.h b/zen/guid.h
index 2ebfa132..d4b59eb4 100644
--- a/zen/guid.h
+++ b/zen/guid.h
@@ -10,7 +10,7 @@
#include <string>
#include <boost/uuid/uuid.hpp>
-#ifdef __GNUC__ //boost should start cleaning this mess up
+#ifdef __GNUC__ //boost should clean this mess up
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wuninitialized"
diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h
index f7ceb5e7..3db2722b 100644
--- a/zen/long_path_prefix.h
+++ b/zen/long_path_prefix.h
@@ -122,7 +122,6 @@ Zstring zen::ntPathToWin32Path(const Zstring& path) //noexcept
const DWORD charsWritten = ::GetEnvironmentVariable(L"SystemRoot", //_In_opt_ LPCTSTR lpName,
&buf[0], //_Out_opt_ LPTSTR lpBuffer,
bufSize); //_In_ DWORD nSize
-
if (0 < charsWritten && charsWritten < bufSize)
return replaceCpy(path, L"\\SystemRoot\\", appendSeparator(Zstring(&buf[0], charsWritten)), false);
}
diff --git a/zen/process_priority.cpp b/zen/process_priority.cpp
index c5932900..577e33a6 100644
--- a/zen/process_priority.cpp
+++ b/zen/process_priority.cpp
@@ -5,7 +5,6 @@
// **************************************************************************
#include "process_priority.h"
-//#include "sys_error.h"
#include "i18n.h"
#ifdef ZEN_WIN
diff --git a/zen/recycler.cpp b/zen/recycler.cpp
index 6cd34a17..75083d57 100644
--- a/zen/recycler.cpp
+++ b/zen/recycler.cpp
@@ -187,7 +187,7 @@ bool zen::recycleBinExists(const Zstring& dirpath, const std::function<void ()>&
&recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo
});
- while (ft.wait_for(boost::chrono::milliseconds(50)) != boost::future_status::ready)
+ while (ft.wait_for(std::chrono::milliseconds(50)) != std::future_status::ready)
if (onUpdateGui)
onUpdateGui(); //may throw!
diff --git a/zen/scope_guard.h b/zen/scope_guard.h
index 8477c7ee..5e917853 100644
--- a/zen/scope_guard.h
+++ b/zen/scope_guard.h
@@ -8,8 +8,7 @@
#define ZEN_SCOPEGUARD_8971632487321434
#include <cassert>
-//#include <type_traits> //std::decay
-//#include <utility>
+#include <type_traits> //std::decay
//best of Zen, Loki and C++11
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index bd76e264..685f5118 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -9,7 +9,7 @@
#include <memory>
#include <algorithm>
-#include <zen/type_tools.h>
+#include "type_tools.h"
//enhancements for <algorithm>
@@ -22,6 +22,9 @@ void vector_remove_if(V& vec, Predicate p);
template <class V, class W>
void vector_append(V& vec, const W& vec2);
+template <class V>
+void removeDuplicates(V& v);
+
template <class V, class W>
void set_append(V& s, const W& s2);
@@ -72,6 +75,14 @@ void vector_remove_if(V& vec, Predicate p)
}
+template <class V> inline
+void removeDuplicates(V& v)
+{
+ std::sort(v.begin(), v.end());
+ v.erase(std::unique(v.begin(), v.end()), v.end());
+}
+
+
template <class V, class W> inline
void vector_append(V& vec, const W& vec2)
{
diff --git a/zen/string_base.h b/zen/string_base.h
index f4ca5f2e..1bf8ed68 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -195,11 +195,10 @@ private:
struct Descriptor
{
Descriptor(size_t len, size_t cap) :
- refCount(1),
length (static_cast<std::uint32_t>(len)),
capacity(static_cast<std::uint32_t>(cap)) { static_assert(ATOMIC_INT_LOCK_FREE == 2, ""); } //2: "the types are always lock-free"
- std::atomic<unsigned int> refCount;
+ std::atomic<unsigned int> refCount { 1 }; //std:atomic is uninitialized by default!
std::uint32_t length;
std::uint32_t capacity; //allocated size without null-termination
};
@@ -222,11 +221,13 @@ public:
Zbase(const Char* source); //implicit conversion from a C-string
Zbase(const Char* source, size_t length);
Zbase(const Zbase& source);
- Zbase(Zbase&& tmp); //make noexcept in C++11
+ Zbase(Zbase&& tmp) noexcept;
explicit Zbase(Char source); //dangerous if implicit: Char buffer[]; return buffer[0]; ups... forgot &, but not a compiler error!
- //allow explicit construction from different string type, prevent ambiguity via SFINAE
- template <class S> explicit Zbase(const S& other, typename S::value_type = 0);
- ~Zbase(); //make noexcept in C++11
+
+//allow explicit construction from different string type, prevent ambiguity via SFINAE
+//template <class S> explicit Zbase(const S& other, typename S::value_type = 0);
+
+ ~Zbase();
//operator const Char* () const; //NO implicit conversion to a C-string!! Many problems... one of them: if we forget to provide operator overloads, it'll just work with a Char*...
@@ -263,11 +264,11 @@ public:
Zbase& assign(const Char* source, size_t len);
Zbase& append(const Char* source, size_t len);
void resize(size_t newSize, Char fillChar = 0);
- void swap(Zbase& other); //make noexcept in C++11
+ void swap(Zbase& other);
void push_back(Char val) { operator+=(val); } //STL access
Zbase& operator=(const Zbase& source);
- Zbase& operator=(Zbase&& tmp); //make noexcept in C++11
+ Zbase& operator=(Zbase&& tmp) noexcept;
Zbase& operator=(const Char* source);
Zbase& operator=(Char source);
Zbase& operator+=(const Zbase& other);
@@ -377,14 +378,14 @@ Zbase<Char, SP, AP>::Zbase(const Zbase<Char, SP, AP>& source)
template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>::Zbase(Zbase<Char, SP, AP>&& tmp)
+Zbase<Char, SP, AP>::Zbase(Zbase<Char, SP, AP>&& tmp) noexcept
{
rawStr = tmp.rawStr;
tmp.rawStr = nullptr; //usually nullptr would violate the class invarants, but it is good enough for the destructor!
//caveat: do not increment ref-count of an unshared string! We'd lose optimization opportunity of reusing its memory!
}
-
+/*
template <class Char, template <class, class> class SP, class AP>
template <class S> inline
Zbase<Char, SP, AP>::Zbase(const S& other, typename S::value_type)
@@ -394,11 +395,13 @@ Zbase<Char, SP, AP>::Zbase(const S& other, typename S::value_type)
std::copy(other.c_str(), other.c_str() + sourceLen, rawStr);
rawStr[sourceLen] = 0;
}
-
+*/
template <class Char, template <class, class> class SP, class AP> inline
Zbase<Char, SP, AP>::~Zbase()
{
+ static_assert(noexcept(this->~Zbase()), ""); //has exception spec of compiler-generated destructor by default
+
this->destroy(rawStr); //rawStr may be nullptr; see move constructor!
}
@@ -650,7 +653,7 @@ Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(const Zbase<Char, SP, AP>& o
template <class Char, template <class, class> class SP, class AP> inline
-Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(Zbase<Char, SP, AP>&& tmp)
+Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator=(Zbase<Char, SP, AP>&& tmp) noexcept
{
swap(tmp); //don't use unifying assignment but save one move-construction in the r-value case instead!
return *this;
diff --git a/zen/string_tools.h b/zen/string_tools.h
index c04adf96..9708464e 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -18,7 +18,7 @@
#include "stl_tools.h"
#include "string_traits.h"
-
+
//enhance arbitray string class with useful non-member functions:
namespace zen
{
@@ -48,11 +48,11 @@ template <class S, class T, class U> void replace ( S& str, const T& oldT
template <class S, class T, class U> S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true);
//high-performance conversion between numbers and strings
-template <class S, class T, class Num> S printNumber(const T& format, const Num& number); //format a single number using std::snprintf()
-
template <class S, class Num> S numberTo(const Num& number);
template <class Num, class S > Num stringTo(const S& str);
+template <class S, class T, class Num> S printNumber(const T& format, const Num& number); //format a single number using std::snprintf()
+
//string to string conversion: converts string-like type into char-compatible target string class
template <class T, class S> T copyStringTo(const S& str);
diff --git a/zen/string_traits.h b/zen/string_traits.h
index 5f91bdc4..add53d3a 100644
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -7,6 +7,7 @@
#ifndef STRING_TRAITS_HEADER_813274321443234
#define STRING_TRAITS_HEADER_813274321443234
+#include <cstring> //strlen
#include "type_tools.h"
//uniform access to string-like types, both classes and character arrays
@@ -143,19 +144,22 @@ struct GetCharType : ResultType<typename implementation::StringTraits<T>::CharTy
namespace implementation
{
+//strlen/wcslen are vectorized since VS14 CTP3
+inline size_t cStringLength(const char* str) { return std::strlen(str); }
+inline size_t cStringLength(const wchar_t* str) { return std::wcslen(str); }
+
+//no significant perf difference for "comparison" test case between cStringLength/wcslen:
+#if 0
template <class C> inline
-size_t cStringLength(const C* str) //naive implementation seems somewhat faster than "optimized" strlen/wcslen!
+size_t cStringLength(const C* str)
{
-#if defined _MSC_VER && _MSC_VER > 1800
- static_assert(false, "strlen/wcslen are vectorized in VS14 CTP3 -> test again!");
-#endif
-
static_assert(IsSameType<C, char>::value || IsSameType<C, wchar_t>::value, "");
size_t len = 0;
while (*str++ != 0)
++len;
return len;
}
+#endif
template <class S, typename = typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type> inline
const typename GetCharType<S>::Type* strBegin(const S& str) //SFINAE: T must be a "string"
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index c8c8c4be..aa320dfe 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -25,7 +25,7 @@
namespace zen
{
#ifdef ZEN_WIN
- bool isSymlink(const WIN32_FIND_DATA& data); //*not* a simple FILE_ATTRIBUTE_REPARSE_POINT check!
+ bool isSymlink(const WIN32_FIND_DATA& data); //checking FILE_ATTRIBUTE_REPARSE_POINT is insufficient!
bool isSymlink(DWORD fileAttributes, DWORD reparseTag);
#endif
@@ -162,8 +162,7 @@ Zstring getResolvedFilePath_impl(const Zstring& linkPath) //throw FileError
const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW");
if (!getFinalPathNameByHandle)
throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\""));
-
-
+
const HANDLE hFile = ::CreateFile(applyLongPathPrefix(linkPath).c_str(), //_In_ LPCTSTR lpFileName,
0, //_In_ DWORD dwDesiredAccess,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
diff --git a/zen/thread.h b/zen/thread.h
index 6d647de8..a3b8760b 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -4,39 +4,62 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef BOOST_THREAD_WRAP_H_78963234
-#define BOOST_THREAD_WRAP_H_78963234
-
-//temporary solution until C++11 thread becomes fully available (considering std::thread's non-interruptibility and std::async craziness, this may be NEVER)
-#include <memory>
-
-//workaround this pathetic boost thread warning mess
-#ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wswitch-enum"
- #pragma GCC diagnostic ignored "-Wstrict-aliasing"
- #pragma GCC diagnostic ignored "-Wredundant-decls"
- #pragma GCC diagnostic ignored "-Wshadow"
- #ifndef __clang__ //clang defines __GNUC__, but doesn't support this warning
- #pragma GCC diagnostic ignored "-Wunused-local-typedefs"
- #endif
-#endif
-#ifdef _MSC_VER
- #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
+#ifndef STD_THREAD_WRAP_H_7896323423432
+#define STD_THREAD_WRAP_H_7896323423432
-#include <boost/thread.hpp>
-
-#ifdef __GNUC__
- #pragma GCC diagnostic pop
-#endif
-#ifdef _MSC_VER
- #pragma warning(pop)
-#endif
+#include <thread>
+#include <future>
+#include <zen/scope_guard.h>
+#include <zen/type_traits.h>
namespace zen
{
+class InterruptionStatus;
+
+
+class InterruptibleThread
+{
+public:
+ InterruptibleThread() {}
+ InterruptibleThread (InterruptibleThread&& tmp) = default;
+ InterruptibleThread& operator=(InterruptibleThread&& tmp) = default;
+
+ template <class Function>
+ InterruptibleThread(Function f);
+
+ bool joinable () const { return stdThread.joinable(); }
+ void interrupt();
+ void join () { stdThread.join(); }
+ void detach () { stdThread.detach(); }
+
+ template <class Rep, class Period>
+ bool tryJoinFor(const std::chrono::duration<Rep, Period>& relTime)
+ {
+ if (threadCompleted.wait_for(relTime) == std::future_status::ready)
+ {
+ stdThread.join(); //runs thread-local destructors => this better be fast!!!
+ return true;
+ }
+ return false;
+ }
+
+private:
+ std::thread stdThread;
+ std::shared_ptr<InterruptionStatus> intStatus_;
+ std::future<void> threadCompleted;
+};
+
+//context of worker thread:
+void interruptionPoint(); //throw ThreadInterruption
+
+template<class Predicate>
+void interruptibleWait(std::condition_variable& cv, std::unique_lock<std::mutex>& lock, Predicate pred); //throw ThreadInterruption
+
+template <class Rep, class Period>
+void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime); //throw ThreadInterruption
+
+//------------------------------------------------------------------------------------------
+
/*
std::async replacement without crappy semantics:
1. guaranteed to run asynchronously
@@ -45,16 +68,20 @@ std::async replacement without crappy semantics:
Example:
Zstring dirpath = ...
auto ft = zen::runAsync([=](){ return zen::dirExists(dirpath); });
- if (ft.wait_for(boost::chrono::milliseconds(200)) == boost::future_status::ready && ft.get())
+ if (ft.wait_for(std::chrono::milliseconds(200)) == std::future_status::ready && ft.get())
//dir exising
*/
template <class Function>
-auto runAsync(Function fun) -> boost::unique_future<decltype(fun())>;
+auto runAsync(Function fun) -> std::future<decltype(fun())>;
//wait for all with a time limit: return true if *all* results are available!
template<class InputIterator, class Duration>
bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& wait_duration);
+template<typename T> inline
+bool isReady(const std::future<T>& f) { return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready; }
+//------------------------------------------------------------------------------------------
+
//wait until first job is successful or all failed: substitute until std::when_any is available
template <class T>
class GetFirstResult
@@ -77,6 +104,7 @@ private:
size_t jobsTotal_;
};
+//------------------------------------------------------------------------------------------
//value associated with mutex and guaranteed protected access:
template <class T>
@@ -89,7 +117,7 @@ public:
template <class Function>
void access(Function fun)
{
- boost::lock_guard<boost::mutex> dummy(lockValue);
+ std::lock_guard<std::mutex> dummy(lockValue);
fun(value_);
}
@@ -97,7 +125,7 @@ private:
Protected (const Protected&) = delete;
Protected& operator=(const Protected&) = delete;
- boost::mutex lockValue;
+ std::mutex lockValue;
T value_;
};
@@ -110,22 +138,15 @@ private:
//###################### implementation ######################
-#ifndef BOOST_HAS_THREADS
- #error just some paranoia check...
-#endif
template <class Function> inline
-auto runAsync(Function fun) -> boost::unique_future<decltype(fun())>
+auto runAsync(Function fun) -> std::future<decltype(fun())>
{
typedef decltype(fun()) ResultType;
-#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK //mirror "boost/thread/future.hpp", hopefully they know what they're doing
- boost::packaged_task<ResultType()> pt(std::move(fun));
-#else
- boost::packaged_task<ResultType> pt(std::move(fun));
-#endif
+ std::packaged_task<ResultType()> pt(std::move(fun));
auto fut = pt.get_future();
- boost::thread(std::move(pt)).detach(); //we have to explicitly detach since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
+ std::thread(std::move(pt)).detach(); //we have to explicitly detach since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
return fut;
}
@@ -133,9 +154,9 @@ auto runAsync(Function fun) -> boost::unique_future<decltype(fun())>
template<class InputIterator, class Duration> inline
bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& duration)
{
- const boost::chrono::steady_clock::time_point endTime = boost::chrono::steady_clock::now() + duration;
+ const std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now() + duration;
for (; first != last; ++first)
- if (first->wait_until(endTime) != boost::future_status::ready)
+ if (first->wait_until(endTime) != std::future_status::ready)
return false; //time elapsed
return true;
}
@@ -155,27 +176,26 @@ public:
void reportFinished(std::unique_ptr<T>&& result)
{
{
- boost::lock_guard<boost::mutex> dummy(lockResult);
+ std::lock_guard<std::mutex> dummy(lockResult);
++jobsFinished;
if (!result_)
result_ = std::move(result);
}
- 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
+ conditionJobDone.notify_all(); //better notify all, considering bugs like: https://svn.boost.org/trac/boost/ticket/7796
}
//context: main thread
template <class Duration>
bool waitForResult(size_t jobsTotal, const Duration& duration)
{
- boost::unique_lock<boost::mutex> dummy(lockResult);
- return conditionJobDone.wait_for(dummy, duration, [&] { return this->jobDone(jobsTotal); }); //throw boost::thread_interrupted
+ std::unique_lock<std::mutex> dummy(lockResult);
+ return conditionJobDone.wait_for(dummy, duration, [&] { return this->jobDone(jobsTotal); });
}
std::unique_ptr<T> getResult(size_t jobsTotal)
{
- boost::unique_lock<boost::mutex> dummy(lockResult);
- conditionJobDone.wait(dummy, [&] { return this->jobDone(jobsTotal); }); //throw boost::thread_interrupted
+ std::unique_lock<std::mutex> dummy(lockResult);
+ conditionJobDone.wait(dummy, [&] { return this->jobDone(jobsTotal); });
#ifndef NDEBUG
assert(!returnedResult);
@@ -191,10 +211,10 @@ private:
bool returnedResult;
#endif
- boost::mutex lockResult;
+ std::mutex lockResult;
size_t jobsFinished; //
std::unique_ptr<T> result_; //our condition is: "have result" or "jobsFinished == jobsTotal"
- boost::condition_variable conditionJobDone;
+ std::condition_variable conditionJobDone;
};
@@ -207,8 +227,7 @@ template <class T>
template <class Fun> inline
void GetFirstResult<T>::addJob(Fun f) //f must return a std::unique_ptr<T> containing a value on success
{
- auto asyncResult = this->asyncResult_; //capture member variable, not "this"!
- boost::thread t([asyncResult, f] { asyncResult->reportFinished(f()); });
+ std::thread t([asyncResult = this->asyncResult_, f = std::move(f)] { asyncResult->reportFinished(f()); });
++jobsTotal_;
t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
}
@@ -221,6 +240,158 @@ bool GetFirstResult<T>::timedWait(const Duration& duration) const { return async
template <class T> inline
std::unique_ptr<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); }
+
+//------------------------------------------------------------------------------------------
+
+//thread_local with non-POD seems to cause memory leaks on VS 14 => pointer only is fine:
+#ifdef _MSC_VER
+ #define ZEN_THREAD_LOCAL_SPECIFIER __declspec(thread)
+#elif defined __GNUC__ || defined __clang__
+ #define ZEN_THREAD_LOCAL_SPECIFIER __thread
+#else
+ #error "game over"
+#endif
+
+
+class ThreadInterruption {};
+
+
+class InterruptionStatus
+{
+public:
+ //context of InterruptibleThread instance:
+ void interrupt()
+ {
+ interrupted = true;
+
+ {
+ std::lock_guard<std::mutex> dummy(lockSleep); //needed! makes sure the following signal is not lost!
+ //usually we'd make "interrupted" non-atomic, but this is already given due to interruptibleWait() handling
+ }
+ conditionSleepInterruption.notify_all();
+
+ std::lock_guard<std::mutex> dummy(lockConditionPtr);
+ if (activeCondition)
+ activeCondition->notify_all(); //signal may get lost!
+ //alternative design locking the cv's mutex here could be dangerous: potential for dead lock!
+ }
+
+ //context of worker thread:
+ void checkInterruption() //throw ThreadInterruption
+ {
+ if (interrupted)
+ throw ThreadInterruption();
+ }
+
+ //context of worker thread:
+ template<class Predicate>
+ void interruptibleWait(std::condition_variable& cv, std::unique_lock<std::mutex>& lock, Predicate pred) //throw ThreadInterruption
+ {
+ setConditionVar(&cv);
+ ZEN_ON_SCOPE_EXIT(setConditionVar(nullptr));
+
+ //"interrupted" is not protected by cv's mutex => signal may get lost!!! => add artifical time out to mitigate! CPU: 0.25% vs 0% for longer time out!
+ while (!cv.wait_for(lock, std::chrono::milliseconds(1), [&] { return this->interrupted || pred(); }))
+ ;
+
+ checkInterruption(); //throw ThreadInterruption
+ }
+
+ //context of worker thread:
+ template <class Rep, class Period>
+ void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime) //throw ThreadInterruption
+ {
+ std::unique_lock<std::mutex> lock(lockSleep);
+ if (conditionSleepInterruption.wait_for(lock, relTime, [&] { return static_cast<bool>(this->interrupted); }))
+ throw ThreadInterruption();
+ }
+
+private:
+ void setConditionVar(std::condition_variable* cv)
+ {
+ std::lock_guard<std::mutex> dummy(lockConditionPtr);
+ activeCondition = cv;
+ }
+
+ std::atomic<bool> interrupted{ false }; //std:atomic is uninitialized by default!
+
+ std::condition_variable* activeCondition = nullptr;
+ std::mutex lockConditionPtr; //serialize pointer access (only!)
+
+ std::condition_variable conditionSleepInterruption;
+ std::mutex lockSleep;
+};
+
+
+namespace impl
+{
+inline
+InterruptionStatus*& refThreadLocalInterruptionStatus()
+{
+ static ZEN_THREAD_LOCAL_SPECIFIER InterruptionStatus* threadLocalInterruptionStatus = nullptr;
+ return threadLocalInterruptionStatus;
+}
+}
+
+//context of worker thread:
+inline
+void interruptionPoint() //throw ThreadInterruption
+{
+ assert(impl::refThreadLocalInterruptionStatus());
+ if (impl::refThreadLocalInterruptionStatus())
+ impl::refThreadLocalInterruptionStatus()->checkInterruption(); //throw ThreadInterruption
+}
+
+
+//context of worker thread:
+template<class Predicate> inline
+void interruptibleWait(std::condition_variable& cv, std::unique_lock<std::mutex>& lock, Predicate pred) //throw ThreadInterruption
+{
+ assert(impl::refThreadLocalInterruptionStatus());
+ if (impl::refThreadLocalInterruptionStatus())
+ impl::refThreadLocalInterruptionStatus()->interruptibleWait(cv, lock, pred);
+ else
+ cv.wait(lock, pred);
+}
+
+//context of worker thread:
+template <class Rep, class Period> inline
+void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime) //throw ThreadInterruption
+{
+ assert(impl::refThreadLocalInterruptionStatus());
+ if (impl::refThreadLocalInterruptionStatus())
+ impl::refThreadLocalInterruptionStatus()->interruptibleSleep(relTime);
+ else
+ std::this_thread::sleep_for(relTime);
+}
+
+
+template <class Function> inline
+InterruptibleThread::InterruptibleThread(Function f) : intStatus_(std::make_shared<InterruptionStatus>())
+{
+ std::promise<void> pFinished;
+ threadCompleted = pFinished.get_future();
+
+ stdThread = std::thread([f = std::move(f),
+ intStatus = this->intStatus_,
+ pFinished = std::move(pFinished)]() mutable
+ {
+ assert(!impl::refThreadLocalInterruptionStatus());
+ impl::refThreadLocalInterruptionStatus() = intStatus.get();
+ ZEN_ON_SCOPE_EXIT(impl::refThreadLocalInterruptionStatus() = nullptr);
+ ZEN_ON_SCOPE_EXIT(pFinished.set_value());
+
+ try
+ {
+ f(); //throw ThreadInterruption
+ }
+ catch (ThreadInterruption&) {}
+ });
+}
+
+
+inline
+void InterruptibleThread::interrupt() { intStatus_->interrupt(); }
}
-#endif //BOOST_THREAD_WRAP_H_78963234
+#endif //STD_THREAD_WRAP_H_7896323423432
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index 8dcd4736..e2a756e6 100644
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -43,22 +43,12 @@ time per call | function
#ifdef ZEN_WIN
namespace
{
-//warning: LOCALE_INVARIANT is NOT available with Windows 2000, so we have to make yet another distinction...
-const LCID ZSTRING_INVARIANT_LOCALE = zen::winXpOrLater() ?
- LOCALE_INVARIANT :
- MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); //see: http://msdn.microsoft.com/en-us/goglobal/bb688122.aspx
-
//try to call "CompareStringOrdinal" for low-level string comparison: unfortunately available not before Windows Vista!
//by a factor ~3 faster than old string comparison using "LCMapString"
typedef int (WINAPI* CompareStringOrdinalFunc)(LPCWSTR lpString1, int cchCount1,
LPCWSTR lpString2, int cchCount2, BOOL bIgnoreCase);
const SysDllFun<CompareStringOrdinalFunc> compareStringOrdinal = SysDllFun<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal");
//watch for dependencies in global namespace!!!
-//caveat: function scope static initialization is not thread-safe in VS 2010!
-#if defined _MSC_VER && _MSC_VER > 1800
- #error not true anymore
-#endif
-
}
@@ -92,7 +82,7 @@ int cmpFilePath(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen
auto copyToUpperCase = [&](const wchar_t* strIn, wchar_t* strOut)
{
//faster than CharUpperBuff + wmemcpy or CharUpper + wmemcpy and same speed like ::CompareString()
- if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale,
+ if (::LCMapString(LOCALE_INVARIANT, //__in LCID Locale,
LCMAP_UPPERCASE, //__in DWORD dwMapFlags,
strIn, //__in LPCTSTR lpSrcStr,
static_cast<int>(minSize), //__in int cchSrc,
@@ -138,13 +128,15 @@ Zstring makeUpperCopy(const Zstring& str)
Zstring output;
output.resize(len);
+ //LOCALE_INVARIANT is NOT available with Windows 2000 -> ok
+
//use Windows' upper case conversion: faster than ::CharUpper()
- if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale,
- LCMAP_UPPERCASE, //__in DWORD dwMapFlags,
- str.c_str(), //__in LPCTSTR lpSrcStr,
- len, //__in int cchSrc,
- &*output.begin(), //__out LPTSTR lpDestStr,
- len) == 0) //__in int cchDest
+ if (::LCMapString(LOCALE_INVARIANT, //__in LCID Locale,
+ LCMAP_UPPERCASE, //__in DWORD dwMapFlags,
+ str.c_str(), //__in LPCTSTR lpSrcStr,
+ len, //__in int cchSrc,
+ &*output.begin(), //__out LPTSTR lpDestStr,
+ len) == 0) //__in int cchDest
throw std::runtime_error("Error comparing strings (LCMapString). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
return output;
bgstack15