summaryrefslogtreecommitdiff
path: root/zen/thread.h
diff options
context:
space:
mode:
Diffstat (limited to 'zen/thread.h')
-rwxr-xr-xzen/thread.h830
1 files changed, 415 insertions, 415 deletions
diff --git a/zen/thread.h b/zen/thread.h
index ae4c347e..76306a51 100755
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -1,415 +1,415 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef THREAD_H_7896323423432235246427
-#define THREAD_H_7896323423432235246427
-
-#include <thread>
-#include <future>
-#include "scope_guard.h"
-#include "type_traits.h"
-#include "optional.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
-
-
-uint64_t getThreadId(); //simple integer thread id, unlike boost::thread::id: https://svn.boost.org/trac/boost/ticket/5754
-
-//------------------------------------------------------------------------------------------
-
-/*
-std::async replacement without crappy semantics:
- 1. guaranteed to run asynchronously
- 2. does not follow C++11 [futures.async], Paragraph 5, where std::future waits for thread in destructor
-
-Example:
- Zstring dirpath = ...
- auto ft = zen::runAsync([=](){ return zen::dirExists(dirpath); });
- if (ft.wait_for(std::chrono::milliseconds(200)) == std::future_status::ready && ft.get())
- //dir exising
-*/
-template <class Function>
-auto runAsync(Function&& 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
-{
-public:
- GetFirstResult();
-
- template <class Fun>
- void addJob(Fun&& f); //f must return a zen::Opt<T> containing a value if successful
-
- template <class Duration>
- bool timedWait(const Duration& duration) const; //true: "get()" is ready, false: time elapsed
-
- //return first value or none if all jobs failed; blocks until result is ready!
- Opt<T> get() const; //may be called only once!
-
-private:
- class AsyncResult;
- std::shared_ptr<AsyncResult> asyncResult_;
- size_t jobsTotal_ = 0;
-};
-
-//------------------------------------------------------------------------------------------
-
-//value associated with mutex and guaranteed protected access:
-template <class T>
-class Protected
-{
-public:
- Protected() {}
- Protected(const T& value) : value_(value) {}
-
- template <class Function>
- void access(Function fun)
- {
- std::lock_guard<std::mutex> dummy(lockValue);
- fun(value_);
- }
-
-private:
- Protected (const Protected&) = delete;
- Protected& operator=(const Protected&) = delete;
-
- std::mutex lockValue;
- T value_{};
-};
-
-
-
-
-
-
-
-
-
-//###################### implementation ######################
-
-namespace impl
-{
-template <class Function> inline
-auto runAsync(Function&& fun, TrueType /*copy-constructible*/)
-{
- using ResultType = decltype(fun());
-
- //note: std::packaged_task does NOT support move-only function objects!
- std::packaged_task<ResultType()> pt(std::forward<Function>(fun));
- auto fut = pt.get_future();
- 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;
-}
-
-
-template <class Function> inline
-auto runAsync(Function&& fun, FalseType /*copy-constructible*/)
-{
- //support move-only function objects!
- auto sharedFun = std::make_shared<Function>(std::forward<Function>(fun));
- return runAsync([sharedFun] { return (*sharedFun)(); }, TrueType());
-}
-}
-
-
-template <class Function> inline
-auto runAsync(Function&& fun)
-{
- return impl::runAsync(std::forward<Function>(fun), StaticBool<std::is_copy_constructible<Function>::value>());
-}
-
-
-template<class InputIterator, class Duration> inline
-bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& duration)
-{
- const std::chrono::steady_clock::time_point stopTime = std::chrono::steady_clock::now() + duration;
- for (; first != last; ++first)
- if (first->wait_until(stopTime) != std::future_status::ready)
- return false; //time elapsed
- return true;
-}
-
-
-template <class T>
-class GetFirstResult<T>::AsyncResult
-{
-public:
- //context: worker threads
- void reportFinished(Opt<T>&& result)
- {
- {
- std::lock_guard<std::mutex> dummy(lockResult);
- ++jobsFinished;
- if (!result_)
- result_ = std::move(result);
- }
- 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)
- {
- std::unique_lock<std::mutex> dummy(lockResult);
- return conditionJobDone.wait_for(dummy, duration, [&] { return this->jobDone(jobsTotal); });
- }
-
- Opt<T> getResult(size_t jobsTotal)
- {
- std::unique_lock<std::mutex> dummy(lockResult);
- conditionJobDone.wait(dummy, [&] { return this->jobDone(jobsTotal); });
-
- return std::move(result_);
- }
-
-private:
- bool jobDone(size_t jobsTotal) const { return result_ || (jobsFinished >= jobsTotal); } //call while locked!
-
-
- std::mutex lockResult;
- size_t jobsFinished = 0; //
- Opt<T> result_; //our condition is: "have result" or "jobsFinished == jobsTotal"
- std::condition_variable conditionJobDone;
-};
-
-
-
-template <class T> inline
-GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult>()) {}
-
-
-template <class T>
-template <class Fun> inline
-void GetFirstResult<T>::addJob(Fun&& f) //f must return a zen::Opt<T> containing a value on success
-{
- std::thread t([asyncResult = this->asyncResult_, f = std::forward<Fun>(f)] { asyncResult->reportFinished(f()); });
- ++jobsTotal_;
- t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
-}
-
-
-template <class T>
-template <class Duration> inline
-bool GetFirstResult<T>::timedWait(const Duration& duration) const { return asyncResult_->waitForResult(jobsTotal_, duration); }
-
-
-template <class T> inline
-Opt<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:
- #define ZEN_THREAD_LOCAL_SPECIFIER __thread
-
-
-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, [this] { 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!!!
- //"The default constructor is trivial: no initialization takes place other than zero initialization of static and thread-local objects."
-
- 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::forward<Function>(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(); }
-
-
-
-
-inline
-uint64_t getThreadId()
-{
- //obviously "gettid()" is not available on Ubuntu/Debian/Suse => use the OpenSSL approach:
- static_assert(sizeof(uint64_t) >= sizeof(void*), "");
- return reinterpret_cast<uint64_t>(static_cast<void*>(&errno));
-
-}
-}
-
-#endif //THREAD_H_7896323423432235246427
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef THREAD_H_7896323423432235246427
+#define THREAD_H_7896323423432235246427
+
+#include <thread>
+#include <future>
+#include "scope_guard.h"
+#include "type_traits.h"
+#include "optional.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
+
+
+uint64_t getThreadId(); //simple integer thread id, unlike boost::thread::id: https://svn.boost.org/trac/boost/ticket/5754
+
+//------------------------------------------------------------------------------------------
+
+/*
+std::async replacement without crappy semantics:
+ 1. guaranteed to run asynchronously
+ 2. does not follow C++11 [futures.async], Paragraph 5, where std::future waits for thread in destructor
+
+Example:
+ Zstring dirpath = ...
+ auto ft = zen::runAsync([=](){ return zen::dirExists(dirpath); });
+ if (ft.wait_for(std::chrono::milliseconds(200)) == std::future_status::ready && ft.get())
+ //dir exising
+*/
+template <class Function>
+auto runAsync(Function&& 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
+{
+public:
+ GetFirstResult();
+
+ template <class Fun>
+ void addJob(Fun&& f); //f must return a zen::Opt<T> containing a value if successful
+
+ template <class Duration>
+ bool timedWait(const Duration& duration) const; //true: "get()" is ready, false: time elapsed
+
+ //return first value or none if all jobs failed; blocks until result is ready!
+ Opt<T> get() const; //may be called only once!
+
+private:
+ class AsyncResult;
+ std::shared_ptr<AsyncResult> asyncResult_;
+ size_t jobsTotal_ = 0;
+};
+
+//------------------------------------------------------------------------------------------
+
+//value associated with mutex and guaranteed protected access:
+template <class T>
+class Protected
+{
+public:
+ Protected() {}
+ Protected(const T& value) : value_(value) {}
+
+ template <class Function>
+ void access(Function fun)
+ {
+ std::lock_guard<std::mutex> dummy(lockValue);
+ fun(value_);
+ }
+
+private:
+ Protected (const Protected&) = delete;
+ Protected& operator=(const Protected&) = delete;
+
+ std::mutex lockValue;
+ T value_{};
+};
+
+
+
+
+
+
+
+
+
+//###################### implementation ######################
+
+namespace impl
+{
+template <class Function> inline
+auto runAsync(Function&& fun, TrueType /*copy-constructible*/)
+{
+ using ResultType = decltype(fun());
+
+ //note: std::packaged_task does NOT support move-only function objects!
+ std::packaged_task<ResultType()> pt(std::forward<Function>(fun));
+ auto fut = pt.get_future();
+ 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;
+}
+
+
+template <class Function> inline
+auto runAsync(Function&& fun, FalseType /*copy-constructible*/)
+{
+ //support move-only function objects!
+ auto sharedFun = std::make_shared<Function>(std::forward<Function>(fun));
+ return runAsync([sharedFun] { return (*sharedFun)(); }, TrueType());
+}
+}
+
+
+template <class Function> inline
+auto runAsync(Function&& fun)
+{
+ return impl::runAsync(std::forward<Function>(fun), StaticBool<std::is_copy_constructible<Function>::value>());
+}
+
+
+template<class InputIterator, class Duration> inline
+bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& duration)
+{
+ const std::chrono::steady_clock::time_point stopTime = std::chrono::steady_clock::now() + duration;
+ for (; first != last; ++first)
+ if (first->wait_until(stopTime) != std::future_status::ready)
+ return false; //time elapsed
+ return true;
+}
+
+
+template <class T>
+class GetFirstResult<T>::AsyncResult
+{
+public:
+ //context: worker threads
+ void reportFinished(Opt<T>&& result)
+ {
+ {
+ std::lock_guard<std::mutex> dummy(lockResult);
+ ++jobsFinished;
+ if (!result_)
+ result_ = std::move(result);
+ }
+ 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)
+ {
+ std::unique_lock<std::mutex> dummy(lockResult);
+ return conditionJobDone.wait_for(dummy, duration, [&] { return this->jobDone(jobsTotal); });
+ }
+
+ Opt<T> getResult(size_t jobsTotal)
+ {
+ std::unique_lock<std::mutex> dummy(lockResult);
+ conditionJobDone.wait(dummy, [&] { return this->jobDone(jobsTotal); });
+
+ return std::move(result_);
+ }
+
+private:
+ bool jobDone(size_t jobsTotal) const { return result_ || (jobsFinished >= jobsTotal); } //call while locked!
+
+
+ std::mutex lockResult;
+ size_t jobsFinished = 0; //
+ Opt<T> result_; //our condition is: "have result" or "jobsFinished == jobsTotal"
+ std::condition_variable conditionJobDone;
+};
+
+
+
+template <class T> inline
+GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult>()) {}
+
+
+template <class T>
+template <class Fun> inline
+void GetFirstResult<T>::addJob(Fun&& f) //f must return a zen::Opt<T> containing a value on success
+{
+ std::thread t([asyncResult = this->asyncResult_, f = std::forward<Fun>(f)] { asyncResult->reportFinished(f()); });
+ ++jobsTotal_;
+ t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
+}
+
+
+template <class T>
+template <class Duration> inline
+bool GetFirstResult<T>::timedWait(const Duration& duration) const { return asyncResult_->waitForResult(jobsTotal_, duration); }
+
+
+template <class T> inline
+Opt<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:
+ #define ZEN_THREAD_LOCAL_SPECIFIER __thread
+
+
+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, [this] { 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!!!
+ //"The default constructor is trivial: no initialization takes place other than zero initialization of static and thread-local objects."
+
+ 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::forward<Function>(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(); }
+
+
+
+
+inline
+uint64_t getThreadId()
+{
+ //obviously "gettid()" is not available on Ubuntu/Debian/Suse => use the OpenSSL approach:
+ static_assert(sizeof(uint64_t) >= sizeof(void*), "");
+ return reinterpret_cast<uint64_t>(static_cast<void*>(&errno));
+
+}
+}
+
+#endif //THREAD_H_7896323423432235246427
bgstack15