feat(util): add primitives for checking the amount of available stack space

Recursive functions that may go very deep should invoke the function check_stack. It throws an exception if the amount of stack space is limited.

The function check_system() is syntax sugar for
    check_interrupted();
    check_stack();

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2013-12-01 12:42:32 -08:00
parent 1ec8f9d536
commit 74dfdd02de
33 changed files with 253 additions and 134 deletions

View file

@ -20,9 +20,10 @@ set(LEAN_EXTRA_CXX_FLAGS "")
# Windows does not support ulimit -s unlimited. So, we reserve a lot of stack space: 100Mb # Windows does not support ulimit -s unlimited. So, we reserve a lot of stack space: 100Mb
if((${CYGWIN} EQUAL "1") OR (${CMAKE_SYSTEM_NAME} MATCHES "Windows")) if((${CYGWIN} EQUAL "1") OR (${CMAKE_SYSTEM_NAME} MATCHES "Windows"))
message(STATUS "Windows detected") message(STATUS "Windows detected")
set(LEAN_EXTRA_LINKER_FLAGS "${LEAN_EXTRA_LINKER_FLAGS} -Wl,--stack,104857600") set(LEAN_WIN_STACK_SIZE "104857600")
set(LEAN_EXTRA_LINKER_FLAGS "${LEAN_EXTRA_LINKER_FLAGS} -Wl,--stack,${LEAN_WIN_STACK_SIZE}")
set(LEAN_EXTRA_CXX_FLAGS "${LEAN_EXTRA_CXX_FLAGS} -D LEAN_DEFAULT_PP_UNICODE=false") set(LEAN_EXTRA_CXX_FLAGS "${LEAN_EXTRA_CXX_FLAGS} -D LEAN_DEFAULT_PP_UNICODE=false")
set(LEAN_EXTRA_CXX_FLAGS "${LEAN_EXTRA_CXX_FLAGS} -D LEAN_WINDOWS") set(LEAN_EXTRA_CXX_FLAGS "${LEAN_EXTRA_CXX_FLAGS} -D LEAN_WINDOWS -D LEAN_WIN_STACK_SIZE=${LEAN_WIN_STACK_SIZE}")
endif() endif()
if("${THREAD_SAFE}" MATCHES "OFF") if("${THREAD_SAFE}" MATCHES "OFF")

View file

