diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e6b74a5e1..fe202179a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,6 +28,7 @@ endif () # GMP include(cmake/FindGMP.cmake) +set(EXTRA_LIBS ${EXTRA_LIBS} ${GMP_LIBRARIES}) # tcmalloc include(cmake/FindTcmalloc.cmake) @@ -40,10 +41,13 @@ include_directories(${LEAN_SOURCE_DIR}/numerics) include_directories(${LEAN_SOURCE_DIR}/interval) add_subdirectory(util) +set(EXTRA_LIBS ${EXTRA_LIBS} util) add_subdirectory(numerics) +set(EXTRA_LIBS ${EXTRA_LIBS} numerics) add_subdirectory(interval) -set(EXTRA_LIBS ${EXTRA_LIBS} util numerics interval ${GMP_LIBRARIES}) +set(EXTRA_LIBS ${EXTRA_LIBS} interval) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread") add_subdirectory(shell) +add_subdirectory(tests/util) add_subdirectory(tests/numerics) add_subdirectory(tests/interval) diff --git a/src/tests/util/CMakeLists.txt b/src/tests/util/CMakeLists.txt new file mode 100644 index 000000000..203f770d3 --- /dev/null +++ b/src/tests/util/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(interrupt interrupt.cpp) +target_link_libraries(interrupt ${EXTRA_LIBS}) +add_test(interrupt ${CMAKE_CURRENT_BINARY_DIR}/interrupt) diff --git a/src/tests/util/interrupt.cpp b/src/tests/util/interrupt.cpp new file mode 100644 index 000000000..cfa8577f3 --- /dev/null +++ b/src/tests/util/interrupt.cpp @@ -0,0 +1,45 @@ +/* +Copyright (c) 2013 Microsoft Corporation. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. + +Author: Leonardo de Moura +*/ +#include +#include +#include +#include "interrupt.h" +#include "test.h" + +std::mutex g_stdout_mutex; + +static void loop(unsigned i) { + try { + while (true) { + lean::check_interrupt(); + } + } + catch (lean::interrupt) { + std::lock_guard lock(g_stdout_mutex); + std::cout << "interrupted " << i << ".\n"; + } +} + +static void tst1() { + lean::interruptible_thread t1([](){ loop(1); }); + lean::interruptible_thread t2([](){ loop(2); }); + + std::chrono::milliseconds dura(200); + std::this_thread::sleep_for(dura); + t2.request_interrupt(); + + std::this_thread::sleep_for(dura); + t1.request_interrupt(); + + t1.join(); + t2.join(); +} + +int main() { + tst1(); + return 0; +} diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 561812db0..7e9f1ae6b 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1 +1 @@ -add_library(util trace.cpp debug.cpp name.cpp exception.cpp verbosity.cpp) +add_library(util trace.cpp debug.cpp name.cpp exception.cpp verbosity.cpp interrupt.cpp) diff --git a/src/util/interrupt.cpp b/src/util/interrupt.cpp new file mode 100644 index 000000000..b19fb5429 --- /dev/null +++ b/src/util/interrupt.cpp @@ -0,0 +1,57 @@ +/* +Copyright (c) 2013 Microsoft Corporation. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. + +Author: Leonardo de Moura +*/ +#include "interrupt.h" + +namespace lean { + +thread_local std::atomic_bool g_interrupt; + +void request_interrupt() { + g_interrupt.store(true); +} + +void reset_interrupt() { + g_interrupt.store(false); +} + +bool interrupted() { + return g_interrupt.load(); +} + +void check_interrupt() { + if (interrupted()) + throw interrupt(); +} + +std::atomic_bool * interruptible_thread::get_flag_addr() { + return &g_interrupt; +} + +bool interruptible_thread::interrupted() const { + std::atomic_bool * f = m_flag_addr.load(); + if (f == nullptr) + return false; + return f->load(); +} + +bool interruptible_thread::request_interrupt() { + std::atomic_bool * f = m_flag_addr.load(); + if (f == nullptr) + return false; + f->store(true); + return true; +} + +void interruptible_thread::join() { + m_thread.join(); +} + +bool interruptible_thread::joinable() { + return m_thread.joinable(); +} + +} diff --git a/src/util/interrupt.h b/src/util/interrupt.h new file mode 100644 index 000000000..a54acee08 --- /dev/null +++ b/src/util/interrupt.h @@ -0,0 +1,74 @@ +/* +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 +#include +// APIs for interrupting Lean. + +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 if the +*/ +bool interrupted(); + +class interrupt : public std::exception { +public: + virtual char const * what() const noexcept { return "interrupted"; } +}; + +/** + \brief Throw an interrupt exception if the (interrupt) flag is set. +*/ +void check_interrupt(); + +/** + \brief Thread that provides a method for setting its interrupt flag. +*/ +class interruptible_thread { +private: + std::atomic m_flag_addr; + std::thread m_thread; + static std::atomic_bool * get_flag_addr(); +public: + template + interruptible_thread(Function && fun, Args &&... args): + m_flag_addr(nullptr), + m_thread( + [&](Function&& fun, Args&&... args) { + m_flag_addr.store(get_flag_addr()); + fun(std::forward(args)...); + }, + std::forward(fun), + std::forward(args)...) + {} + + /** + \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. + */ + bool request_interrupt(); + + void join(); + bool joinable(); +}; + +}