refactor(*): isolate std::thread dependency

This commit allows us to build Lean without the pthread dependency.
It is also useful if we want to implement multi-threading on top of Boost.

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2013-12-09 14:56:48 -08:00
parent 0eaa98221b
commit 8f2fe273ea
51 changed files with 417 additions and 297 deletions

View file

@ -7,7 +7,7 @@ set(CMAKE_COLOR_MAKEFILE ON)
enable_testing()
option(TRACK_MEMORY_USAGE "TRACK_MEMORY_USAGE" ON)
option(THREAD_SAFE "THREAD_SAFE" ON)
option(MULTI_THREAD "MULTI_THREAD" ON)
# Added for CTest
INCLUDE(CTest)
@ -26,9 +26,10 @@ if((${CYGWIN} EQUAL "1") OR (${CMAKE_SYSTEM_NAME} MATCHES "Windows"))
set(LEAN_EXTRA_CXX_FLAGS "${LEAN_EXTRA_CXX_FLAGS} -D LEAN_WINDOWS -D LEAN_WIN_STACK_SIZE=${LEAN_WIN_STACK_SIZE}")
endif()
if("${THREAD_SAFE}" MATCHES "OFF")
message(STATUS "Disabled thread-safety, it will not be safe to run multiple threads in parallel")
set(LEAN_EXTRA_CXX_FLAGS "${LEAN_EXTRA_CXX_FLAGS} -D LEAN_THREAD_UNSAFE")
if("${MULTI_THREAD}" MATCHES "OFF")
message(STATUS "Disabled multi-thread support, it will not be safe to run multiple threads in parallel")
else()
set(LEAN_EXTRA_CXX_FLAGS "${LEAN_EXTRA_CXX_FLAGS} -D LEAN_MULTI_THREAD")
endif()
# Set Module Path
@ -173,7 +174,11 @@ add_subdirectory(frontends/lean)
set(LEAN_LIBS ${LEAN_LIBS} lean_frontend)
add_subdirectory(frontends/lua)
set(LEAN_LIBS ${LEAN_LIBS} leanlua)
if("${MULTI_THREAD}" MATCHES "ON")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread ${LEAN_EXTRA_LINKER_FLAGS}")
else()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LEAN_EXTRA_LINKER_FLAGS}")
endif()
set(CMAKE_EXE_LINKER_FLAGS_TESTCOV "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
set(EXTRA_LIBS ${LEAN_LIBS} ${EXTRA_LIBS})
add_subdirectory(shell)

View file

@ -4,12 +4,11 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <atomic>
#include <unordered_set>
#include <vector>
#include <utility>
#include <functional>
#include "util/thread.h"
#include "util/map.h"
#include "util/sstream.h"
#include "util/exception.h"

View file

@ -195,9 +195,9 @@ void import_basic(environment & env);
*/
#define MK_BUILTIN(Name, ClassName) \
expr mk_##Name() { \
static thread_local expr r = mk_value(*(new ClassName())); \
static LEAN_THREAD_LOCAL expr r = mk_value(*(new ClassName())); \
return r; \
} \
}
/**
\brief Helper macro for generating "defined" constants.
@ -205,7 +205,7 @@ expr mk_##Name() { \
#define MK_CONSTANT(Name, NameObj) \
static name Name ## _name = NameObj; \
expr mk_##Name() { \
static thread_local expr r = mk_constant(Name ## _name); \
static LEAN_THREAD_LOCAL expr r = mk_constant(Name ## _name); \
return r ; \
} \
bool is_ ## Name(expr const & e) { \

View file

@ -7,11 +7,10 @@ Author: Leonardo de Moura
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <atomic>
#include <tuple>
#include <set>
#include <unordered_map>
#include <mutex>
#include "util/thread.h"
#include "util/safe_arith.h"
#include "util/realpath.h"
#include "kernel/for_each_fn.h"
@ -26,17 +25,17 @@ namespace lean {
static name g_builtin_module("builtin_module");
class extension_factory {
std::vector<environment::mk_extension> m_makers;
std::mutex m_makers_mutex;
mutex m_makers_mutex;
public:
unsigned register_extension(environment::mk_extension mk) {
std::lock_guard<std::mutex> lock(m_makers_mutex);
lock_guard<mutex> lock(m_makers_mutex);
unsigned r = m_makers.size();
m_makers.push_back(mk);
return r;
}
std::unique_ptr<environment::extension> mk(unsigned extid) {
std::lock_guard<std::mutex> lock(m_makers_mutex);
lock_guard<mutex> lock(m_makers_mutex);
return m_makers[extid]();
}
};
@ -61,11 +60,7 @@ struct environment::imp {
std::vector<constraint> m_constraints;
std::vector<level> m_uvars;
// Children environment management
#ifdef LEAN_THREAD_UNSAFE
unsigned m_num_children;
#else
std::atomic<unsigned> m_num_children;
#endif
atomic<unsigned> m_num_children;
std::shared_ptr<imp> m_parent;
// Object management
std::vector<object> m_objects;

View file

@ -45,7 +45,7 @@ expr_cell::expr_cell(expr_kind k, unsigned h, bool has_mv):
// Remark: using pointer address as a hash code is not a good idea.
// - each execution run will behave differently.
// - the hash is not diverse enough
static thread_local unsigned g_hash_alloc_counter = 0;
static LEAN_THREAD_LOCAL unsigned g_hash_alloc_counter = 0;
m_hash_alloc = g_hash_alloc_counter;
g_hash_alloc_counter++;
}
@ -218,7 +218,7 @@ void expr_cell::dealloc() {
}
expr mk_type() {
static thread_local expr r = mk_type(level());
static LEAN_THREAD_LOCAL expr r = mk_type(level());
return r;
}

View file

@ -6,11 +6,11 @@ Author: Leonardo de Moura
*/
#pragma once
#include <algorithm>
#include <atomic>
#include <iostream>
#include <limits>
#include <utility>
#include <tuple>
#include "util/thread.h"
#include "util/lua.h"
#include "util/rc.h"
#include "util/name.h"
@ -71,7 +71,7 @@ protected:
#ifdef LEAN_THREAD_UNSAFE
unsigned short m_flags;
#else
std::atomic_ushort m_flags;
atomic_ushort m_flags;
#endif
unsigned m_hash; // hash based on the structure of the expression (this is a good hash for structural equality)
unsigned m_hash_alloc; // hash based on 'time' of allocation (this is a good hash for pointer-based equality)

View file