@ -179,7 +179,7 @@ class normalizer::imp {
/** \brief Normalize the expression \c a in a context composed of stack \c s and \c k binders. */ /** \brief Normalize the expression \c a in a context composed of stack \c s and \c k binders. */
svalue normalize(expr const & a, value_stack const & s, unsigned k) { svalue normalize(expr const & a, value_stack const & s, unsigned k) {
flet<unsigned> l(m_depth, m_depth+1); flet<unsigned> l(m_depth, m_depth+1);
check_interrupted(); check_system();
if (m_depth > m_max_depth) if (m_depth > m_max_depth)
throw kernel_exception(env(), "normalizer maximum recursion depth exceeded"); throw kernel_exception(env(), "normalizer maximum recursion depth exceeded");
bool shared = false; bool shared = false;

View file

@ -85,7 +85,7 @@ class type_checker::imp {
} }
expr infer_type_core(expr const & e, context const & ctx) { expr infer_type_core(expr const & e, context const & ctx) {
check_interrupted(); check_system();
bool shared = false; bool shared = false;
if (is_shared(e)) { if (is_shared(e)) {
shared = true; shared = true;

View file

@ -144,7 +144,7 @@ class type_inferer::imp {
break; // expensive cases break; // expensive cases
} }
check_interrupted(); check_system();
bool shared = false; bool shared = false;
if (is_shared(e)) { if (is_shared(e)) {
shared = true; shared = true;

View file

@ -8,6 +8,7 @@ Author: Leonardo de Moura
#include <fstream> #include <fstream>
#include <signal.h> #include <signal.h>
#include <getopt.h> #include <getopt.h>
#include "util/stackinfo.h"
#include "util/debug.h" #include "util/debug.h"
#include "util/interrupt.h" #include "util/interrupt.h"
#include "util/script_state.h" #include "util/script_state.h"
@ -68,6 +69,7 @@ static struct option g_long_options[] = {
}; };
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
lean::save_stack_info();
lean::register_modules(); lean::register_modules();
input_kind default_k = input_kind::Lean; // default input_kind default_k = input_kind::Lean; // default
int optind = 1; int optind = 1;

View file

@ -205,6 +205,7 @@ static void tst11() {
} }
int main() { int main() {
save_stack_info();
tst1(); tst1();
tst2(); tst2();
tst3(); tst3();

View file

@ -106,6 +106,7 @@ static void tst3() {
} }
int main() { int main() {
save_stack_info();
tst1(); tst1();
tst2(); tst2();
tst3(); tst3();

View file

@ -103,6 +103,7 @@ static void tst6() {
} }
int main() { int main() {
save_stack_info();
tst1(); tst1();
tst2(); tst2();
tst3(); tst3();

View file

@ -119,6 +119,7 @@ static void tst3() {
} }
int main() { int main() {
save_stack_info();
tst1(); tst1();
tst2(); tst2();
tst3(); tst3();

View file

@ -19,9 +19,6 @@ add_test(replace ${CMAKE_CURRENT_BINARY_DIR}/replace)
add_executable(type_checker type_checker.cpp) add_executable(type_checker type_checker.cpp)
target_link_libraries(type_checker ${EXTRA_LIBS}) target_link_libraries(type_checker ${EXTRA_LIBS})
add_test(type_checker ${CMAKE_CURRENT_BINARY_DIR}/type_checker) add_test(type_checker ${CMAKE_CURRENT_BINARY_DIR}/type_checker)
add_executable(arith arith.cpp)
target_link_libraries(arith ${EXTRA_LIBS})
add_test(arith ${CMAKE_CURRENT_BINARY_DIR}/arith)
add_executable(environment environment.cpp) add_executable(environment environment.cpp)
target_link_libraries(environment ${EXTRA_LIBS}) target_link_libraries(environment ${EXTRA_LIBS})
add_test(environment ${CMAKE_CURRENT_BINARY_DIR}/environment) add_test(environment ${CMAKE_CURRENT_BINARY_DIR}/environment)

View file

@ -1,106 +0,0 @@
/*
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 <thread>
#include "util/test.h"
#include "kernel/environment.h"
#include "kernel/type_checker.h"
#include "kernel/builtin.h"
#include "kernel/normalizer.h"
#include "kernel/abstract.h"
#include "kernel/printer.h"
#include "library/arith/arith.h"
#include "library/all/all.h"
using namespace lean;
static void tst1() {
environment env = mk_toplevel();
expr e = mk_int_value(mpz(10));
lean_assert(is_int_value(e));
lean_assert(infer_type(e, env) == Int);
std::cout << "e: " << e << "\n";
}
static void tst2() {
environment env = mk_toplevel();
expr e = iAdd(iVal(10), iVal(30));
std::cout << e << "\n";
std::cout << normalize(e, env) << "\n";
lean_assert(normalize(e, env) == iVal(40));
std::cout << infer_type(mk_int_add_fn(), env) << "\n";
lean_assert(infer_type(e, env) == Int);
lean_assert(infer_type(mk_app(mk_int_add_fn(), iVal(10)), env) == (Int >> Int));
lean_assert(is_int_value(normalize(e, env)));
expr e2 = Fun("a", Int, iAdd(Const("a"), iAdd(iVal(10), iVal(30))));
std::cout << e2 << " --> " << normalize(e2, env) << "\n";
lean_assert(infer_type(e2, env) == mk_arrow(Int, Int));
lean_assert(normalize(e2, env) == Fun("a", Int, iAdd(Const("a"), iVal(40))));
}
static void tst3() {
environment env = mk_toplevel();
expr e = iMul(iVal(10), iVal(30));
std::cout << e << "\n";
std::cout << normalize(e, env) << "\n";
lean_assert(normalize(e, env) == iVal(300));
std::cout << infer_type(mk_int_mul_fn(), env) << "\n";
lean_assert(infer_type(e, env) == Int);
lean_assert(infer_type(mk_app(mk_int_mul_fn(), iVal(10)), env) == mk_arrow(Int, Int));
lean_assert(is_int_value(normalize(e, env)));
expr e2 = Fun("a", Int, iMul(Const("a"), iMul(iVal(10), iVal(30))));
std::cout << e2 << " --> " << normalize(e2, env) << "\n";
lean_assert(infer_type(e2, env) == (Int >> Int));
lean_assert(normalize(e2, env) == Fun("a", Int, iMul(Const("a"), iVal(300))));
}
static void tst4() {
environment env = mk_toplevel();
expr e = iSub(iVal(10), iVal(30));
std::cout << e << "\n";
std::cout << normalize(e, env) << "\n";
lean_assert(normalize(e, env) == iVal(-20));
std::cout << infer_type(mk_int_sub_fn(), env) << "\n";
lean_assert(infer_type(e, env) == Int);
lean_assert(infer_type(mk_app(mk_int_sub_fn(), iVal(10)), env) == mk_arrow(Int, Int));
lean_assert(is_int_value(normalize(e, env)));
expr e2 = Fun("a", Int, iSub(Const("a"), iSub(iVal(10), iVal(30))));
std::cout << e2 << " --> " << normalize(e2, env) << "\n";
lean_assert(infer_type(e2, env) == (Int >> Int));
lean_assert(normalize(e2, env) == Fun("a", Int, iAdd(Const("a"), iVal(20))));
}
static void tst5() {
environment env = mk_toplevel();
env.add_var(name("a"), Int);
expr e = Eq(iVal(3), iVal(4));
std::cout << e << " --> " << normalize(e, env) << "\n";
lean_assert(normalize(e, env) == False);
lean_assert(normalize(Eq(Const("a"), iVal(3)), env) == Eq(Const("a"), iVal(3)));
}
static void tst6() {
std::cout << "tst6\n";
std::cout << mk_int_add_fn().raw() << "\n";
std::cout << mk_int_add_fn().raw() << "\n";
#ifndef __APPLE__
std::thread t1([](){ std::cout << "t1: " << mk_int_add_fn().raw() << "\n"; });
t1.join();
std::thread t2([](){ std::cout << "t2: " << mk_int_add_fn().raw() << "\n"; });
t2.join();
#endif
std::cout << mk_int_add_fn().raw() << "\n";
}
int main() {
tst1();
tst2();
tst3();
tst4();
tst5();
tst6();
return has_violations() ? 1 : 0;
}

View file

@ -283,6 +283,7 @@ static void tst13() {
} }
int main() { int main() {
save_stack_info();
enable_trace("is_convertible"); enable_trace("is_convertible");
tst1(); tst1();
tst2(); tst2();

View file

@ -594,6 +594,7 @@ static void tst27() {
} }
int main() { int main() {
save_stack_info();
tst1(); tst1();
tst2(); tst2();
tst3(); tst3();

View file

@ -250,6 +250,7 @@ static void tst6() {
} }
int main() { int main() {
save_stack_info();
tst_church_numbers(); tst_church_numbers();
tst1(); tst1();
tst2(); tst2();

View file

@ -52,7 +52,7 @@ static void tst1() {
#ifndef __APPLE__ #ifndef __APPLE__
for (unsigned i = 0; i < 8; i++) { for (unsigned i = 0; i < 8; i++) {
ts.push_back(std::thread([&](){ mk(a); })); ts.push_back(std::thread([&](){ save_stack_info(); mk(a); }));
} }
for (unsigned i = 0; i < 8; i++) { for (unsigned i = 0; i < 8; i++) {
ts[i].join(); ts[i].join();
@ -64,6 +64,7 @@ static void tst1() {
} }
int main() { int main() {
save_stack_info();
tst1(); tst1();
return 0; return 0;
} }

View file

@ -325,7 +325,7 @@ static void tst16() {
} }
int main() { int main() {
tst15(); return 0; save_stack_info();
tst1(); tst1();
tst2(); tst2();
tst3(); tst3();

View file

@ -4,13 +4,17 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura Author: Leonardo de Moura
*/ */
#include <thread>
#include "util/test.h" #include "util/test.h"
#include "kernel/builtin.h" #include "kernel/builtin.h"
#include "kernel/normalizer.h" #include "kernel/normalizer.h"
#include "kernel/type_checker.h"
#include "kernel/abstract.h"
#include "library/arith/arith.h" #include "library/arith/arith.h"
#include "library/all/all.h"
using namespace lean; using namespace lean;
static void tst1() { static void tst0() {
environment env; environment env;
normalizer norm(env); normalizer norm(env);
import_basic(env); import_basic(env);
@ -42,8 +46,98 @@ static void tst1() {
lean_assert_eq(norm(iDiv(iVal(2), iVal(0))), iVal(0)); lean_assert_eq(norm(iDiv(iVal(2), iVal(0))), iVal(0));
} }
int main() { static void tst1() {
tst1(); environment env;
return has_violations() ? 1 : 0; import_all(env);
expr e = mk_int_value(mpz(10));
lean_assert(is_int_value(e));
lean_assert(infer_type(e, env) == Int);
std::cout << "e: " << e << "\n";
} }
static void tst2() {
environment env;
import_all(env);
expr e = iAdd(iVal(10), iVal(30));
std::cout << e << "\n";
std::cout << normalize(e, env) << "\n";
lean_assert(normalize(e, env) == iVal(40));
std::cout << infer_type(mk_int_add_fn(), env) << "\n";
lean_assert(infer_type(e, env) == Int);
lean_assert(infer_type(mk_app(mk_int_add_fn(), iVal(10)), env) == (Int >> Int));
lean_assert(is_int_value(normalize(e, env)));
expr e2 = Fun("a", Int, iAdd(Const("a"), iAdd(iVal(10), iVal(30))));
std::cout << e2 << " --> " << normalize(e2, env) << "\n";
lean_assert(infer_type(e2, env) == mk_arrow(Int, Int));
lean_assert(normalize(e2, env) == Fun("a", Int, iAdd(Const("a"), iVal(40))));
}
static void tst3() {
environment env;
import_all(env);
expr e = iMul(iVal(10), iVal(30));
std::cout << e << "\n";
std::cout << normalize(e, env) << "\n";
lean_assert(normalize(e, env) == iVal(300));
std::cout << infer_type(mk_int_mul_fn(), env) << "\n";
lean_assert(infer_type(e, env) == Int);
lean_assert(infer_type(mk_app(mk_int_mul_fn(), iVal(10)), env) == mk_arrow(Int, Int));
lean_assert(is_int_value(normalize(e, env)));
expr e2 = Fun("a", Int, iMul(Const("a"), iMul(iVal(10), iVal(30))));
std::cout << e2 << " --> " << normalize(e2, env) << "\n";
lean_assert(infer_type(e2, env) == (Int >> Int));
lean_assert(normalize(e2, env) == Fun("a", Int, iMul(Const("a"), iVal(300))));
}
static void tst4() {
environment env;
import_all(env);
expr e = iSub(iVal(10), iVal(30));
std::cout << e << "\n";
std::cout << normalize(e, env) << "\n";
lean_assert(normalize(e, env) == iVal(-20));
std::cout << infer_type(mk_int_sub_fn(), env) << "\n";
lean_assert(infer_type(e, env) == Int);
lean_assert(infer_type(mk_app(mk_int_sub_fn(), iVal(10)), env) == mk_arrow(Int, Int));
lean_assert(is_int_value(normalize(e, env)));
expr e2 = Fun("a", Int, iSub(Const("a"), iSub(iVal(10), iVal(30))));
std::cout << e2 << " --> " << normalize(e2, env) << "\n";
lean_assert(infer_type(e2, env) == (Int >> Int));
lean_assert(normalize(e2, env) == Fun("a", Int, iAdd(Const("a"), iVal(20))));
}
static void tst5() {
environment env;
import_all(env);
env.add_var(name("a"), Int);
expr e = Eq(iVal(3), iVal(4));
std::cout << e << " --> " << normalize(e, env) << "\n";
lean_assert(normalize(e, env) == False);
lean_assert(normalize(Eq(Const("a"), iVal(3)), env) == Eq(Const("a"), iVal(3)));
}
static void tst6() {
std::cout << "tst6\n";
std::cout << mk_int_add_fn().raw() << "\n";
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"; });
t1.join();
std::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";
}
int main() {
save_stack_info();
tst0();
tst1();
tst2();
tst3();
tst4();
tst5();
tst6();
return has_violations() ? 1 : 0;
}

View file

@ -874,6 +874,7 @@ void tst27() {
} }
int main() { int main() {
save_stack_info();
tst1(); tst1();
tst2(); tst2();
tst3(); tst3();

View file

@ -42,6 +42,7 @@ static void tst1() {
} }
int main() { int main() {
save_stack_info();
tst1(); tst1();
return has_violations() ? 1 : 0; return has_violations() ? 1 : 0;
} }

View file

@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Soonho Kong Author: Soonho Kong
*/ */
#include "util/test.h"
#include "util/trace.h" #include "util/trace.h"
#include "kernel/abstract.h" #include "kernel/abstract.h"
#include "kernel/context.h" #include "kernel/context.h"
@ -638,6 +639,7 @@ static void depth_rewriter1_tst() {
} }
int main() { int main() {
save_stack_info();
theorem_rewriter1_tst(); theorem_rewriter1_tst();
theorem_rewriter2_tst(); theorem_rewriter2_tst();
then_rewriter1_tst(); then_rewriter1_tst();

View file

@ -144,6 +144,7 @@ static void tst2() {
} }
int main() { int main() {
save_stack_info();
tst1(); tst1();
tst2(); tst2();
return has_violations() ? 1 : 0; return has_violations() ? 1 : 0;

View file

@ -138,6 +138,7 @@ static void tst4() {
} }
int main() { int main() {
save_stack_info();
tst1(); tst1();
tst2(); tst2();
tst3(); tst3();

View file

@ -64,3 +64,6 @@ add_test(set ${CMAKE_CURRENT_BINARY_DIR}/set)
add_executable(optional optional.cpp) add_executable(optional optional.cpp)
target_link_libraries(optional ${EXTRA_LIBS}) target_link_libraries(optional ${EXTRA_LIBS})
add_test(optional ${CMAKE_CURRENT_BINARY_DIR}/optional) add_test(optional ${CMAKE_CURRENT_BINARY_DIR}/optional)
add_executable(stackinfo stackinfo.cpp)
target_link_libraries(stackinfo ${EXTRA_LIBS})
add_test(stackinfo ${CMAKE_CURRENT_BINARY_DIR}/stackinfo)

View file

@ -169,6 +169,7 @@ static void tst4() {
} }
int main() { int main() {
save_stack_info();
tst1(); tst1();
tst2(); tst2();
tst3(); tst3();

View file

@ -0,0 +1,30 @@
/*
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 <iostream>
#include "util/test.h"
#include "util/stackinfo.h"
using namespace lean;
static char foo(int i) {
#define SZ 1000000
char buffer[SZ];
buffer[i] = i;
std::cout << get_available_stack_size() << "\n";
return buffer[i];
}
static void tst1() {
std::cout << get_available_stack_size() << "\n";
foo(10);
std::cout << get_available_stack_size() << "\n";
}
int main() {
save_stack_info();
tst1();
return has_violations() ? 1 : 0;
}

View file

@ -181,6 +181,7 @@ static void tst6() {}
#endif #endif
int main() { int main() {
save_stack_info();
tst1(); tst1();
tst2(); tst2();
tst3(); tst3();

View file

@ -2,6 +2,6 @@ add_library(util trace.cpp debug.cpp name.cpp name_set.cpp
exception.cpp interrupt.cpp hash.cpp escaped.cpp bit_tricks.cpp exception.cpp interrupt.cpp hash.cpp escaped.cpp bit_tricks.cpp
safe_arith.cpp ascii.cpp memory.cpp shared_mutex.cpp realpath.cpp safe_arith.cpp ascii.cpp memory.cpp shared_mutex.cpp realpath.cpp
script_state.cpp script_exception.cpp splay_map.cpp lua.cpp script_state.cpp script_exception.cpp splay_map.cpp lua.cpp
luaref.cpp) luaref.cpp stackinfo.cpp)
target_link_libraries(util ${LEAN_LIBS}) target_link_libraries(util ${LEAN_LIBS})

View file

@ -50,11 +50,13 @@ public:
virtual exception * clone() const { return new interrupted(); } virtual exception * clone() const { return new interrupted(); }
virtual void rethrow() const { throw *this; } virtual void rethrow() const { throw *this; }
}; };
/** \brief Throw interrupted exception iff flag is true. */ class stack_space_exception : public exception {
inline void check_interrupted(bool flag) { public:
if (flag) stack_space_exception() {}
throw interrupted(); virtual char const * what() const noexcept { return "deep recursion was detected (potential solution: increase stack space in your system)"; }
} virtual exception * clone() const { return new stack_space_exception(); }
virtual void rethrow() const { throw *this; }
};
int push_exception(lua_State * L, exception const & e); int push_exception(lua_State * L, exception const & e);
exception const & to_exception(lua_State * L, int i); exception const & to_exception(lua_State * L, int i);
bool is_exception(lua_State * L, int i); bool is_exception(lua_State * L, int i);

View file

@ -8,6 +8,7 @@ Author: Leonardo de Moura
#include <atomic> #include <atomic>
#include <thread> #include <thread>
#include <utility> #include <utility>
#include "util/stackinfo.h"
namespace lean { namespace lean {
/** /**
@ -29,6 +30,8 @@ bool interrupt_requested();
*/ */
void check_interrupted(); void check_interrupted();
inline void check_system() { check_stack(); check_interrupted(); }
constexpr unsigned g_small_sleep = 50; constexpr unsigned g_small_sleep = 50;
/** /**
@ -68,6 +71,7 @@ public:
m_thread( m_thread(
[&](Function&& fun, Args&&... args) { [&](Function&& fun, Args&&... args) {
m_flag_addr.store(get_flag_addr()); m_flag_addr.store(get_flag_addr());
save_stack_info();
fun(std::forward<Args>(args)...); fun(std::forward<Args>(args)...);
m_flag_addr.store(&m_dummy_addr); // see comment before m_dummy_addr m_flag_addr.store(&m_dummy_addr); // see comment before m_dummy_addr
}, },

View file

@ -65,7 +65,7 @@ lazy_list<T> append(lazy_list<T> const & l1, lazy_list<T> const & l2) {
return mk_lazy_list<T>([=]() { return mk_lazy_list<T>([=]() {
auto p = l1.pull(); auto p = l1.pull();
if (!p) { if (!p) {
check_interrupted(); check_system();
return l2.pull(); return l2.pull();
} else { } else {
return some(mk_pair(p->first, append(p->second, l2))); return some(mk_pair(p->first, append(p->second, l2)));
@ -81,7 +81,7 @@ lazy_list<T> orelse(lazy_list<T> const & l1, lazy_list<T> const & l2) {
return mk_lazy_list<T>([=]() { return mk_lazy_list<T>([=]() {
auto p = l1.pull(); auto p = l1.pull();
if (!p) { if (!p) {
check_interrupted(); check_system();
return l2.pull(); return l2.pull();
} else { } else {
return p; return p;
@ -98,7 +98,7 @@ lazy_list<T> interleave(lazy_list<T> const & l1, lazy_list<T> const & l2) {
return mk_lazy_list<T>([=]() { return mk_lazy_list<T>([=]() {
auto p = l1.pull(); auto p = l1.pull();
if (!p) { if (!p) {
check_interrupted(); check_system();
return l2.pull(); return l2.pull();
} else { } else {
return some(mk_pair(p->first, interleave(l2, p->second))); return some(mk_pair(p->first, interleave(l2, p->second)));
@ -125,7 +125,7 @@ lazy_list<T> map(lazy_list<T> const & l, F && f) {
\remark Lazy lists may be infinite, and none of them may satisfy \c pred. \remark Lazy lists may be infinite, and none of them may satisfy \c pred.
\remark \c check_interrupted() is invoked whenever \c pred returns false. \remark \c check_system() is invoked whenever \c pred returns false.
*/ */
template<typename T, typename P> template<typename T, typename P>
lazy_list<T> filter(lazy_list<T> const & l, P && pred) { lazy_list<T> filter(lazy_list<T> const & l, P && pred) {
@ -136,7 +136,7 @@ lazy_list<T> filter(lazy_list<T> const & l, P && pred) {
} else if (pred(p->first)) { } else if (pred(p->first)) {
return p; return p;
} else { } else {
check_interrupted(); check_system();
return filter(p->second, pred).pull(); return filter(p->second, pred).pull();
} }
}); });
@ -155,7 +155,7 @@ lazy_list<T> map_append_aux(lazy_list<T> const & h, lazy_list<T> const & l, F &&
check_interrupted(); check_interrupted();
auto p2 = l.pull(); auto p2 = l.pull();
if (p2) { if (p2) {
check_interrupted(); check_system();
return map_append_aux(f(p2->first), p2->second, f).pull(); return map_append_aux(f(p2->first), p2->second, f).pull();
} else { } else {
return typename lazy_list<T>::maybe_pair(); return typename lazy_list<T>::maybe_pair();
@ -180,7 +180,7 @@ lazy_list<T> repeat(T const & v, F && f) {
if (!p) { if (!p) {
return some(mk_pair(v, lazy_list<T>())); return some(mk_pair(v, lazy_list<T>()));
} else { } else {
check_interrupted(); check_system();
return append(repeat(p->first, f), return append(repeat(p->first, f),
map_append(p->second, [=](T const & v2) { return repeat(v2, f); })).pull(); map_append(p->second, [=](T const & v2) { return repeat(v2, f); })).pull();
} }
@ -197,7 +197,7 @@ lazy_list<T> repeat_at_most(T const & v, F && f, unsigned k) {
if (!p) { if (!p) {
return some(mk_pair(v, lazy_list<T>())); return some(mk_pair(v, lazy_list<T>()));
} else { } else {
check_interrupted(); check_system();
return append(repeat_at_most(p->first, f, k - 1), return append(repeat_at_most(p->first, f, k - 1),
map_append(p->second, [=](T const & v2) { return repeat_at_most(v2, f, k - 1); })).pull(); map_append(p->second, [=](T const & v2) { return repeat_at_most(v2, f, k - 1); })).pull();
} }

58
src/util/stackinfo.cpp Normal file
View file

@ -0,0 +1,58 @@
/*
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 <pthread.h>
#include <memory.h>
#include <iostream>
#include "util/exception.h"
#define LEAN_MIN_STACK_SPACE 128*1024 // 128 Kb
namespace lean {
#ifdef LEAN_WINDOWS
size_t get_stack_size() {
return LEAN_WIN_STACK_SIZE
}
#else
size_t get_stack_size() {
pthread_attr_t attr;
memset (&attr, 0, sizeof(attr));
pthread_getattr_np (pthread_self(), &attr);
void * ptr;
size_t result;
pthread_attr_getstack (&attr, &ptr, &result);
return result;
}
#endif
static thread_local size_t g_stack_size;
static thread_local size_t g_stack_base;
void save_stack_info() {
g_stack_size = get_stack_size();
char x;
g_stack_base = reinterpret_cast<size_t>(&x);
}
size_t get_used_stack_size() {
char y;
size_t curr_stack = reinterpret_cast<size_t>(&y);
return g_stack_base - curr_stack;
}
size_t get_available_stack_size() {
size_t sz = get_used_stack_size();
if (sz > g_stack_size)
return 0;
else
return g_stack_size - sz;
}
void check_stack() {
if (get_used_stack_size() + LEAN_MIN_STACK_SPACE > g_stack_size)
throw stack_space_exception();
}
}

17
src/util/stackinfo.h Normal file
View file

@ -0,0 +1,17 @@
/*
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
namespace lean {
size_t get_stack_size();
void save_stack_info();
size_t get_used_stack_size();
size_t get_available_stack_size();
/**
\brief Throw an exception if the amount of available stack space is low.
*/
void check_stack();
}

View file

@ -10,3 +10,4 @@ Author: Leonardo de Moura
#define LEAN_DEBUG #define LEAN_DEBUG
#endif #endif
#include "util/debug.h" #include "util/debug.h"
#include "util/stackinfo.h"