// ************************************************************************** // * This file is part of the FreeFileSync project. It is distributed under * // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** #ifndef BOOST_THREAD_WRAP_H #define BOOST_THREAD_WRAP_H //temporary solution until C++11 thread becomes fully available #include //fix this pathetic boost thread warning mess #ifdef __MINGW32__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" #pragma GCC diagnostic ignored "-Wstrict-aliasing" #pragma GCC diagnostic ignored "-Wshadow" #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 #include #ifdef __MINGW32__ #pragma GCC diagnostic pop #endif #ifdef _MSC_VER #pragma warning(pop) #endif namespace zen { /* std::async replacement without crappy semantics: 1. guaranteed to run asynchronous 2. does not follow C++11 [futures.async], Paragraph 5, where std::future waits for thread in destructor Example: Zstring dirname = ... auto ft = zen::async([=](){ return zen::dirExists(dirname); }); if (ft.timed_wait(boost::posix_time::milliseconds(200)) && ft.get()) //dir exising */ template auto async(Function fun) -> boost::unique_future; //wait for all with a time limit: return true if *all* results are available! template bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& wait_duration); //wait until first job is successful or all failed template class RunUntilFirstHit { public: RunUntilFirstHit(); template void addJob(Fun f); //f must return a std::unique_ptr containing a value if successful template 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! std::unique_ptr get() const; //may be called only once! private: class AsyncResult; std::shared_ptr result; size_t jobsTotal; }; //###################### implementation ###################### #ifndef BOOST_HAS_THREADS #error just some paranoia check... #endif template inline auto async2(Function fun) -> boost::unique_future //support for workaround of VS2010 bug: bool (*fun)(); decltype(fun()) == int! { #if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK //mirror "boost/thread/future.hpp", hopefully they know what they're doing boost::packaged_task pt(std::move(fun)); //packaged task seems to even require r-value reference: https://sourceforge.net/p/freefilesync/bugs/234/ #else boost::packaged_task pt(std::move(fun)); #endif 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()!!! return std::move(fut); //compiler error without "move", why needed??? } template inline auto async(Function fun) -> boost::unique_future { return async2(fun); } template inline bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& wait_duration) { const boost::system_time endTime = boost::get_system_time() + wait_duration; for (; first != last; ++first) if (!first->timed_wait_until(endTime)) return false; //time elapsed return true; } template class RunUntilFirstHit::AsyncResult { public: AsyncResult() : #ifndef NDEBUG returnedResult(false), #endif jobsFinished(0) {} //context: worker threads void reportFinished(std::unique_ptr&& result) { { boost::unique_lock 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 } //context: main thread template bool waitForResult(size_t jobsTotal, const Duration& duration) { boost::unique_lock dummy(lockResult); return conditionJobDone.timed_wait(dummy, duration, [&] { return this->jobDone(jobsTotal); }); //use timed_wait predicate if exitting before condition is reached: http://www.boost.org/doc/libs/1_49_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref.condition_variable.timed_wait_rel } std::unique_ptr getResult(size_t jobsTotal) { boost::unique_lock dummy(lockResult); while (!jobDone(jobsTotal)) conditionJobDone.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! #ifndef NDEBUG assert(!returnedResult); returnedResult = true; #endif return std::move(result_); } private: bool jobDone(size_t jobsTotal) const { return result_ || (jobsFinished >= jobsTotal); } //call while locked! #ifndef NDEBUG bool returnedResult; #endif boost::mutex lockResult; size_t jobsFinished; // std::unique_ptr result_; //our condition is: "have result" or "jobsFinished == jobsTotal" boost::condition_variable conditionJobDone; }; template inline RunUntilFirstHit::RunUntilFirstHit() : result(std::make_shared()), jobsTotal(0) {} template template inline void RunUntilFirstHit::addJob(Fun f) //f must return a std::unique_ptr containing a value on success { auto result2 = result; //MSVC2010: this is ridiculous!!! boost::thread t([result2, f] { result2->reportFinished(f()); }); ++jobsTotal; t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!! } template template inline bool RunUntilFirstHit::timedWait(const Duration& duration) const { return result->waitForResult(jobsTotal, duration); } template inline std::unique_ptr RunUntilFirstHit::get() const { return result->getResult(jobsTotal); } } #endif //BOOST_THREAD_WRAP_H