@ -32,7 +32,7 @@ public:
*/
class read_write_environment {
environment m_env;
unique_lock m_lock;
exclusive_lock m_lock;
public:
read_write_environment(environment const & env);
~read_write_environment();

View file

@ -121,7 +121,7 @@ int io_state_set_options(lua_State * L) {
return 0;
}
static std::mutex g_print_mutex;
static mutex g_print_mutex;
static void print(io_state * ios, bool reg, char const * msg) {
if (ios) {
@ -136,7 +136,7 @@ static void print(io_state * ios, bool reg, char const * msg) {
/** \brief Thread safe version of print function */
static int print(lua_State * L, int start, bool reg) {
std::lock_guard<std::mutex> lock(g_print_mutex);
lock_guard<mutex> lock(g_print_mutex);
io_state * ios = get_io_state(L);
int n = lua_gettop(L);
int i;

View file

@ -689,7 +689,7 @@ static int mk_lua_tactic01(lua_State * L) {
});
while (!done) {
check_interrupted();
std::this_thread::yield(); // give another thread a chance to execute
this_thread::yield(); // give another thread a chance to execute
S_copy.exec_protected([&]() {
done = resume(co, 0);
});

View file

@ -8,8 +8,8 @@ Author: Leonardo de Moura
#include <algorithm>
#include <utility>
#include <memory>
#include <mutex>
#include <string>
#include "util/thread.h"
#include "util/lazy_list.h"
#include "library/io_state.h"
#include "library/tactic/proof_state.h"

View file

@ -75,7 +75,7 @@ ENDFOREACH(T)
# LEAN LUA THREAD TESTS
if((${CYGWIN} EQUAL "1") OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux"))
if ((NOT (${CMAKE_CXX_COMPILER} MATCHES "clang")) AND (${THREAD_SAFE} MATCHES "ON"))
if ((NOT (${CMAKE_CXX_COMPILER} MATCHES "clang")) AND (${MULTI_THREAD} MATCHES "ON"))
file(GLOB LEANLUATHREADTESTS "${LEAN_SOURCE_DIR}/../tests/lua/threads/*.lua")
FOREACH(T ${LEANLUATHREADTESTS})
GET_FILENAME_COMPONENT(T_NAME ${T} NAME)

View file

@ -5,8 +5,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <algorithm>
#include <thread>
#include <chrono>
#include "util/thread.h"
#include "util/test.h"
#include "util/trace.h"
#include "util/exception.h"
@ -213,7 +213,7 @@ static expr mk_big(unsigned depth) {
}
static void tst5() {
#ifndef __APPLE__
#if !defined(__APPLE__) && defined(LEAN_MULTI_THREAD)
expr t = mk_big(18);
environment env = mk_toplevel();
env.add_var("f", Bool >> (Bool >> Bool));
@ -230,7 +230,7 @@ static void tst5() {
std::cout << "interrupted\n";
}
});
std::this_thread::sleep_for(dura);
this_thread::sleep_for(dura);
thread.request_interrupt();
thread.join();
#endif

View file

@ -4,9 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <thread>
#include <mutex>
#include <vector>
#include "util/thread.h"
#include "util/test.h"
#include "kernel/expr.h"
#include "kernel/free_vars.h"
@ -48,11 +47,11 @@ static void tst1() {
expr a = Const("a");
expr f = Const("f");
a = f(a, a);
std::vector<std::thread> ts;
std::vector<thread> ts;
#if !defined(__APPLE__) && !defined(LEAN_THREAD_UNSAFE)
#if !defined(__APPLE__) && defined(LEAN_MULTI_THREAD)
for (unsigned i = 0; i < 8; i++) {
ts.push_back(std::thread([&](){ save_stack_info(); mk(a); }));
ts.push_back(thread([&](){ save_stack_info(); mk(a); }));
}
for (unsigned i = 0; i < 8; i++) {
ts[i].join();

View file

@ -5,9 +5,9 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <iostream>
#include <thread>
#include <chrono>
#include <string>
#include "util/thread.h"
#include "util/test.h"
#include "util/trace.h"
#include "util/exception.h"
@ -221,7 +221,7 @@ static expr mk_big(unsigned depth) {
}
static void tst12() {
#ifndef __APPLE__
#if !defined(__APPLE__) && defined(LEAN_MULTI_THREAD)
expr t = mk_big(18);
environment env = mk_toplevel();
env.add_var("f", Int >> (Int >> Int));
@ -238,7 +238,7 @@ static void tst12() {
std::cout << "interrupted\n";
}
});
std::this_thread::sleep_for(dura);
this_thread::sleep_for(dura);
thread.request_interrupt();
thread.join();
#endif

View file

@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <thread>
#include "util/thread.h"
#include "util/test.h"
#include "kernel/builtin.h"
#include "kernel/normalizer.h"
@ -122,9 +122,9 @@ static void tst6() {
std::cout << mk_int_add_fn().raw() << "\n";
#ifndef __APPLE__
std::thread t1([](){ save_stack_info(); std::cout << "t1: " << mk_int_add_fn().raw() << "\n"; });
thread t1([](){ save_stack_info(); std::cout << "t1: " << mk_int_add_fn().raw() << "\n"; });
t1.join();
std::thread t2([](){ save_stack_info(); std::cout << "t2: " << mk_int_add_fn().raw() << "\n"; });
thread t2([](){ save_stack_info(); std::cout << "t2: " << mk_int_add_fn().raw() << "\n"; });
t2.join();
#endif
std::cout << mk_int_add_fn().raw() << "\n";

View file

@ -26,7 +26,7 @@ tactic loop_tactic() {
});
}
tactic set_tactic(std::atomic<bool> * flag) {
tactic set_tactic(atomic<bool> * flag) {
return mk_tactic1([=](environment const &, io_state const &, proof_state const & s) -> proof_state {
*flag = true;
return s;
@ -65,14 +65,14 @@ static void tst1() {
check_failure(now_tactic(), env, io, ctx, q);
std::cout << "proof 2: " << orelse(fail_tactic(), t).solve(env, io, ctx, q).get_proof() << "\n";
#if !defined(__APPLE__)
#if !defined(__APPLE__) && defined(LEAN_MULTI_THREAD)
check_failure(try_for(loop_tactic(), 100), env, io, ctx, q);
std::cout << "proof 1: " << try_for(t, 10000).solve(env, io, s).get_proof() << "\n";
check_failure(try_for(orelse(try_for(loop_tactic(), 10000),
trace_tactic(std::string("hello world"))),
100),
env, io, ctx, q);
std::atomic<bool> flag1(false);
atomic<bool> flag1(false);
check_failure(try_for(orelse(try_for(loop_tactic(), 10000),
set_tactic(&flag1)),
100),
@ -83,10 +83,8 @@ static void tst1() {
set_tactic(&flag1)),
env, io, ctx, q);
lean_assert(flag1);
#if !defined(LEAN_THREAD_UNSAFE)
std::cout << "Before parallel 3 parallel tactics...\n";
std::cout << "proof 2: " << par(loop_tactic(), par(loop_tactic(), t)).solve(env, io, ctx, q).get_proof() << "\n";
#endif
#endif
std::cout << "Before hello1 and 2...\n";
std::cout << "proof 2: " << orelse(then(repeat_at_most(append(trace_tactic("hello1"), trace_tactic("hello2")), 5), fail_tactic()),

View file

@ -94,7 +94,7 @@ static void tst1() {
display(orelse(lazy_list<int>(), take(10, seq(100))));
display(orelse(take(0, seq(1)), take(10, seq(100))));
display(orelse(filter(take(100, seq(1)), [](int i) { return i < 0; }), take(10, seq(1000))));
#if !defined(__APPLE__) && !defined(LEAN_THREAD_UNSAFE)
#if !defined(__APPLE__) && defined(LEAN_MULTI_THREAD)
display(timeout(append(append(take(10, seq(1)), loop()), seq(100)), 5));
display(take(10, par(seq(1), loop())));
#endif

View file

@ -5,18 +5,17 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <cstdlib>
#include <thread>
#include <iostream>
#include <mutex>
#include <vector>
#include <atomic>
#include "util/thread.h"
#include "util/debug.h"
#include "util/shared_mutex.h"
#include "util/interrupt.h"
using namespace lean;
#if !defined(__APPLE__) && defined(LEAN_MULTI_THREAD)
void foo() {
static thread_local std::vector<int> v(1024);
static LEAN_THREAD_LOCAL std::vector<int> v(1024);
if (v.size() != 1024) {
std::cerr << "Error\n";
exit(1);
@ -26,7 +25,7 @@ void foo() {
static void tst1() {
unsigned n = 5;
for (unsigned i = 0; i < n; i++) {
std::thread t([](){ foo(); });
thread t([](){ foo(); });
t.join();
}
}
@ -34,18 +33,18 @@ static void tst1() {
static void tst2() {
unsigned N = 10;
unsigned n = 1;
lean::shared_mutex mut;
std::vector<std::thread> threads;
shared_mutex mut;
std::vector<thread> threads;
for (unsigned i = 0; i < N; i++) {
threads.emplace_back([&]() {
unsigned sum = 0;
{
lean::shared_lock lock(mut);
shared_lock lock(mut);
for (unsigned i = 0; i < 1000000; i++)
sum += n;
}
{
lean::unique_lock lock(mut);
exclusive_lock lock(mut);
std::cout << sum << "\n";
}
});
@ -54,19 +53,18 @@ static void tst2() {
threads[i].join();
}
#if !defined(__APPLE__) && !defined(LEAN_THREAD_UNSAFE)
static void tst3() {
shared_mutex mutex;
std::atomic<bool> t2_started(false);
std::atomic<bool> t2_done(false);
atomic<bool> t2_started(false);
atomic<bool> t2_done(false);
std::chrono::milliseconds small_delay(10);
std::thread t1([&]() {
thread t1([&]() {
while (!t2_started) {
std::this_thread::sleep_for(small_delay);
this_thread::sleep_for(small_delay);
}
while (!mutex.try_lock()) {
std::this_thread::sleep_for(small_delay);
this_thread::sleep_for(small_delay);
}
// test recursive try_lock
lean_verify(mutex.try_lock());
@ -76,11 +74,11 @@ static void tst3() {
mutex.unlock();
});
std::thread t2([&]() {
thread t2([&]() {
{
unique_lock lock(mutex);
exclusive_lock lock(mutex);
t2_started = true;
std::this_thread::sleep_for(small_delay);
this_thread::sleep_for(small_delay);
}
t2_done = true;
});
@ -92,16 +90,16 @@ static void tst3() {
static void tst4() {
shared_mutex mutex;
std::atomic<bool> t2_started(false);
std::atomic<bool> t2_done(false);
atomic<bool> t2_started(false);
atomic<bool> t2_done(false);
std::chrono::milliseconds small_delay(10);
std::thread t1([&]() {
thread t1([&]() {
while (!t2_started) {
std::this_thread::sleep_for(small_delay);
this_thread::sleep_for(small_delay);
}
while (!mutex.try_lock_shared()) {
std::this_thread::sleep_for(small_delay);
this_thread::sleep_for(small_delay);
}
// test recursive try_lock_shared
lean_verify(mutex.try_lock_shared());
@ -111,11 +109,11 @@ static void tst4() {
mutex.unlock_shared();
});
std::thread t2([&]() {
thread t2([&]() {
{
unique_lock lock(mutex);
exclusive_lock lock(mutex);
t2_started = true;
std::this_thread::sleep_for(small_delay);
this_thread::sleep_for(small_delay);
}
t2_done = true;
});
@ -127,30 +125,30 @@ static void tst4() {
static void tst5() {
shared_mutex mutex;
std::atomic<bool> t2_started(false);
std::atomic<bool> t1_done(false);
atomic<bool> t2_started(false);
atomic<bool> t1_done(false);
std::chrono::milliseconds small_delay(10);
std::thread t1([&]() {
thread t1([&]() {
while (!t2_started) {
std::this_thread::sleep_for(small_delay);
this_thread::sleep_for(small_delay);
}
lean_verify(mutex.try_lock_shared()); // t2 is also using a shared lock
std::this_thread::sleep_for(small_delay);
this_thread::sleep_for(small_delay);
lean_verify(mutex.try_lock_shared());
std::this_thread::sleep_for(small_delay);
this_thread::sleep_for(small_delay);
t1_done = true;
mutex.unlock_shared();
std::this_thread::sleep_for(small_delay);
this_thread::sleep_for(small_delay);
mutex.unlock_shared();
});
std::thread t2([&]() {
thread t2([&]() {
{
shared_lock lock(mutex);
t2_started = true;
while (!t1_done) {
std::this_thread::sleep_for(small_delay);
this_thread::sleep_for(small_delay);
}
}
});
@ -162,8 +160,8 @@ static void tst5() {
static void tst6() {
interruptible_thread t1([]() {
try {
// Remark: std::this_thread::sleep_for does not check whether the thread has been interrupted or not.
// std::this_thread::sleep_for(std::chrono::milliseconds(1000000));
// Remark: this_thread::sleep_for does not check whether the thread has been interrupted or not.
// this_thread::sleep_for(std::chrono::milliseconds(1000000));
sleep_for(1000000);
} catch (interrupted &) {
std::cout << "interrupted...\n";
@ -174,6 +172,8 @@ static void tst6() {
t1.join();
}
#else
static void tst1() {}
static void tst2() {}
static void tst3() {}
static void tst4() {}
static void tst5() {}

View file

@ -8,6 +8,7 @@ Author: Leonardo de Moura
#include <string>
#include "util/exception.h"
#include "util/sstream.h"
#include "util/thread.h"
namespace lean {
exception::exception(char const * msg):m_msg(msg) {}
@ -22,7 +23,7 @@ parser_exception::parser_exception(sstream const & msg, unsigned l, unsigned p):
parser_exception::~parser_exception() noexcept {}
char const * parser_exception::what() const noexcept {
try {
static thread_local std::string buffer;
static LEAN_THREAD_LOCAL std::string buffer;
std::ostringstream s;
s << "(line: " << m_line << ", pos: " << m_pos << ") " << m_msg;
buffer = s.str();
@ -34,7 +35,7 @@ char const * parser_exception::what() const noexcept {
}
char const * stack_space_exception::what() const noexcept {
static thread_local std::string buffer;
static LEAN_THREAD_LOCAL std::string buffer;
std::ostringstream s;
s << "deep recursion was detected at '" << m_component_name << "' (potential solution: increase stack space in your system)";
buffer = s.str();

View file

@ -4,11 +4,12 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <chrono>
#include "util/interrupt.h"
#include "util/exception.h"
namespace lean {
static thread_local std::atomic_bool g_interrupt;
static LEAN_THREAD_LOCAL atomic_bool g_interrupt;
void request_interrupt() {
g_interrupt.store(true);
@ -36,19 +37,19 @@ void sleep_for(unsigned ms, unsigned step_ms) {
std::chrono::milliseconds c(step_ms);
std::chrono::milliseconds r(ms % step_ms);
for (unsigned i = 0; i < rounds; i++) {
std::this_thread::sleep_for(c);
this_thread::sleep_for(c);
check_interrupted();
}
std::this_thread::sleep_for(r);
this_thread::sleep_for(r);
check_interrupted();
}
std::atomic_bool * interruptible_thread::get_flag_addr() {
atomic_bool * interruptible_thread::get_flag_addr() {
return &g_interrupt;
}
bool interruptible_thread::interrupted() const {
std::atomic_bool * f = m_flag_addr.load();
atomic_bool * f = m_flag_addr.load();
if (f == nullptr)
return false;
return f->load();
@ -56,12 +57,12 @@ bool interruptible_thread::interrupted() const {
void interruptible_thread::request_interrupt(unsigned try_ms) {
while (true) {
std::atomic_bool * f = m_flag_addr.load();
atomic_bool * f = m_flag_addr.load();
if (f != nullptr) {
f->store(true);
return;
}
std::this_thread::sleep_for(std::chrono::milliseconds(try_ms));
this_thread::sleep_for(std::chrono::milliseconds(try_ms));
check_interrupted();
}
}

View file

@ -5,9 +5,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
#include <atomic>
#include <thread>
#include <utility>
#include "util/thread.h"
#include "util/stackinfo.h"
#include "util/exception.h"
@ -46,7 +45,7 @@ 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 {
std::atomic<std::atomic_bool*> m_flag_addr;
atomic<atomic_bool*> 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
@ -62,9 +61,9 @@ class interruptible_thread {
m_dummy_addr before terminating the execution of the child
thread.
*/
std::atomic_bool m_dummy_addr;
std::thread m_thread;
static std::atomic_bool * get_flag_addr();
atomic_bool m_dummy_addr;
thread m_thread;
static atomic_bool * get_flag_addr();
public:
template<typename Function, typename... Args>
interruptible_thread(Function && fun, Args &&... args):
@ -98,7 +97,7 @@ public:
bool joinable();
};
#ifdef LEAN_THREAD_UNSAFE
#if !defined(LEAN_MULTI_THREAD)
inline void check_threadsafe() {
throw exception("Lean was compiled without support for multi-threading");
}

View file

@ -5,7 +5,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
#include <mutex>
#include "util/thread.h"
namespace lean {
/**
@ -24,19 +24,19 @@ namespace lean {
template<typename T>
class interruptable_ptr {
T * m_ptr;
std::mutex m_mutex;
mutex m_mutex;
public:
interruptable_ptr():m_ptr(nullptr) {}
T * set(T * ptr) {
std::lock_guard<std::mutex> lock(m_mutex);
lock_guard<mutex> lock(m_mutex);
T * old = m_ptr;
m_ptr = ptr;
return old;
}
void set_interrupt(bool flag) {
std::lock_guard<std::mutex> lock(m_mutex);
lock_guard<mutex> lock(m_mutex);
if (m_ptr)
m_ptr->set_interrupt(flag);
}

View file

@ -280,8 +280,8 @@ template<bool compute_intv, bool compute_deps>
interval<T> & interval<T>::sub(interval<T> const & o, interval_deps & deps) {
if (compute_intv) {
using std::swap;
static thread_local T new_l_val;
static thread_local T new_u_val;
static LEAN_THREAD_LOCAL T new_l_val;
static LEAN_THREAD_LOCAL T new_u_val;
xnumeral_kind new_l_kind, new_u_kind;
lean_trace("numerics", tout << "this: " << *this << " o: " << o << "\n";);
round_to_minus_inf();
@ -342,8 +342,8 @@ interval<T> & interval<T>::mul(interval<T> const & o, interval_deps & deps) {
bool c_o = i2.is_lower_open();
bool d_o = i2.is_upper_open();
static thread_local T new_l_val;
static thread_local T new_u_val;
static LEAN_THREAD_LOCAL T new_l_val;
static LEAN_THREAD_LOCAL T new_u_val;
xnumeral_kind new_l_kind, new_u_kind;
if (i1.is_N()) {
@ -422,10 +422,10 @@ interval<T> & interval<T>::mul(interval<T> const & o, interval_deps & deps) {
deps.m_upper_deps = DEP_IN_LOWER1 | DEP_IN_LOWER2 | DEP_IN_UPPER2;
}
} else if (i2.is_M()) {
static thread_local T ad; static thread_local xnumeral_kind ad_k;
static thread_local T bc; static thread_local xnumeral_kind bc_k;
static thread_local T ac; static thread_local xnumeral_kind ac_k;
static thread_local T bd; static thread_local xnumeral_kind bd_k;
static LEAN_THREAD_LOCAL T ad; static LEAN_THREAD_LOCAL xnumeral_kind ad_k;
static LEAN_THREAD_LOCAL T bc; static LEAN_THREAD_LOCAL xnumeral_kind bc_k;
static LEAN_THREAD_LOCAL T ac; static LEAN_THREAD_LOCAL xnumeral_kind ac_k;
static LEAN_THREAD_LOCAL T bd; static LEAN_THREAD_LOCAL xnumeral_kind bd_k;
bool ad_o = a_o || d_o;
bool bc_o = b_o || c_o;
@ -586,8 +586,8 @@ interval<T> & interval<T>::div(interval<T> const & o, interval_deps & deps) {
bool c_o = i2.m_lower_open;
bool d_o = i2.m_upper_open;
static thread_local T new_l_val;
static thread_local T new_u_val;
static LEAN_THREAD_LOCAL T new_l_val;
static LEAN_THREAD_LOCAL T new_u_val;
xnumeral_kind new_l_kind, new_u_kind;
if (i1.is_N()) {
@ -795,7 +795,7 @@ interval<T> & interval<T>::operator-=(T const & o) {
template<typename T>
interval<T> & interval<T>::operator*=(T const & o) {
xnumeral_kind new_l_kind, new_u_kind;
static thread_local T tmp1;
static LEAN_THREAD_LOCAL T tmp1;
if (this->is_zero()) {
return *this;
}
@ -831,7 +831,7 @@ interval<T> & interval<T>::operator*=(T const & o) {
template<typename T>
interval<T> & interval<T>::operator/=(T const & o) {
xnumeral_kind new_l_kind, new_u_kind;
static thread_local T tmp1;
static LEAN_THREAD_LOCAL T tmp1;
if (this->is_zero()) {
return *this;
}
@ -872,8 +872,8 @@ void interval<T>::inv(interval_deps & deps) {
using std::swap;
static thread_local T new_l_val;
static thread_local T new_u_val;
static LEAN_THREAD_LOCAL T new_l_val;
static LEAN_THREAD_LOCAL T new_u_val;
xnumeral_kind new_l_kind, new_u_kind;
if (is_P1()) {
@ -1032,8 +1032,8 @@ void interval<T>::power(unsigned n, interval_deps & deps) {
// we need both bounds to justify upper bound
xnumeral_kind un1_kind = lower_kind();
xnumeral_kind un2_kind = upper_kind();
static thread_local T un1;
static thread_local T un2;
static LEAN_THREAD_LOCAL T un1;
static LEAN_THREAD_LOCAL T un2;
if (compute_intv) {
swap(un1, m_lower);
swap(un2, m_upper);
@ -1106,7 +1106,7 @@ T a_div_x_n(T a, T const & x, unsigned n, bool to_plus_inf) {
numeric_traits<T>::set_rounding(to_plus_inf);
a /= x;
} else {
static thread_local T tmp;
static LEAN_THREAD_LOCAL T tmp;
numeric_traits<T>::set_rounding(!to_plus_inf);
tmp = x;
numeric_traits<T>::power(tmp, n);
@ -1141,7 +1141,7 @@ void interval<T>::display(std::ostream & out) const {
template<typename T> void interval<T>::fmod(interval<T> y) {
T const & yb = numeric_traits<T>::is_neg(m_lower) ? y.m_lower : y.m_upper;
static thread_local T n;
static LEAN_THREAD_LOCAL T n;
numeric_traits<T>::set_rounding(false);
n = m_lower / yb;
numeric_traits<T>::floor(n);
@ -1149,7 +1149,7 @@ template<typename T> void interval<T>::fmod(interval<T> y) {
}
template<typename T> void interval<T>::fmod(T y) {
static thread_local T n;
static LEAN_THREAD_LOCAL T n;
numeric_traits<T>::set_rounding(false);
n = m_lower / y;
numeric_traits<T>::floor(n);
@ -1781,8 +1781,8 @@ void interval<T>::tan(interval_deps & deps) {
template<typename T>
template<bool compute_intv, bool compute_deps>
void interval<T>::csc (interval_deps & deps) {
static thread_local T l;
static thread_local T u;
static LEAN_THREAD_LOCAL T l;
static LEAN_THREAD_LOCAL T u;
if (compute_intv) {
l = m_lower;
u = m_upper;
@ -1975,8 +1975,8 @@ void interval<T>::sec (interval_deps & deps) {
template<typename T>
template<bool compute_intv, bool compute_deps>
void interval<T>::cot (interval_deps & deps) {
static thread_local T l;
static thread_local T u;
static LEAN_THREAD_LOCAL T l;
static LEAN_THREAD_LOCAL T u;
if (compute_intv) {
l = m_lower;
u = m_upper;

View file

@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include "util/thread.h"
#include "util/numerics/mpq.h"
#include "util/numerics/double.h"
#include "util/numerics/float.h"

View file

@ -219,13 +219,19 @@ lazy_list<T> repeat_at_most(T const & v, F && f, unsigned k, char const * cname
\remark \c check_ms is how often the main thread checks whether the child
thread finished.
*/
#if !defined(LEAN_MULTI_THREAD)
template<typename T>
lazy_list<T> timeout(lazy_list<T> const & l, unsigned, unsigned) {
return l;
}
#else
template<typename T>
lazy_list<T> timeout(lazy_list<T> const & l, unsigned ms, unsigned check_ms = g_small_sleep) {
if (check_ms == 0)
check_ms = 1;
return mk_lazy_list<T>([=]() {
typename lazy_list<T>::maybe_pair r;
std::atomic<bool> done(false);
atomic<bool> done(false);
interruptible_thread th([&]() {
try {
r = l.pull();
@ -243,7 +249,7 @@ lazy_list<T> timeout(lazy_list<T> const & l, unsigned ms, unsigned check_ms = g_
if (std::chrono::duration_cast<std::chrono::milliseconds>(curr - start) > d)
break;
check_interrupted();
std::this_thread::sleep_for(small);
this_thread::sleep_for(small);
}
th.request_interrupt();
th.join();
@ -258,13 +264,14 @@ lazy_list<T> timeout(lazy_list<T> const & l, unsigned ms, unsigned check_ms = g_
}
});
}
#endif
/**
\brief Similar to interleave, but the heads are computed in parallel.
Moreover, when pulling results from the lists, if one finishes before the other,
then the other one is interrupted.
*/
#ifdef LEAN_THREAD_UNSAFE
#if !defined(LEAN_MULTI_THREAD)
template<typename T>
lazy_list<T> par(lazy_list<T> const & l1, lazy_list<T> const & l2, unsigned = g_small_sleep) {
return interleave(l1, l2);
@ -275,8 +282,8 @@ lazy_list<T> par(lazy_list<T> const & l1, lazy_list<T> const & l2, unsigned chec
return mk_lazy_list<T>([=]() {
typename lazy_list<T>::maybe_pair r1;
typename lazy_list<T>::maybe_pair r2;
std::atomic<bool> done1(false);
std::atomic<bool> done2(false);
atomic<bool> done1(false);
atomic<bool> done2(false);
interruptible_thread th1([&]() {
try {
r1 = l1.pull();
@ -297,7 +304,7 @@ lazy_list<T> par(lazy_list<T> const & l1, lazy_list<T> const & l2, unsigned chec
std::chrono::milliseconds small(check_ms);
while (!done1 && !done2) {
check_interrupted();
std::this_thread::sleep_for(small);
this_thread::sleep_for(small);
}
th1.request_interrupt();
th2.request_interrupt();

View file

@ -40,7 +40,7 @@ void free(void * ptr) {
}
#else
#include <atomic>
#include "util/thread.h"
#if defined(HAS_MALLOC_USABLE_SIZE)
#include <malloc.h> // NOLINT
@ -96,11 +96,7 @@ inline void free_core(void * ptr) { ::free(static_cast<size_t*>(
namespace lean {
class alloc_info {
#ifdef LEAN_THREAD_UNSAFE
size_t m_size;
#else
std::atomic<size_t> m_size;
#endif
atomic<size_t> m_size;
public:
alloc_info():m_size(0) {}
~alloc_info() {}
@ -119,7 +115,7 @@ public:
};
static alloc_info g_global_memory;
static thread_local thread_alloc_info g_thread_memory;
static LEAN_THREAD_LOCAL thread_alloc_info g_thread_memory;
size_t get_allocated_memory() { return g_global_memory.size(); }
long long get_thread_allocated_memory() { return g_thread_memory.size(); }

View file

@ -9,7 +9,7 @@ Author: Leonardo de Moura
#include <algorithm>
#include <sstream>
#include <string>
#include <atomic>
#include "util/thread.h"
#include "util/name.h"
#include "util/sstream.h"
#include "util/debug.h"
@ -156,11 +156,7 @@ name const & name::anonymous() {
return g_anonymous;
}
#ifdef LEAN_THREAD_UNSAFE
static unsigned g_next_id(0);
#else
static std::atomic<unsigned> g_next_id(0);
#endif
static atomic<unsigned> g_next_id(0);
name name::mk_internal_unique_name() {
unsigned id = g_next_id++;

View file

@ -5,12 +5,13 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Soonho Kong
*/
#include <cmath>
#include "util/thread.h"
#include "util/numerics/numeric_traits.h"
#include "util/numerics/double.h"
namespace lean {
static thread_local mpfr_rnd_t g_rnd;
static LEAN_THREAD_LOCAL mpfr_rnd_t g_rnd;
void set_double_rnd(bool plus_inf) {
g_rnd = plus_inf ? MPFR_RNDU : MPFR_RNDD;
}

View file

@ -21,7 +21,7 @@ void double_floor(double & v);
// Macro to implement transcendental functions using MPFR
#define LEAN_TRANS_DOUBLE_FUNC(f, v, rnd) \
static thread_local mpfp t(53); \
static LEAN_THREAD_LOCAL mpfp t(53); \
t = v; \
t.f(rnd); \
v = t.get_double(rnd);

View file

@ -5,12 +5,13 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Soonho Kong
*/
#include <cmath>
#include "util/thread.h"
#include "util/numerics/numeric_traits.h"
#include "util/numerics/float.h"
namespace lean {
static thread_local mpfr_rnd_t g_rnd;
static LEAN_THREAD_LOCAL mpfr_rnd_t g_rnd;
void set_float_rnd(bool plus_inf) {
g_rnd = plus_inf ? MPFR_RNDU : MPFR_RNDD;
}

View file

@ -21,7 +21,7 @@ void float_floor(float & v);
// Macro to implement transcendental functions using MPFR
#define LEAN_TRANS_FLOAT_FUNC(f, v, rnd) \
static thread_local mpfp t(24); \
static LEAN_THREAD_LOCAL mpfp t(24); \
t = v; \
t.f(rnd); \
v = t.get_float(rnd);

View file

@ -5,6 +5,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <limits.h>
#include "util/thread.h"
#include "util/numerics/mpbq.h"
namespace lean {
@ -15,7 +16,7 @@ bool set(mpbq & a, mpq const & b) {
a.m_k = 0;
return true;
} else {
static thread_local mpz d;
static LEAN_THREAD_LOCAL mpz d;
denominator(d, b);
unsigned shift;
if (d.is_power_of_two(shift)) {
@ -46,7 +47,7 @@ void mpbq::normalize() {
}
int cmp(mpbq const & a, mpbq const & b) {
static thread_local mpz tmp;
static LEAN_THREAD_LOCAL mpz tmp;
if (a.m_k == b.m_k) {
return cmp(a.m_num, b.m_num);
} else if (a.m_k < b.m_k) {
@ -60,7 +61,7 @@ int cmp(mpbq const & a, mpbq const & b) {
}
int cmp(mpbq const & a, mpz const & b) {
static thread_local mpz tmp;
static LEAN_THREAD_LOCAL mpz tmp;
if (a.m_k == 0) {
return cmp(a.m_num, b);
} else {
@ -73,8 +74,8 @@ int cmp(mpbq const & a, mpq const & b) {
if (a.is_integer() && b.is_integer()) {
return -cmp(b, a.m_num);
} else {
static thread_local mpz tmp1;
static thread_local mpz tmp2;
static LEAN_THREAD_LOCAL mpz tmp1;
static LEAN_THREAD_LOCAL mpz tmp2;
// tmp1 <- numerator(a)*denominator(b)
denominator(tmp1, b); tmp1 *= a.m_num;
// tmp2 <- numerator(b)*denominator(a)
@ -92,7 +93,7 @@ mpbq & mpbq::operator+=(mpbq const & a) {
m_num += a.m_num;
} else {
lean_assert(m_k > a.m_k);
static thread_local mpz tmp;
static LEAN_THREAD_LOCAL mpz tmp;
mul2k(tmp, a.m_num, m_k - a.m_k);
m_num += tmp;
}
@ -106,7 +107,7 @@ mpbq & mpbq::add_int(T const & a) {
m_num += a;
} else {
lean_assert(m_k > 0);
static thread_local mpz tmp;
static LEAN_THREAD_LOCAL mpz tmp;
tmp = a;
mul2k(tmp, tmp, m_k);
m_num += tmp;
@ -126,7 +127,7 @@ mpbq & mpbq::operator-=(mpbq const & a) {
m_num -= a.m_num;
} else {
lean_assert(m_k > a.m_k);
static thread_local mpz tmp;
static LEAN_THREAD_LOCAL mpz tmp;
mul2k(tmp, a.m_num, m_k - a.m_k);
m_num -= tmp;
}
@ -140,7 +141,7 @@ mpbq & mpbq::sub_int(T const & a) {
m_num -= a;
} else {
lean_assert(m_k > 0);
static thread_local mpz tmp;
static LEAN_THREAD_LOCAL mpz tmp;
tmp = a;
mul2k(tmp, tmp, m_k);
m_num -= tmp;
@ -302,7 +303,7 @@ bool lt_1div2k(mpbq const & a, unsigned k) {
return false;
} else {
lean_assert(a.m_k > k);
static thread_local mpz tmp;
static LEAN_THREAD_LOCAL mpz tmp;
tmp = 1;
mul2k(tmp, tmp, a.m_k - k);
return a.m_num < tmp;

View file

@ -6,6 +6,7 @@ Author: Leonardo de Moura
*/
#pragma once
#include <algorithm>
#include "util/thread.h"
#include "util/bit_tricks.h"
#include "util/numerics/mpz.h"
#include "util/numerics/mpq.h"
@ -268,7 +269,7 @@ public:
template<>
class numeric_traits<mpbq> {
private:
static thread_local bool rnd;
static LEAN_THREAD_LOCAL bool rnd;
public:
static bool precise() { return true; }
static bool is_zero(mpbq const & v) { return v.is_zero(); }

View file

@ -7,10 +7,11 @@ Author: Soonho Kong
#include <cmath>
#include <cstdio>
#include <string>
#include "util/thread.h"
#include "util/numerics/mpfp.h"
namespace lean {
static thread_local mpfr_rnd_t g_mpfp_rnd = MPFR_RNDN;
static LEAN_THREAD_LOCAL mpfr_rnd_t g_mpfp_rnd = MPFR_RNDN;
mpfp numeric_traits<mpfp>::pi_l;
mpfp numeric_traits<mpfp>::pi_n;

View file

@ -458,21 +458,21 @@ public:
void rround(mpfr_rnd_t rnd = get_mpfp_rnd()) { mpfr_rint_round(m_val, m_val, rnd); }
void rtrunc(mpfr_rnd_t rnd = get_mpfp_rnd()) { mpfr_rint_trunc(m_val, m_val, rnd); }
friend mpfp floor(mpfp const & a) { static thread_local mpfp tmp; tmp = a; tmp.floor(); return tmp; }
friend mpfp ceil (mpfp const & a) { static thread_local mpfp tmp; tmp = a; tmp.ceil(); return tmp; }
friend mpfp round(mpfp const & a) { static thread_local mpfp tmp; tmp = a; tmp.round(); return tmp; }
friend mpfp trunc(mpfp const & a) { static thread_local mpfp tmp; tmp = a; tmp.trunc(); return tmp; }
friend mpfp floor(mpfp const & a) { static LEAN_THREAD_LOCAL mpfp tmp; tmp = a; tmp.floor(); return tmp; }
friend mpfp ceil (mpfp const & a) { static LEAN_THREAD_LOCAL mpfp tmp; tmp = a; tmp.ceil(); return tmp; }
friend mpfp round(mpfp const & a) { static LEAN_THREAD_LOCAL mpfp tmp; tmp = a; tmp.round(); return tmp; }
friend mpfp trunc(mpfp const & a) { static LEAN_THREAD_LOCAL mpfp tmp; tmp = a; tmp.trunc(); return tmp; }
friend mpfp rfloor(mpfp const & a, mpfr_rnd_t rnd = get_mpfp_rnd()) {
static thread_local mpfp tmp; tmp = a; tmp.rfloor(rnd); return tmp;
static LEAN_THREAD_LOCAL mpfp tmp; tmp = a; tmp.rfloor(rnd); return tmp;
}
friend mpfp rceil (mpfp const & a, mpfr_rnd_t rnd = get_mpfp_rnd()) {
static thread_local mpfp tmp; tmp = a; tmp.rceil(rnd); return tmp;
static LEAN_THREAD_LOCAL mpfp tmp; tmp = a; tmp.rceil(rnd); return tmp;
}
friend mpfp rround(mpfp const & a, mpfr_rnd_t rnd = get_mpfp_rnd()) {
static thread_local mpfp tmp; tmp = a; tmp.rround(rnd); return tmp;
static LEAN_THREAD_LOCAL mpfp tmp; tmp = a; tmp.rround(rnd); return tmp;
}
friend mpfp rtrunc(mpfp const & a, mpfr_rnd_t rnd = get_mpfp_rnd()) {
static thread_local mpfp tmp; tmp = a; tmp.rtrunc(rnd); return tmp;
static LEAN_THREAD_LOCAL mpfp tmp; tmp = a; tmp.rtrunc(rnd); return tmp;
}
void power(mpfp const & b, mpfr_rnd_t rnd = get_mpfp_rnd()) { mpfr_pow(m_val, m_val, b.m_val, rnd); }

View file

@ -5,6 +5,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include "util/sstream.h"
#include "util/thread.h"
#include "util/numerics/mpq.h"
#include "util/numerics/mpbq.h"
@ -26,7 +27,7 @@ int cmp(mpq const & a, mpz const & b) {
if (a.is_integer()) {
return mpz_cmp(mpq_numref(a.m_val), mpq::zval(b));
} else {
static thread_local mpz tmp;
static LEAN_THREAD_LOCAL mpz tmp;
mpz_mul(mpq::zval(tmp), mpq_denref(a.m_val), mpq::zval(b));
return mpz_cmp(mpq_numref(a.m_val), mpq::zval(tmp));
}
@ -128,7 +129,7 @@ DECL_UDATA(mpq)
template<int idx>
static mpq const & to_mpq(lua_State * L) {
static thread_local mpq arg;
static LEAN_THREAD_LOCAL mpq arg;
switch (lua_type(L, idx)) {
case LUA_TNUMBER: arg = lua_tonumber(L, idx); return arg;
case LUA_TSTRING: arg = mpq(lua_tostring(L, idx)); return arg;

View file

@ -6,6 +6,7 @@ Author: Leonardo de Moura
*/
#include <memory>
#include "util/sstream.h"
#include "util/thread.h"
#include "util/numerics/mpz.h"
namespace lean {
@ -53,7 +54,7 @@ mpz operator%(mpz const & a, mpz const & b) {
}
bool root(mpz & root, mpz const & a, unsigned k) {
static thread_local mpz rem;
static LEAN_THREAD_LOCAL mpz rem;
mpz_rootrem(root.m_val, rem.m_val, a.m_val, k);
return rem.is_zero();
}
@ -87,7 +88,7 @@ DECL_UDATA(mpz)
template<int idx>
static mpz const & to_mpz(lua_State * L) {
static thread_local mpz arg;
static LEAN_THREAD_LOCAL mpz arg;
switch (lua_type(L, idx)) {
case LUA_TNUMBER: arg = static_cast<long>(lua_tointeger(L, idx)); return arg;
case LUA_TSTRING: arg = mpz(lua_tostring(L, idx)); return arg;

View file

@ -5,7 +5,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <vector>
#include <mutex>
#include "util/thread.h"
#include "util/int64.h"
#include "util/debug.h"
#include "util/exception.h"
@ -91,7 +91,7 @@ public:
};
static prime_generator g_prime_generator;
static std::mutex g_prime_generator_mutex;
static mutex g_prime_generator_mutex;
prime_iterator::prime_iterator():
@ -102,7 +102,7 @@ uint64 prime_iterator::next() {
unsigned idx = m_idx;
m_idx++;
{
std::lock_guard<std::mutex> guard(g_prime_generator_mutex);
lock_guard<mutex> guard(g_prime_generator_mutex);
return g_prime_generator(idx);
}
}

View file

@ -7,28 +7,18 @@ Author: Leonardo de Moura
#pragma once
// Goodies for reference counting
#ifdef LEAN_THREAD_UNSAFE
#define MK_LEAN_RC() \
private: \
unsigned m_rc; \
public: \
unsigned get_rc() const { return m_rc; } \
void inc_ref() { m_rc++; } \
bool dec_ref_core() { lean_assert(get_rc() > 0); m_rc--; return m_rc == 0; } \
void dec_ref() { if (dec_ref_core()) dealloc(); }
#else
#include <atomic>
#define MK_LEAN_RC() \
private: \
std::atomic<unsigned> m_rc; \
public: \
unsigned get_rc() const { return std::atomic_load(&m_rc); } \
void inc_ref() { std::atomic_fetch_add_explicit(&m_rc, 1u, std::memory_order_relaxed); } \
bool dec_ref_core() { lean_assert(get_rc() > 0); return std::atomic_fetch_sub_explicit(&m_rc, 1u, std::memory_order_relaxed) == 1u; } \
void dec_ref() { if (dec_ref_core()) dealloc(); }
#endif
#include "util/thread.h"
#include "util/debug.h"
#define MK_LEAN_RC() \
private: \
atomic<unsigned> m_rc; \
public: \
unsigned get_rc() const { return atomic_load(&m_rc); } \
void inc_ref() { atomic_fetch_add_explicit(&m_rc, 1u, memory_order_relaxed); } \
bool dec_ref_core() { lean_assert(get_rc() > 0); return atomic_fetch_sub_explicit(&m_rc, 1u, memory_order_relaxed) == 1u; } \
void dec_ref() { if (dec_ref_core()) dealloc(); }
#define LEAN_COPY_REF(T, Arg) \
if (Arg.m_ptr) \
Arg.m_ptr->inc_ref(); \

View file

@ -5,12 +5,10 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <iostream>
#include <mutex>
#include <thread>
#include <chrono>
#include <string>
#include <vector>
#include <condition_variable>
#include "util/thread.h"
#include "util/lua.h"
#include "util/debug.h"
#include "util/exception.h"
@ -48,7 +46,7 @@ static char g_weak_ptr_key; // key for Lua registry (used at get_weak_ptr and sa
struct script_state::imp {
lua_State * m_state;
std::mutex m_mutex;
mutex m_mutex;
static std::weak_ptr<imp> * get_weak_ptr(lua_State * L) {
lua_pushlightuserdata(L, static_cast<void *>(&g_weak_ptr_key));
@ -110,12 +108,12 @@ struct script_state::imp {
}
void dofile(char const * fname) {
std::lock_guard<std::mutex> lock(m_mutex);
lock_guard<mutex> lock(m_mutex);
::lean::dofile(m_state, fname);
}
void dostring(char const * str) {
std::lock_guard<std::mutex> lock(m_mutex);
lock_guard<mutex> lock(m_mutex);
::lean::dostring(m_state, str);
}
};
@ -146,7 +144,7 @@ void script_state::dostring(char const * str) {
m_ptr->dostring(str);
}
std::mutex & script_state::get_mutex() {
mutex & script_state::get_mutex() {
return m_ptr->m_mutex;
}
@ -308,7 +306,7 @@ static void open_state(lua_State * L) {
#define SMALL_DELAY 10 // in ms
std::chrono::milliseconds g_small_delay(SMALL_DELAY);
#if !defined(LEAN_THREAD_UNSAFE)
#if defined(LEAN_MULTI_THREAD)
/**
\brief Channel for communicating with thread objects in the Lua API
*/
@ -318,8 +316,8 @@ class data_channel {
// another.
script_state m_channel;
int m_ini;
std::mutex m_mutex;
std::condition_variable m_cv;
mutex m_mutex;
condition_variable m_cv;
public:
data_channel() {
lua_State * channel = m_channel.m_ptr->m_state;
@ -335,7 +333,7 @@ public:
// on m_channel.
if (last < first)
return;
std::lock_guard<std::mutex> lock(m_mutex);
lock_guard<mutex> lock(m_mutex);
lua_State * channel = m_channel.m_ptr->m_state;
bool was_empty = lua_gettop(channel) == m_ini;
copy_values(src, first, last, channel);
@ -348,7 +346,7 @@ public:
the execution of \c tgt if the channel is empty.
*/
int read(lua_State * tgt, int i) {
std::unique_lock<std::mutex> lock(m_mutex);
unique_lock<mutex> lock(m_mutex);
lua_State * channel = m_channel.m_ptr->m_state;
if (i > 0) {
// i is the position of the timeout argument
@ -383,10 +381,10 @@ public:
*/
class data_channel_ref {
std::unique_ptr<data_channel> m_channel;
std::mutex m_mutex;
mutex m_mutex;
public:
data_channel & get() {
std::lock_guard<std::mutex> lock(m_mutex);
lock_guard<mutex> lock(m_mutex);
if (!m_channel)
m_channel.reset(new data_channel());
lean_assert(m_channel);
@ -410,8 +408,8 @@ class leanlua_thread {
script_state m_state;
int m_sz_before;
std::unique_ptr<exception> m_exception;
std::atomic<data_channel_ref *> m_in_channel_addr;
std::atomic<data_channel_ref *> m_out_channel_addr;
atomic<data_channel_ref *> m_in_channel_addr;
atomic<data_channel_ref *> m_out_channel_addr;
interruptible_thread m_thread;
public:
leanlua_thread(script_state const & st, int sz_before, int num_args):
@ -460,7 +458,7 @@ public:
void write(lua_State * src, int first, int last) {
while (!m_in_channel_addr) {
check_interrupted();
std::this_thread::sleep_for(g_small_delay);
this_thread::sleep_for(g_small_delay);
}
data_channel & in = m_in_channel_addr.load()->get();
in.write(src, first, last);
@ -469,7 +467,7 @@ public:
int read(lua_State * src) {
if (!m_out_channel_addr) {
check_interrupted();
std::this_thread::sleep_for(g_small_delay);
this_thread::sleep_for(g_small_delay);
}
data_channel & out = m_out_channel_addr.load()->get();
int nargs = lua_gettop(src);
@ -588,7 +586,7 @@ static void open_interrupt(lua_State * L) {
SET_GLOBAL_FUN(check_interrupted, "check_interrupted");
SET_GLOBAL_FUN(sleep, "sleep");
SET_GLOBAL_FUN(yield, "yield");
#if !defined(LEAN_THREAD_UNSAFE)
#if defined(LEAN_MULTI_THREAD)
SET_GLOBAL_FUN(channel_read, "read");
SET_GLOBAL_FUN(channel_write, "write");
#endif
@ -596,7 +594,7 @@ static void open_interrupt(lua_State * L) {
void open_extra(lua_State * L) {
open_state(L);
#if !defined(LEAN_THREAD_UNSAFE)
#if defined(LEAN_MULTI_THREAD)
open_thread(L);
#endif
open_interrupt(L);

View file

@ -6,8 +6,8 @@ Author: Leonardo de Moura
*/
#pragma once
#include <memory>
#include <mutex>
#include <lua.hpp>
#include "util/thread.h"
#include "util/unlock_guard.h"
namespace lean {
@ -20,7 +20,7 @@ public:
private:
std::shared_ptr<imp> m_ptr;
friend script_state to_script_state(lua_State * L);
std::mutex & get_mutex();
mutex & get_mutex();
lua_State * get_state();
friend class data_channel;
public:
@ -50,7 +50,7 @@ public:
*/
template<typename F>
typename std::result_of<F(lua_State * L)>::type apply(F && f) {
std::lock_guard<std::mutex> lock(get_mutex());
lock_guard<mutex> lock(get_mutex());
return f(get_state());
}
@ -72,7 +72,7 @@ public:
template<typename F>
void exec_protected(F && f) {
std::lock_guard<std::mutex> lock(get_mutex());
lock_guard<mutex> lock(get_mutex());
f();
}
};

View file

@ -120,7 +120,7 @@ format const & colon() { return g_colon; }
format const & dot() { return g_dot; }
// Auxiliary flag used to mark whether flatten
// produce a different sexpr
static bool thread_local g_diff_flatten = false;
static bool LEAN_THREAD_LOCAL g_diff_flatten = false;
//
sexpr format::flatten(sexpr const & s) {
lean_assert(is_cons(s));

View file

@ -16,12 +16,12 @@
namespace lean {
shared_mutex::shared_mutex():m_rw_counter(0), m_state(0) {}
shared_mutex::~shared_mutex() {
std::lock_guard<std::mutex> lock(m_mutex);
lock_guard<mutex> lock(m_mutex);
}
void shared_mutex::lock() {
std::unique_lock<std::mutex> lock(m_mutex);
if (m_rw_owner == std::this_thread::get_id()) {
unique_lock<mutex> lock(m_mutex);
if (m_rw_owner == this_thread::get_id()) {
m_rw_counter++;
return; // already has the lock
}
@ -31,20 +31,20 @@ void shared_mutex::lock() {
while (m_state & readers)
m_gate2.wait(lock);
lean_assert(m_rw_counter == 0);
m_rw_owner = std::this_thread::get_id();
m_rw_owner = this_thread::get_id();
m_rw_counter = 1;
}
bool shared_mutex::try_lock() {
std::unique_lock<std::mutex> lock(m_mutex);
if (m_rw_owner == std::this_thread::get_id()) {
unique_lock<mutex> lock(m_mutex);
if (m_rw_owner == this_thread::get_id()) {
m_rw_counter++;
return true; // already has the lock
}
if (m_state == 0) {
m_state = write_entered;
lean_assert(m_rw_counter == 0);
m_rw_owner = std::this_thread::get_id();
m_rw_owner = this_thread::get_id();
m_rw_counter = 1;
return true;
}
@ -52,22 +52,22 @@ bool shared_mutex::try_lock() {
}
void shared_mutex::unlock() {
std::lock_guard<std::mutex> lock(m_mutex);
lean_assert(m_rw_owner == std::this_thread::get_id());
lock_guard<mutex> lock(m_mutex);
lean_assert(m_rw_owner == this_thread::get_id());
lean_assert(m_rw_counter > 0);
m_rw_counter--;
if (m_rw_counter > 0)
return; // keep the lock
m_rw_owner = std::thread::id(); // reset owner
m_rw_owner = thread::id(); // reset owner
lean_assert(m_rw_counter == 0);
lean_assert(m_rw_owner != std::this_thread::get_id());
lean_assert(m_rw_owner != this_thread::get_id());
m_state = 0;
m_gate1.notify_all();
}
void shared_mutex::lock_shared() {
std::unique_lock<std::mutex> lock(m_mutex);
if (m_rw_owner == std::this_thread::get_id()) {
unique_lock<mutex> lock(m_mutex);
if (m_rw_owner == this_thread::get_id()) {
lean_assert(m_rw_counter > 0);
m_rw_counter++;
return; // already has the lock
@ -80,8 +80,8 @@ void shared_mutex::lock_shared() {
}
bool shared_mutex::try_lock_shared() {
std::unique_lock<std::mutex> lock(m_mutex);
if (m_rw_owner == std::this_thread::get_id()) {
unique_lock<mutex> lock(m_mutex);
if (m_rw_owner == this_thread::get_id()) {
lean_assert(m_rw_counter > 0);
m_rw_counter++;
return true; // already has the lock
@ -97,8 +97,8 @@ bool shared_mutex::try_lock_shared() {
}
void shared_mutex::unlock_shared() {
std::lock_guard<std::mutex> lock(m_mutex);
if (m_rw_owner == std::this_thread::get_id()) {
lock_guard<mutex> lock(m_mutex);
if (m_rw_owner == this_thread::get_id()) {
// if we have a rw (aka unshared) lock, then
// the shared lock must be nested.
lean_assert(m_rw_counter > 1);

View file

@ -10,18 +10,16 @@
Hinnant. The proposal is also part of the Boost library which is
licensed under http://www.boost.org/LICENSE_1_0.txt
*/
#include <mutex>
#include <thread>
#include <condition_variable>
#include <climits>
#include "util/thread.h"
namespace lean {
class shared_mutex {
std::mutex m_mutex;
std::thread::id m_rw_owner;
mutex m_mutex;
thread::id m_rw_owner;
unsigned m_rw_counter;
std::condition_variable m_gate1;
std::condition_variable m_gate2;
condition_variable m_gate1;
condition_variable m_gate2;
unsigned m_state;
static constexpr unsigned write_entered = 1u << (sizeof(unsigned)*8 - 1);
@ -52,10 +50,10 @@ public:
~shared_lock() { m_mutex.unlock_shared(); }
};
class unique_lock {
class exclusive_lock {
shared_mutex & m_mutex;
public:
unique_lock(shared_mutex & m):m_mutex(m) { m_mutex.lock(); }
~unique_lock() { m_mutex.unlock(); }
exclusive_lock(shared_mutex & m):m_mutex(m) { m_mutex.lock(); }
~exclusive_lock() { m_mutex.unlock(); }
};
}

View file

@ -13,6 +13,7 @@ Author: Leonardo de Moura
#include "util/pair.h"
#include "util/debug.h"
#include "util/buffer.h"
#include "util/thread.h"
namespace lean {
/**
@ -223,7 +224,7 @@ class splay_tree : public CMP {
}
bool insert_pull(T const & v, bool is_insert) {
static thread_local std::vector<entry> path;
static LEAN_THREAD_LOCAL std::vector<entry> path;
node * n = m_ptr;
bool found = false;
while (true) {
@ -271,7 +272,7 @@ class splay_tree : public CMP {
void pull_max() {
if (!m_ptr) return;
static thread_local std::vector<entry> path;
static LEAN_THREAD_LOCAL std::vector<entry> path;
node * n = m_ptr;
while (true) {
lean_assert(n);

View file

@ -7,8 +7,18 @@ Author: Leonardo de Moura
#include <pthread.h>
#include <memory.h>
#include <iostream>
#include "util/thread.h"
#include "util/exception.h"
#if defined(LEAN_WINDOWS)
// no extra included needed so far
#elif defined(__APPLE__)
#include <sys/resource.h> // NOLINT
#else
#include <sys/time.h> // NOLINT
#include <sys/resource.h> // NOLINT
#endif
#define LEAN_MIN_STACK_SPACE 128*1024 // 128 Kb
namespace lean {
@ -16,12 +26,11 @@ void throw_get_stack_size_failed() {
throw exception("failed to retrieve thread stack size");
}
#ifdef LEAN_WINDOWS
#if defined(LEAN_WINDOWS)
size_t get_stack_size(int ) {
return LEAN_WIN_STACK_SIZE;
}
#elif defined (__APPLE__)
#include <sys/resource.h>
size_t get_stack_size(int main) {
if (main) {
// Retrieve stack size of the main thread.
@ -31,6 +40,7 @@ size_t get_stack_size(int main) {
}
return curr.rlim_max;
} else {
#if defined(LEAN_MULTI_THREAD)
// This branch retrieves the default thread size for pthread threads.
// This is *not* the stack size of the main thread.
pthread_attr_t attr;
@ -41,10 +51,22 @@ size_t get_stack_size(int main) {
throw_get_stack_size_failed();
}
return result;
#else
return 0;
#endif
}
}
#else
size_t get_stack_size(int ) {
size_t get_stack_size(int main) {
if (main) {
// Retrieve stack size of the main thread.
struct rlimit curr;
if (getrlimit(RLIMIT_STACK, &curr) != 0) {
throw_get_stack_size_failed();
}
return curr.rlim_max;
} else {
#if defined(LEAN_MULTI_THREAD)
pthread_attr_t attr;
memset (&attr, 0, sizeof(attr));
if (pthread_getattr_np(pthread_self(), &attr) != 0) {
@ -59,11 +81,15 @@ size_t get_stack_size(int ) {
throw_get_stack_size_failed();
}
return result;
#else
return 0;
#endif
}
}
#endif
static thread_local size_t g_stack_size;
static thread_local size_t g_stack_base;
static LEAN_THREAD_LOCAL size_t g_stack_size;
static LEAN_THREAD_LOCAL size_t g_stack_base;
void save_stack_info(bool main) {
g_stack_size = get_stack_size(main);

View file

@ -5,6 +5,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
#include <cstdlib>
namespace lean {
size_t get_stack_size(bool main);
void save_stack_info(bool main = true);

101
src/util/thread.h Normal file
View file

@ -0,0 +1,101 @@
/*
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
#if defined(LEAN_MULTI_THREAD)
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
#define LEAN_THREAD_LOCAL thread_local
namespace lean {
using std::thread;
using std::mutex;
using std::atomic;
using std::atomic_bool;
using std::atomic_ushort;
using std::condition_variable;
using std::lock_guard;
using std::unique_lock;
using std::atomic_load;
using std::atomic_fetch_add_explicit;
using std::atomic_fetch_sub_explicit;
using std::memory_order_relaxed;
namespace this_thread = std::this_thread;
}
#else
#include <utility>
#include <chrono>
#define LEAN_THREAD_LOCAL
namespace lean {
constexpr int memory_order_relaxed = 0;
template<typename T>
class atomic {
T m_value;
public:
atomic(T const & v = T()):m_value(v) {}
atomic(T && v):m_value(std::forward<T>(v)) {}
atomic(atomic const & v):m_value(v.m_value) {}
atomic(atomic && v):m_value(std::forward<T>(v.m_value)) {}
atomic & operator=(T const & v) { m_value = v; return *this; }
atomic & operator=(T && v) { m_value = std::forward<T>(v); return *this; }
atomic & operator=(atomic const & v) { m_value = v.m_value; return *this; }
atomic & operator=(atomic && v) { m_value = std::forward<T>(v.m_value); return *this; }
operator T() const { return m_value; }
void store(T const & v) { m_value = v; }
T load() const { return m_value; }
atomic & operator|=(T const & v) { m_value |= v; return *this; }
atomic & operator+=(T const & v) { m_value += v; return *this; }
atomic & operator-=(T const & v) { m_value -= v; return *this; }
atomic & operator++() { ++m_value; return *this; }
atomic operator++(int ) { atomic tmp(*this); ++m_value; return tmp; }
atomic & operator--() { --m_value; return *this; }
atomic operator--(int ) { atomic tmp(*this); --m_value; return tmp; }
friend T atomic_load(atomic const * a) { return a->m_value; }
friend T atomic_fetch_add_explicit(atomic * a, T const & v, int ) { T r(a->m_value); a->m_value += v; return r; }
friend T atomic_fetch_sub_explicit(atomic * a, T const & v, int ) { T r(a->m_value); a->m_value -= v; return r; }
};
typedef atomic<unsigned short> atomic_ushort;
typedef atomic<bool> atomic_bool;
class thread {
public:
thread() {}
template<typename Function, typename... Args>
thread(Function && fun, Args &&... args) {
fun(std::forward<Args>(args)...);
}
typedef unsigned id;
bool joinable() const { return true; }
void join() {}
};
class this_thread {
public:
template<typename Rep, typename Period>
static void sleep_for(std::chrono::duration<Rep, Period> const &) {}
static thread::id get_id() { return 0; }
static void yield() {}
};
class mutex {
public:
void lock() {}
void unlock() {}
};
class condition_variable {
public:
template<typename Lock> void wait(Lock const &) {}
void notify_all() {}
void notify_one() {}
};
template<typename T> class lock_guard {
public:
lock_guard(T const &) {}
};
template<typename T> class unique_lock {
public:
unique_lock(T const &) {}
};
}
#endif

View file

@ -18,7 +18,7 @@ namespace lean {
#ifdef LEAN_TRACE
std::ofstream tout(LEAN_TRACE_OUT);
std::mutex trace_mutex;
mutex trace_mutex;
#endif
static std::unique_ptr<std::set<std::string>> g_enabled_trace_tags;

View file

@ -7,13 +7,13 @@ Author: Leonardo de Moura
#pragma once
#ifdef LEAN_TRACE
#include <mutex>
#include "util/thread.h"
#include <fstream>
namespace lean {
extern std::ofstream tout;
extern std::mutex trace_mutex;
extern mutex trace_mutex;
}
#define TRACE_CODE(CODE) { std::lock_guard<std::mutex> _lean_trace_lock_(lean::trace_mutex); CODE }
#define TRACE_CODE(CODE) { lock_guard<mutex> _lean_trace_lock_(lean::trace_mutex); CODE }
#else
#define TRACE_CODE(CODE)
#endif

View file

@ -5,7 +5,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
#include <mutex>
#include "util/thread.h"
namespace lean {
/**
\brief The class \c unlock_guard is a mutex wrapper that provides a
@ -17,7 +17,7 @@ namespace lean {
Example:
<code>
{
std::lock_guard<std::mutex> lock(m);
lock_guard<mutex> lock(m);
...
{
unlock_guard unlock(m);
@ -30,9 +30,9 @@ namespace lean {
\warning The calling thread must own the lock to m_mutex
*/
class unlock_guard {
std::mutex & m_mutex;
mutex & m_mutex;
public:
explicit unlock_guard(std::mutex & m):m_mutex(m) { m_mutex.unlock(); }
explicit unlock_guard(mutex & m):m_mutex(m) { m_mutex.unlock(); }
unlock_guard(unlock_guard const &) = delete;
unlock_guard(unlock_guard &&) = delete;
unlock_guard & operator=(unlock_guard const &) = delete;