/* Copyright (c) 2013 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Author: Leonardo de Moura */ #pragma once #include #include "util/thread.h" #include "util/stackinfo.h" #include "util/exception.h" namespace lean { /** \brief Mark flag for interrupting current thread. */ void request_interrupt(); /** \brief Reset (interrupt) flag for current thread. */ void reset_interrupt(); /** \brief Return true iff the current thread was marked for interruption. */ bool interrupt_requested(); /** \brief Throw an interrupted exception if the (interrupt) flag is set. */ void check_interrupted(); inline void check_system(char const * component_name) { check_stack(component_name); check_interrupted(); } constexpr unsigned g_small_sleep = 50; /** \brief Put the current thread to sleep for \c ms milliseconds. \remark check_interrupted is invoked every \c step_ms milliseconds; */ void sleep_for(unsigned ms, unsigned step_ms = g_small_sleep); /** \brief Thread that provides a method for setting its interrupt flag. */ class interruptible_thread { public: #if !defined(LEAN_USE_BOOST) template interruptible_thread(Function && fun, Args &&... args): m_flag_addr(nullptr), m_thread( [&](Function&& fun, Args&&... args) { m_flag_addr.store(get_flag_addr()); save_stack_info(false); fun(std::forward(args)...); m_flag_addr.store(&m_dummy_addr); // see comment before m_dummy_addr run_thread_finalizers(); run_post_thread_finalizers(); }, std::forward(fun), std::forward(args)...) {} #else // Simpler version that works with Boost, and set stack size private: std::function m_fun; static void execute(interruptible_thread * _this) { _this->m_flag_addr.store(get_flag_addr()); save_stack_info(false); _this->m_fun(); _this->m_flag_addr.store(&(_this->m_dummy_addr)); // see comment before m_dummy_addr run_thread_finalizers(); run_post_thread_finalizers(); } public: template interruptible_thread(Function && fun):m_fun(fun), m_flag_addr(nullptr), m_thread(get_thread_attributes(), boost::bind(execute, this)) {} #endif /** \brief Return true iff an interrupt request has been made to the current thread. */ bool interrupted() const; /** \brief Send a interrupt request to the current thread. Return true iff the request has been successfully performed. \remark The main thread may have to wait the interrupt flag of this thread to be initialized. If the flag was not initialized, then the main thread will be put to sleep for \c try_ms milliseconds until it tries to set the flag again. */ void request_interrupt(unsigned try_ms = g_small_sleep); void join(); bool joinable(); private: atomic m_flag_addr; /* The following auxiliary field is used to workaround a nasty bug that occurs in some platforms. On cygwin, it seems that the thread local storage is deleted before m_thread is destructed. Then, the main thread may corrupt memory if it invokes request_interrupt after the child thread referenced by m_thread has terminated. The method request_interrupt access the child thread local storage pointed by m_flag_addr. To avoid this bug, we use a simple hack, we reset m_flag_addr to m_dummy_addr before terminating the execution of the child thread. */ atomic_bool m_dummy_addr; thread m_thread; static atomic_bool * get_flag_addr(); }; #if !defined(LEAN_MULTI_THREAD) inline void check_threadsafe() { throw exception("Lean was compiled without support for multi-threading"); } #else inline void check_threadsafe() {} #endif }