refactor(*): task<T>, log_tree, cancellation_token

This commit is contained in:
Gabriel Ebner 2017-02-11 17:37:10 +01:00
parent 6887a99f08
commit 595cbb8fe9
60 changed files with 2080 additions and 2302 deletions

View file

@ -227,7 +227,7 @@
(when lean-server-show-pending-tasks
(let ((tasks (if lean-server-session (lean-server-session-tasks lean-server-session)))
(cur-fn (buffer-file-name)))
(dolist (task tasks)
(dolist (task (plist-get tasks :tasks))
(if (equal (plist-get task :file_name) cur-fn)
(let* ((reg (lean-server-task-region task))
(ov (make-overlay (car reg) (cdr reg))))
@ -267,8 +267,9 @@
(defun lean-server-notify-tasks-changed (sess old-tasks)
(force-mode-line-update)
(when (or (plist-get old-tasks :tasks)
(plist-get (lean-server-session-tasks sess) :tasks))
(when (and (not lean-server-show-pending-tasks)
(or (plist-get old-tasks :tasks)
(plist-get (lean-server-session-tasks sess) :tasks)))
; update task flycheck messages only if the task list is non-empty
(lean-server-notify-messages-changed sess))
(dolist (buf (buffer-list))

View file

@ -6,6 +6,7 @@ Author: Leonardo de Moura
*/
#include <string>
#include <vector>
#include <library/library_task_builder.h>
#include "library/sorry.h"
#include "library/module_mgr.h"
#include "util/timeit.h"
@ -367,7 +368,7 @@ static expr fix_rec_fn_name(expr const & e, name const & c_name, name const & c_
static pair<environment, name>
declare_definition(parser & p, environment const & env, def_cmd_kind kind, buffer<name> const & lp_names,
name const & c_name, expr const & type, optional<expr> const & _val, task_result<expr> const & proof,
name const & c_name, expr const & type, optional<expr> const & _val, task<expr> const & proof,
decl_modifiers const & modifiers, decl_attributes attrs, optional<std::string> const & doc_string,
pos_info const & pos) {
auto env_n = mk_real_name(env, c_name, modifiers.m_is_private, pos);
@ -605,182 +606,107 @@ static expr inline_new_defs(environment const & old_env, environment const & new
});
}
class proof_elaboration_task : public task<expr> {
environment m_decl_env;
options m_opts;
pos_info m_header_pos, m_end_pos;
bool m_use_info_manager;
static expr elaborate_proof(
environment const & decl_env, options const & opts,
pos_info const & header_pos,
list<expr> const & params_list,
expr const & fn, expr const & val0, elaborator::theorem_finalization_info const & finfo,
bool is_rfl_lemma, expr const & final_type,
metavar_context const & mctx, local_context const & lctx,
parser_pos_provider pos_provider, bool use_info_manager, std::string const & file_name) {
scoped_expr_caching disable(false); // FIXME: otherwise sigma.eq fails to elaborate
auto tc = std::make_shared<type_context>(decl_env, opts, mctx, lctx);
scope_trace_env scope2(decl_env, opts, *tc);
scope_pos_info_provider scope3(pos_provider);
auto_reporting_info_manager_scope scope4(file_name, use_info_manager);
std::vector<expr> m_params;
expr m_fn, m_val;
elaborator::theorem_finalization_info m_finfo;
bool m_is_rfl_lemma;
expr m_final_type;
try {
bool recover_froerrors = true;
elaborator elab(decl_env, opts, local_pp_name(fn), mctx, lctx, recover_froerrors);
metavar_context m_mctx;
local_context m_lctx;
parser_pos_provider m_pos_provider;
public:
proof_elaboration_task(environment const & decl_env, options const & opts,
pos_info const & header_pos, pos_info const & end_pos,
buffer<expr> const & params,
expr const & fn, expr const & val, elaborator::theorem_finalization_info const & finfo,
bool is_rfl_lemma, expr const & final_type,
metavar_context const & mctx, local_context const & lctx,
parser_pos_provider const & prov) :
m_decl_env(decl_env), m_opts(opts), m_header_pos(header_pos), m_end_pos(end_pos),
m_use_info_manager(get_global_info_manager() != nullptr),
m_params(params.begin(), params.end()), m_fn(fn), m_val(val), m_finfo(finfo),
m_is_rfl_lemma(is_rfl_lemma), m_final_type(final_type),
m_mctx(mctx), m_lctx(lctx), m_pos_provider(prov) {}
void description(std::ostream & out) const override {
out << "proving " << local_pp_name(m_fn) << " (" << get_module_id() << ")";
}
pos_info get_end_pos() const override { return m_end_pos; }
expr_pair elaborate_proof_core(elaborator & elab) {
expr type = mlocal_type(m_fn);
return elab.elaborate_with_type(m_val, mk_as_is(type));
}
expr_pair elaborate_proof(elaborator & elab) {
expr val, type;
// TODO(Leo): create an aux function for retrieving this info
if (m_opts.get_bool("profiler", false)) {
if (opts.get_bool("profiler", false)) {
// TODO(Leo): cleanup this hack
xtimeit timer(LEAN_PROFILE_THRESHOLD, [&](double duration) {
scope_traces_as_messages traces_as_messages(m_pos_provider.get_file_name(), m_header_pos);
std::ostringstream out;
out << "elaboration time for " << local_pp_name(m_fn) << " "
<< std::fixed << std::setprecision(5) << duration << " secs\n";
tout() << out.str();
});
return elaborate_proof_core(elab);
scope_traces_as_messages traces_as_messages(pos_provider.get_file_name(), header_pos);
std::ostringstream out;
out << "elaboration time for " << local_pp_name(fn) << " "
<< std::fixed << std::setprecision(5) << duration << " secs\n";
tout() << out.str();
});
std::tie(val, type) = elab.elaborate_with_type(val0, mk_as_is(mlocal_type(fn)));
} else {
return elaborate_proof_core(elab);
std::tie(val, type) = elab.elaborate_with_type(val0, mk_as_is(mlocal_type(fn)));
}
if (is_equations_result(val))
val = get_equations_result(val, 0);
buffer<expr> params; for (auto & e : params_list) params.push_back(e);
finalize_theorem_proof(elab, params, val, finfo);
if (is_rfl_lemma && !lean::is_rfl_lemma(final_type, val))
throw exception("not a rfl-lemma, even though marked as rfl");
if (elab.has_errors()) throw std::exception();
return inline_new_defs(decl_env, elab.env(), local_pp_name(fn), val);
} catch (exception & ex) {
/* Remark: we need the catch to be able to produce correct line information */
message_builder error_msg(tc, decl_env, get_global_ios(),
pos_provider.get_file_name(), pos_provider.get_some_pos(),
ERROR);
error_msg.set_exception(ex);
error_msg.report();
throw ex;
}
}
expr execute() override {
scoped_expr_caching disable(false); // FIXME: otherwise sigma.eq fails to elaborate
auto tc = std::make_shared<type_context>(m_decl_env, m_opts, m_mctx, m_lctx);
scope_trace_env scope2(m_decl_env, m_opts, *tc);
scope_pos_info_provider scope3(m_pos_provider);
auto_reporting_info_manager_scope scope4(get_module_id(), m_use_info_manager);
try {
bool recover_from_errors = true;
elaborator elab(m_decl_env, m_opts, local_pp_name(m_fn), m_mctx, m_lctx, recover_from_errors);
expr val, type;
std::tie(val, type) = elaborate_proof(elab);
if (is_equations_result(val))
val = get_equations_result(val, 0);
buffer<expr> params; for (auto & e : m_params) params.push_back(e);
finalize_theorem_proof(elab, params, val, m_finfo);
if (m_is_rfl_lemma && !is_rfl_lemma(m_final_type, val))
throw exception("not a rfl-lemma, even though marked as rfl");
if (elab.has_errors()) throw std::exception();
return inline_new_defs(m_decl_env, elab.env(), local_pp_name(m_fn), val);
} catch (exception & ex) {
/* Remark: we need the catch to be able to produce correct line information */
message_builder error_msg(tc, m_decl_env, get_global_ios(),
m_pos_provider.get_file_name(), m_pos_provider.get_some_pos(),
ERROR);
error_msg.set_exception(ex);
error_msg.report();
throw exception("failed to elaborate theorem");
}
}
};
class example_checking_task : public task<unit> {
environment m_decl_env;
options m_opts;
pos_info m_end_pos;
bool m_use_info_manager;
decl_modifiers m_modifiers;
level_param_names m_univ_params;
std::vector<expr> m_params;
expr m_fn, m_val;
metavar_context m_mctx;
local_context m_lctx;
parser_pos_provider m_pos_provider;
public:
example_checking_task(environment const & decl_env, options const & opts,
pos_info const & end_pos,
static void check_example(environment const & decl_env, options const & opts,
decl_modifiers modifiers,
level_param_names const & univ_params,
buffer<expr> const & params,
expr const & fn, expr const & val,
level_param_names const & univ_params, list<expr> const & params,
expr const & fn, expr const & val0,
metavar_context const & mctx, local_context const & lctx,
parser_pos_provider const & prov) :
m_decl_env(decl_env), m_opts(opts),
m_end_pos(end_pos),
m_use_info_manager(get_global_info_manager() != nullptr),
m_modifiers(modifiers),
m_univ_params(univ_params), m_params(params.begin(), params.end()), m_fn(fn), m_val(val),
m_mctx(mctx), m_lctx(lctx), m_pos_provider(prov) {
}
parser_pos_provider pos_provider, bool use_info_manager, std::string const & file_name) {
scoped_expr_caching disable(false); // FIXME: otherwise sigma.eq fails to elaborate
auto tc = std::make_shared<type_context>(decl_env, opts, mctx, lctx);
scope_trace_env scope2(decl_env, opts, *tc);
scope_pos_info_provider scope3(pos_provider);
auto_reporting_info_manager_scope scope4(file_name, use_info_manager);
module::scope_pos_info scope_pos(pos_provider.get_some_pos());
task_kind get_kind() const override { return task_kind::print; }
name decl_name = "_example";
void description(std::ostream & out) const override {
out << "checking example on line " << m_pos_provider.get_some_pos().first << " (" << get_module_id() << ")";
}
try {
bool recover_froerrors = true;
elaborator elab(decl_env, opts, decl_name, mctx, lctx, recover_froerrors);
pos_info get_end_pos() const override { return m_end_pos; }
unit execute() override {
scoped_expr_caching disable(false); // FIXME: otherwise sigma.eq fails to elaborate
auto tc = std::make_shared<type_context>(m_decl_env, m_opts, m_mctx, m_lctx);
scope_trace_env scope2(m_decl_env, m_opts, *tc);
scope_pos_info_provider scope3(m_pos_provider);
auto_reporting_info_manager_scope scope4(get_module_id(), m_use_info_manager);
module::scope_pos_info scope_pos(m_pos_provider.get_some_pos());
name decl_name = "_example";
try {
bool recover_from_errors = true;
elaborator elab(m_decl_env, m_opts, decl_name, m_mctx, m_lctx, recover_from_errors);
expr val, type;
std::tie(val, type) = elab.elaborate_with_type(m_val, mlocal_type(m_fn));
buffer<expr> params_buf; for (auto & p : m_params) params_buf.push_back(p);
if (m_modifiers.m_is_meta) {
val = fix_rec_fn_macro_args(elab, mlocal_name(m_fn), params_buf, type, val);
}
buffer<name> univ_params_buf; to_buffer(m_univ_params, univ_params_buf);
finalize_definition(elab, params_buf, type, val, univ_params_buf, m_modifiers.m_is_meta);
bool use_conv_opt = true;
bool is_trusted = !m_modifiers.m_is_meta;
auto new_env = elab.env();
auto def = mk_definition(new_env, decl_name, to_list(univ_params_buf), type, val, use_conv_opt, is_trusted);
auto cdef = check(new_env, def);
if (elab.has_errors()) throw std::exception();
new_env = module::add(new_env, cdef);
if (!check_noncomputable(false, new_env, decl_name, def.get_name(), m_modifiers.m_is_noncomputable,
m_pos_provider.get_file_name(), m_pos_provider.get_some_pos())) {
throw std::exception(); // set parser to failed.
}
} catch (exception & ex) {
message_builder error_msg(tc, m_decl_env, get_global_ios(),
m_pos_provider.get_file_name(), m_pos_provider.get_some_pos(),
ERROR);
error_msg.set_exception(ex);
error_msg.report();
throw std::exception(); // this bypasses the default exception reporting
expr val, type;
std::tie(val, type) = elab.elaborate_with_type(val0, mlocal_type(fn));
buffer<expr> params_buf; for (auto & p : params) params_buf.push_back(p);
if (modifiers.m_is_meta) {
val = fix_rec_fn_macro_args(elab, mlocal_name(fn), params_buf, type, val);
}
return {};
buffer<name> univ_params_buf; to_buffer(univ_params, univ_params_buf);
finalize_definition(elab, params_buf, type, val, univ_params_buf, modifiers.m_is_meta);
bool use_conv_opt = true;
bool is_trusted = !modifiers.m_is_meta;
auto new_env = elab.env();
auto def = mk_definition(new_env, decl_name, to_list(univ_params_buf), type, val, use_conv_opt, is_trusted);
auto cdef = check(new_env, def);
if (elab.has_errors()) throw std::exception();
new_env = module::add(new_env, cdef);
if (!check_noncomputable(false, new_env, decl_name, def.get_name(), modifiers.m_is_noncomputable,
pos_provider.get_file_name(), pos_provider.get_some_pos())) {
throw std::exception(); // set parser to failed.
}
} catch (exception & ex) {
message_builder error_msg(tc, decl_env, get_global_ios(),
pos_provider.get_file_name(), pos_provider.get_some_pos(),
ERROR);
error_msg.set_exception(ex);
error_msg.report();
throw;
}
};
}
static bool is_rfl_preexpr(expr const & e) {
return is_constant(e, get_rfl_name());
@ -802,6 +728,11 @@ environment single_definition_cmd_core(parser & p, def_cmd_kind kind, decl_modif
attrs.set_attribute(p.env(), "instance");
std::tie(fn, val) = parse_definition(p, lp_names, params, is_example, is_instance, modifiers.m_is_meta);
auto begin_pos = p.cmd_pos();
auto end_pos = p.pos();
scope_log_tree lt(logtree().mk_child({}, local_pp_name(fn).to_string(),
{logtree().get_location().m_file_name, {begin_pos, end_pos}}));
// skip elaboration of definitions during reparsing
if (p.get_break_at_pos())
return p.env();
@ -828,20 +759,36 @@ environment single_definition_cmd_core(parser & p, def_cmd_kind kind, decl_modif
elaborator::theorem_finalization_info thm_finfo;
finalize_theorem_type(elab, new_params, type, lp_names, thm_finfo);
auto decl_env = elab.env();
auto elab_task = get_global_task_queue()->submit<proof_elaboration_task>(
decl_env, p.get_options(), header_pos, p.pos(),
new_params, new_fn, val, thm_finfo, is_rfl, type,
elab.mctx(), elab.lctx(), p.get_parser_pos_provider(header_pos));
p.require_success(elab_task);
env_n = declare_definition(p, elab.env(), kind, lp_names, c_name, type, opt_val, elab_task, modifiers, attrs,
auto opts = p.get_options();
auto new_params_list = to_list(new_params);
auto mctx = elab.mctx();
auto lctx = elab.lctx();
auto pos_provider = p.get_parser_pos_provider(header_pos);
bool use_info_manager = get_global_info_manager() != nullptr;
std::string file_name = p.get_file_name();
auto proof = add_library_task(task_builder<expr>([=] {
return elaborate_proof(decl_env, opts, header_pos, new_params_list,
new_fn, val ,thm_finfo, is_rfl, type,
mctx, lctx, pos_provider, use_info_manager, file_name);
}));
p.require_success(proof);
env_n = declare_definition(p, elab.env(), kind, lp_names, c_name, type, opt_val, proof, modifiers, attrs,
doc_string, header_pos);
} else if (kind == Example) {
p.require_success(get_global_task_queue()->submit<example_checking_task>(
p.env(), p.get_options(), p.pos(),
modifiers, to_list(lp_names),
new_params, fn, val,
elab.mctx(), elab.lctx(),
p.get_parser_pos_provider(header_pos)));
auto env = p.env();
auto opts = p.get_options();
auto lp_name_list = to_list(lp_names);
auto new_params_list = to_list(new_params);
auto mctx = elab.mctx();
auto lctx = elab.lctx();
auto pos_provider = p.get_parser_pos_provider(header_pos);
bool use_info_manager = get_global_info_manager() != nullptr;
std::string file_name = p.get_file_name();
p.require_success(add_library_task<unit>([=] {
check_example(env, opts, modifiers, lp_name_list, new_params_list, fn, val, mctx, lctx,
pos_provider, use_info_manager, file_name);
return unit();
}));
if (elab.has_errors()) p.set_error();
return p.env();
} else {

View file

@ -69,7 +69,7 @@ public:
typedef rb_map<unsigned, list<info_data>, unsigned_cmp> line_info_data_set;
class info_manager {
class info_manager : public log_entry_cell {
std::string m_file_name;
rb_map<unsigned, line_info_data_set, unsigned_cmp> m_line_data;
@ -105,18 +105,18 @@ public:
};
class auto_reporting_info_manager_scope {
optional<info_manager> m_infom;
std::shared_ptr<info_manager> m_infom;
scoped_info_manager m_infom_scope;
public:
auto_reporting_info_manager_scope(std::string const & file_name, bool enabled) :
m_infom(enabled ? optional<info_manager>(info_manager(file_name)) : optional<info_manager>()),
m_infom(enabled ? std::make_shared<info_manager>(file_name) : nullptr),
m_infom_scope(enabled ? &*m_infom : nullptr) {}
~auto_reporting_info_manager_scope() {
if (m_infom && !m_infom->empty()) {
try {
report_info_manager(*m_infom);
logtree().add(m_infom);
} catch (...) {}
}
}

View file

@ -9,12 +9,12 @@ Author: Sebastian Ullrich
#include "util/utf8.h"
#include "util/lean_path.h"
#include "library/module_mgr.h"
#include "library/versioned_msg_buf.h"
#include "library/attribute_manager.h"
#include "frontends/lean/completion.h"
#include "frontends/lean/interactive.h"
#include "frontends/lean/pp.h"
#include "frontends/lean/tactic_notation.h"
#include "info_manager.h"
namespace lean {
LEAN_THREAD_VALUE(break_at_pos_exception::token_context, g_context, break_at_pos_exception::token_context::none);

View file

@ -8,6 +8,7 @@ Author: Sebastian Ullrich
#include <vector>
#include "library/module_mgr.h"
#include "frontends/lean/parser.h"
#include "frontends/lean/info_manager.h"
namespace lean {
void interactive_report_type(environment const & env, options const & opts, expr const & e, json & j);
void report_completions(environment const & env, options const & opts, pos_info const & pos, bool skip_completions,

View file

@ -113,8 +113,8 @@ json json_of_name(name const & n0) {
return j;
}
void json_message_stream::report(message_bucket_id const &, message const & msg) {
m_out << json_of_message(msg) << std::endl;
void print_json(std::ostream & out, message const & msg) {
out << json_of_message(msg) << std::endl;
}
}

View file

@ -6,7 +6,7 @@ Author: Gabriel Ebner
*/
#ifdef LEAN_JSON
#pragma once
#include "library/message_buffer.h"
#include "library/messages.h"
#include "kernel/environment.h"
#include "util/json.hpp"
@ -24,13 +24,7 @@ void add_source_info(environment const & env, name const & d, json & record);
json serialize_decl(name const & short_name, name const & long_name, environment const & env, options const & o);
json serialize_decl(name const & d, environment const & env, options const & o);
class json_message_stream : public message_buffer {
std::ostream & m_out;
public:
json_message_stream(std::ostream & out) : m_out(out) {}
~json_message_stream() {}
void report(message_bucket_id const &, message const & msg) override;
};
void print_json(std::ostream &, message const & msg);
}
#endif

View file

@ -8,6 +8,7 @@ Author: Leonardo de Moura
#include <string>
#include <limits>
#include <vector>
#include "library/library_task_builder.h"
#include "util/utf8.h"
#include "util/interrupt.h"
#include "util/sstream.h"
@ -167,13 +168,15 @@ parser::parser(environment const & env, io_state const & ios,
m_file_name(file_name),
m_scanner(strm, m_file_name.c_str(), s ? s->m_pos : pos_info(1, 0)),
m_imports_parsed(false),
m_snapshot_vector(sv) {
m_snapshot_vector(sv),
m_cancellation_token(global_cancellation_token()) {
m_next_inst_idx = 1;
m_ignore_noncomputable = false;
if (s) {
m_env = s->m_env;
m_ios.set_options(s->m_options);
m_old_buckets_from_snapshot = s->m_sub_buckets;
s->m_sub_buckets.for_each([] (name const & n) { logtree().reuse(n); });
m_cancellation_token = s->m_cancellation_token;
m_local_level_decls = s->m_lds;
m_local_decls = s->m_eds;
m_level_variables = s->m_lvars;
@ -2240,33 +2243,6 @@ void parser::parse_imports(unsigned & fingerprint, std::vector<module_name> & im
}
}
struct sorry_import_task : public task<unit> {
module_id m_imported_module_id;
task_result<bool> m_has_sorry;
pos_info m_pos;
sorry_import_task(loaded_module const & mod, pos_info const & pos) :
m_imported_module_id(mod.m_module_name), m_has_sorry(mod.m_uses_sorry), m_pos(pos) {}
void description(std::ostream & out) const override {
out << "Checking whether import " << m_imported_module_id << " uses sorry";
}
std::vector<generic_task_result> get_dependencies() override {
return {m_has_sorry};
}
bool is_tiny() const override { return true; }
bool do_priority_inversion() const override { return false; }
unit execute() override {
if (m_has_sorry.get())
report_message(message(get_module_id(), m_pos, WARNING,
(sstream() << "imported file '" << m_imported_module_id << "' uses sorry").str()));
return {};
}
};
void parser::process_imports() {
unsigned fingerprint = 0;
std::vector<module_name> imports;
@ -2282,7 +2258,17 @@ void parser::process_imports() {
module_loader sorry_checking_import_fn =
[&] (std::string const & mod_id, module_name const & import) {
auto mod = m_import_fn(mod_id, import);
get_global_task_queue()->submit<sorry_import_task>(*mod, m_last_cmd_pos);
auto pos = m_last_cmd_pos;
auto mod_name = mod->m_module_name;
auto fn = m_file_name;
add_library_task(map<unit>(mod->m_uses_sorry, [pos, mod_name, fn] (bool uses_sorry) {
if (uses_sorry)
report_message(message(fn, pos, WARNING,
(sstream() << "imported file '" << mod_name << "' uses sorry").str()));
return unit {};
}));
return mod;
};
m_env = import_modules(m_env, m_file_name, imports, sorry_checking_import_fn, import_errors);
@ -2317,54 +2303,28 @@ void parser::get_imports(std::vector<module_name> & imports) {
parse_imports(fingerprint, imports);
}
struct combine_parse_success_task : public task<bool> {
list<generic_task_result> m_required_successes;
combine_parse_success_task(list<generic_task_result> const & required_successes) :
task(), m_required_successes(required_successes) {}
void description(std::ostream & out) const override {
out << "Checking parse success (" << get_module_id() << ")";
}
std::vector<generic_task_result> get_dependencies() override {
std::vector<generic_task_result> deps;
for (auto & d : m_required_successes)
deps.push_back(d);
return deps;
}
bool is_tiny() const override { return true; }
bool do_priority_inversion() const override { return false; }
bool execute() override {
for (auto & d : m_required_successes) get_global_task_queue()->wait(d);
return true;
}
};
task_result<bool> parser::parse_commands() {
task<bool> parser::parse_commands() {
protected_call([&]() {
// We disable hash-consing while parsing to make sure the pos-info are correct.
scoped_expr_caching disable(false);
scope_pos_info_provider scope1(*this);
scope_message_context scope_parser_msgs("_parser", m_old_buckets_from_snapshot);
try {
bool done = false;
// Only parse imports when we are at the beginning.
if (!m_imports_parsed) {
// initial snapshot strictly before actual input
save_snapshot(scope_parser_msgs, {0, 0});
scope_message_context scope_msg_ctx("imports");
save_snapshot({0, 0});
scope_cancellation_token scope_ctok(m_cancellation_token);
scope_log_tree lt("imports");
// TODO(gabriel): separate flag for snapshots/infos?
auto_reporting_info_manager_scope scope_infom(m_file_name, m_snapshot_vector != nullptr);
protected_call([&]() { process_imports(); }, [&]() { sync_command(); });
}
while (!done) {
save_snapshot(scope_parser_msgs);
scoped_task_context scope_task_ctx(get_current_module(), pos());
scope_message_context scope_msg_ctx;
save_snapshot();
scope_cancellation_token scope_ctok(m_cancellation_token);
// scoped_task_context scope_task_ctx(get_current_module(), pos());
scope_log_tree lt({}, {pos(), logtree().get_location().m_range.m_end});
// TODO(gabriel): separate flag for snapshots/infos?
auto_reporting_info_manager_scope scope_infom(m_file_name, m_snapshot_vector != nullptr);
protected_call([&]() {
@ -2400,7 +2360,8 @@ task_result<bool> parser::parse_commands() {
},
[&]() { sync_command(); });
}
scope_message_context scope_msg_ctx("end");
{
scope_log_tree lt;
if (has_open_scopes(m_env)) {
m_found_errors = true;
if (!m_use_exceptions && m_show_errors)
@ -2408,17 +2369,27 @@ task_result<bool> parser::parse_commands() {
else if (m_use_exceptions)
throw_parser_exception("invalid end of module, expecting 'end'", pos());
}
save_snapshot(scope_parser_msgs);
}
save_snapshot();
} catch (interrupt_parser) {
while (has_open_scopes(m_env))
m_env = pop_scope_core(m_env, m_ios);
}
}, [](){});
scope_log_tree lt;
scope_cancellation_token scope_ctok(m_cancellation_token);
if (m_found_errors) {
return mk_pure_task_result(false, "parse success");
return mk_pure_task(false);
} else {
return get_global_task_queue()->submit<combine_parse_success_task>(m_required_successes);
auto req_succ = m_required_successes;
return task_builder<bool>([req_succ] {
for (auto t : req_succ)
taskq().wait_for_success(t);
return true;
}).depends_on(req_succ)
.does_not_require_own_thread()
.build();
}
}
@ -2436,7 +2407,7 @@ bool parser::curr_is_command_like() const {
}
}
void parser::save_snapshot(scope_message_context & smc, pos_info p) {
void parser::save_snapshot(pos_info p) {
#ifdef LEAN_NO_SNAPSHOT
return;
#endif
@ -2444,11 +2415,12 @@ void parser::save_snapshot(scope_message_context & smc, pos_info p) {
return;
if (m_snapshot_vector->empty() || m_snapshot_vector->back()->m_pos != p) {
m_snapshot_vector->push_back(std::make_shared<snapshot>(
m_env, smc.get_sub_buckets(), m_local_level_decls, m_local_decls,
m_env, logtree().get_used_names(), m_local_level_decls, m_local_decls,
m_level_variables, m_variables, m_include_vars,
m_ios.get_options(), m_imports_parsed, m_ignore_noncomputable, m_parser_scope_stack, m_next_inst_idx, p,
m_required_successes));
m_required_successes, m_cancellation_token));
}
m_cancellation_token = mk_cancellation_token(m_cancellation_token);
}
optional<pos_info> parser::get_pos_info(expr const & e) const {
@ -2478,15 +2450,16 @@ message_builder parser::mk_message(message_severity severity) {
}
bool parse_commands(environment & env, io_state & ios, char const * fname) {
st_task_queue tq;
scope_global_task_queue scope(&tq);
// st_task_queue tq;
// scope_global_task_queue scope(&tq);
fs_module_vfs vfs;
vfs.m_modules_to_load_from_source.insert(std::string(fname));
module_mgr mod_mgr(&vfs, &get_global_message_buffer(), env, ios);
log_tree lt;
module_mgr mod_mgr(&vfs, lt.get_root(), env, ios);
auto mod = mod_mgr.get_module(fname);
env = mod->get_produced_env();
return mod->m_result.get().is_ok();
return get(mod->m_result).is_ok();
}
void initialize_parser() {

View file

@ -8,10 +8,11 @@ Author: Leonardo de Moura
#include <string>
#include <utility>
#include <vector>
#include <util/cancellable.h>
#include "util/flet.h"
#include "util/name_map.h"
#include "util/exception.h"
#include "util/task_queue.h"
#include "util/task.h"
#include "kernel/environment.h"
#include "kernel/expr_maps.h"
#include "library/util.h"
@ -66,7 +67,7 @@ class parser : public abstract_parser {
// info support
snapshot_vector * m_snapshot_vector;
name_set m_old_buckets_from_snapshot;
cancellation_token m_cancellation_token;
optional<pos_info> m_break_at_pos;
// auto completing
bool m_complete{false};
@ -85,7 +86,7 @@ class parser : public abstract_parser {
optional<std::string> m_doc_string;
// Tasks that need to be successful (no exception) for parsing to succeed
list<generic_task_result> m_required_successes;
list<gtask> m_required_successes;
void throw_parser_exception(char const * msg, pos_info p);
void throw_nested_exception(throwable const & ex);
@ -108,7 +109,7 @@ class parser : public abstract_parser {
void process_imports();
void parse_command();
task_result<bool> parse_commands();
task<bool> parse_commands();
void process_postponed(buffer<expr> const & args, bool is_left, buffer<notation::action_kind> const & kinds,
buffer<list<expr>> const & nargs, buffer<expr> const & ps, buffer<pair<unsigned, pos_info>> const & scoped_info,
list<notation::action> const & postponed, pos_info const & p, buffer<expr> & new_args);
@ -171,8 +172,8 @@ class parser : public abstract_parser {
void push_local_scope(bool save_options = true);
void pop_local_scope();
void save_snapshot(scope_message_context &, pos_info);
void save_snapshot(scope_message_context & smc) { save_snapshot(smc, m_scanner.get_pos_info()); }
void save_snapshot(pos_info);
void save_snapshot() { save_snapshot(m_scanner.get_pos_info()); }
public:
parser(environment const & env, io_state const & ios,
@ -479,7 +480,7 @@ public:
expr mk_sorry(pos_info const & p);
void require_success(generic_task_result const & t) {
void require_success(gtask const & t) {
m_required_successes = cons(t, m_required_successes);
}
@ -489,7 +490,7 @@ public:
bool profiling() const { return m_profile; }
/** parse all commands in the input stream */
task_result<bool> operator()() { return parse_commands(); }
task<bool> operator()() { return parse_commands(); }
void get_imports(std::vector<module_name> &);

View file

@ -16,6 +16,7 @@ Author: Leonardo de Moura
#include "frontends/lean/local_level_decls.h"
#include "frontends/lean/local_context_adapter.h"
#include "frontends/lean/parser_pos_provider.h"
#include "util/cancellable.h"
namespace lean {
typedef environment local_environment;
@ -107,7 +108,7 @@ public:
scope_stack m_scope_stack;
scope_stack m_quote_stack;
/* Tasks that need to be successful (no exception) for parsing to succeed. */
list<generic_task_result> m_required_successes;
list<gtask> m_required_successes;
context(environment const & env, io_state const & ios):m_env(env), m_ios(ios) {}
};
@ -160,7 +161,7 @@ private:
void process_imports();
void parse_command();
task_result<bool> parse_commands();
task<bool> parse_commands();
void process_postponed(buffer<expr> const & args, bool is_left, buffer<notation::action_kind> const & kinds,
buffer<list<expr>> const & nargs, buffer<expr> const & ps, buffer<pair<unsigned, pos_info>> const & scoped_info,
list<notation::action> const & postponed, pos_info const & p, buffer<expr> & new_args);
@ -489,7 +490,7 @@ public:
bool used_sorry() const { return m_used_sorry; }
void declare_sorry_if_used();
void require_success(generic_task_result const & t) {
void require_success(gtask const & t) {
ensure_exclusive_context();
m_context->m_required_successes = cons(t, m_context->m_required_successes);
}
@ -522,14 +523,15 @@ struct snapshot {
parser_scope_stack m_parser_scope_stack;
unsigned m_next_inst_idx;
pos_info m_pos;
list<generic_task_result> m_required_successes;
list<gtask> m_required_successes;
cancellation_token m_cancellation_token;
snapshot(environment const & env, name_set const & sub_buckets, local_level_decls const & lds,
local_expr_decls const & eds, name_set const & lvars, name_set const & vars,
name_set const & includes, options const & opts, bool imports_parsed, bool noncomputable_theory, parser_scope_stack const & pss,
unsigned next_inst_idx, pos_info const & pos, list<generic_task_result> const & required_successes):
unsigned next_inst_idx, pos_info const & pos, list<gtask> const & required_successes, cancellation_token const & ctok):
m_env(env), m_sub_buckets(sub_buckets), m_lds(lds), m_eds(eds), m_lvars(lvars), m_vars(vars), m_include_vars(includes),
m_options(opts), m_imports_parsed(imports_parsed), m_noncomputable_theory(noncomputable_theory), m_parser_scope_stack(pss), m_next_inst_idx(next_inst_idx), m_pos(pos),
m_required_successes(required_successes) {}
m_required_successes(required_successes), m_cancellation_token(ctok) {}
};
typedef std::vector<std::shared_ptr<snapshot const>> snapshot_vector;

View file

@ -7,6 +7,7 @@ Author: Leonardo de Moura
#include "kernel/declaration.h"
#include "kernel/environment.h"
#include "kernel/for_each_fn.h"
#include "util/task_builder.h"
namespace lean {
int compare(reducibility_hints const & h1, reducibility_hints const & h2) {
@ -59,14 +60,14 @@ level_param_names const & declaration::get_univ_params() const { return m_ptr->m
unsigned declaration::get_num_univ_params() const { return length(get_univ_params()); }
expr const & declaration::get_type() const { return m_ptr->m_type; }
task_result<expr> const & declaration::get_value_task() const {
task<expr> const & declaration::get_value_task() const {
lean_assert(is_theorem());
return m_ptr->m_proof;
}
expr const & declaration::get_value() const {
lean_assert(is_definition());
if (m_ptr->m_proof) {
return m_ptr->m_proof.get();
return get(m_ptr->m_proof);
} else {
return *(m_ptr->m_value);
}
@ -95,11 +96,11 @@ declaration mk_definition(environment const & env, name const & n, level_param_n
unsigned h = get_max_height(env, v);
return mk_definition(n, params, t, v, reducibility_hints::mk_regular(h+1, use_self_opt), trusted);
}
declaration mk_theorem(name const & n, level_param_names const & params, expr const & t, task_result<expr> const & v) {
declaration mk_theorem(name const & n, level_param_names const & params, expr const & t, task<expr> const & v) {
return declaration(new declaration::cell(n, params, t, v));
}
declaration mk_theorem(name const & n, level_param_names const & params, expr const & t, expr const & v) {
return mk_theorem(n, params, t, mk_pure_task_result(v, {}));
return mk_theorem(n, params, t, mk_pure_task(v));
}
declaration mk_axiom(name const & n, level_param_names const & params, expr const & t) {
return declaration(new declaration::cell(n, params, t, true, true));

View file

@ -9,7 +9,7 @@ Author: Leonardo de Moura
#include <string>
#include <limits>
#include "util/rc.h"
#include "util/task_queue.h"
#include "util/task.h"
#include "kernel/expr.h"
namespace lean {
@ -76,7 +76,7 @@ class declaration {
expr m_type;
bool m_theorem;
optional<expr> m_value; // if none, then declaration is actually a postulate
task_result<expr> m_proof;
task<expr> m_proof;
reducibility_hints m_hints;
/* Definitions are trusted by default, and nested macros are expanded when kernel is instantiated with
trust level 0. When this flag is false, then we do not expand nested macros. We say the
@ -92,7 +92,7 @@ class declaration {
reducibility_hints const & h, bool trusted):
m_rc(1), m_name(n), m_params(params), m_type(t), m_theorem(false),
m_value(v), m_hints(h), m_trusted(trusted) {}
cell(name const & n, level_param_names const & params, expr const & t, task_result<expr> const & v):
cell(name const & n, level_param_names const & params, expr const & t, task<expr> const & v):
m_rc(1), m_name(n), m_params(params), m_type(t), m_theorem(true),
m_proof(v), m_hints(reducibility_hints::mk_opaque()), m_trusted(true) {}
};
@ -132,7 +132,7 @@ public:
unsigned get_num_univ_params() const;
expr const & get_type() const;
task_result<expr> const & get_value_task() const;
task<expr> const & get_value_task() const;
expr const & get_value() const;
reducibility_hints const & get_hints() const;
@ -141,7 +141,7 @@ public:
reducibility_hints const & hints, bool trusted);
friend declaration mk_definition(environment const & env, name const & n, level_param_names const & params, expr const & t,
expr const & v, bool use_conv_opt, bool trusted);
friend declaration mk_theorem(name const &, level_param_names const &, expr const &, task_result<expr> const &);
friend declaration mk_theorem(name const &, level_param_names const &, expr const &, task<expr> const &);
friend declaration mk_axiom(name const & n, level_param_names const & params, expr const & t);
friend declaration mk_constant_assumption(name const & n, level_param_names const & params, expr const & t, bool trusted);
};
@ -155,7 +155,7 @@ declaration mk_definition(name const & n, level_param_names const & params, expr
declaration mk_definition(environment const & env, name const & n, level_param_names const & params, expr const & t, expr const & v,
bool use_conv_opt = true, bool trusted = true);
declaration mk_theorem(name const & n, level_param_names const & params, expr const & t, expr const & v);
declaration mk_theorem(name const & n, level_param_names const & params, expr const & t, task_result<expr> const & v);
declaration mk_theorem(name const & n, level_param_names const & params, expr const & t, task<expr> const & v);
declaration mk_axiom(name const & n, level_param_names const & params, expr const & t);
declaration mk_constant_assumption(name const & n, level_param_names const & params, expr const & t, bool trusted = true);

View file

@ -7,6 +7,7 @@ Author: Leonardo de Moura
#include <utility>
#include <vector>
#include <limits>
#include <util/task_builder.h>
#include "util/thread.h"
#include "kernel/environment.h"
#include "kernel/kernel_exception.h"
@ -191,37 +192,20 @@ void environment::for_each_declaration(std::function<void(declaration const & d)
m_declarations.for_each([&](name const &, declaration const & d) { return f(d); });
}
class environment_check_task : public task<bool> {
environment m_env;
public:
environment_check_task(environment const & env) : m_env(env) {}
task<bool> environment::is_correct() const {
std::vector<gtask> deps;
for_each_declaration([&] (declaration const & d) {
if (d.is_theorem())
deps.push_back(d.get_value_task());
});
bool is_tiny() const override { return true; }
bool do_priority_inversion() const override { return false; }
void description(std::ostream & out) const override {
out << "checking environment for incorrect proofs (" << get_module_id() << ")";
}
std::vector<generic_task_result> get_dependencies() override {
std::vector<generic_task_result> deps;
m_env.for_each_declaration([&] (declaration const & d) {
if (d.is_theorem())
deps.push_back(d.get_value_task());
});
return deps;
}
bool execute() override {
m_env.for_each_declaration([&] (declaration const & d) {
auto env = *this;
return task_builder<bool>([env] {
env.for_each_declaration([&] (declaration const & d) {
if (d.is_definition()) d.get_value();
});
return true;
}
};
task_result<bool> environment::is_correct() const {
return get_global_task_queue()->submit<environment_check_task>(*this);
}).depends_on(std::move(deps)).build();
}
}

View file

@ -190,7 +190,7 @@ public:
}
/** \brief Returns a task that returns true iff all proofs are correct. May throw an exception otherwise. */
task_result<bool> is_correct() const;
task<bool> is_correct() const;
};
void initialize_environment();

View file

@ -6,7 +6,7 @@ Author: Leonardo de Moura
*/
#include <utility>
#include <vector>
#include "util/task_queue.h"
#include "util/task.h"
#include "util/interrupt.h"
#include "util/lbool.h"
#include "util/flet.h"
@ -21,6 +21,7 @@ Author: Leonardo de Moura
#include "kernel/kernel_exception.h"
#include "kernel/abstract.h"
#include "kernel/replace_fn.h"
#include "util/task_builder.h"
namespace lean {
static expr * g_dont_care = nullptr;
@ -740,35 +741,6 @@ static void check_definition(environment const & env, declaration const & d, typ
}
}
class proof_checking_task : public task<expr> {
environment m_env;
declaration m_decl;
public:
proof_checking_task(environment const & env, declaration const & d) :
m_env(env), m_decl(d) {
lean_assert(d.is_theorem());
}
virtual bool is_tiny() const override { return true; }
void description(std::ostream & out) const override {
out << "type-checking " << m_decl.get_name();
}
std::vector<generic_task_result> get_dependencies() override {
return { m_decl.get_value_task() };
}
expr execute() override {
scoped_expr_caching disable(false);
bool memoize = true;
bool trusted_only = m_decl.is_trusted();
type_checker checker(m_env, memoize, trusted_only);
check_definition(m_env, m_decl, checker);
return m_decl.get_value();
}
};
certified_declaration check(environment const & env, declaration const & d, bool immediately) {
check_no_mlocal(env, d.get_name(), d.get_type(), true);
check_name(env, d.get_name());
@ -778,8 +750,16 @@ certified_declaration check(environment const & env, declaration const & d, bool
expr sort = checker.check(d.get_type(), d.get_univ_params());
checker.ensure_sort(sort, d.get_type());
if (d.is_definition()) {
if (!immediately && env.trust_lvl() != 0 && d.is_theorem() && get_global_task_queue()) {
auto checked_proof = get_global_task_queue()->submit<proof_checking_task>(env, d);
if (!immediately && env.trust_lvl() != 0 && d.is_theorem()) {
// TODO(gabriel): cancellation
auto checked_proof =
map<expr>(d.get_value_task(),
[d, env, memoize, trusted_only] (expr const & val) -> expr {
scoped_expr_caching disable(false);
type_checker checker(env, memoize, trusted_only);
check_definition(env, d, checker);
return val;
}).build();
return certified_declaration(env.get_id(),
mk_theorem(d.get_name(), d.get_univ_params(), d.get_type(), checked_proof));
}

View file

@ -16,6 +16,7 @@ add_library(library OBJECT deep_copy.cpp expr_lt.cpp io_state.cpp
mpq_macro.cpp replace_visitor_with_tc.cpp
aux_definition.cpp inverse.cpp pattern_attribute.cpp choice.cpp
locals.cpp normalize.cpp discr_tree.cpp
mt_task_queue.cpp st_task_queue.cpp task_helper.cpp
messages.cpp message_buffer.cpp versioned_msg_buf.cpp message_builder.cpp module_mgr.cpp comp_val.cpp
mt_task_queue.cpp st_task_queue.cpp
library_task_builder.cpp
messages.cpp message_builder.cpp module_mgr.cpp comp_val.cpp
documentation.cpp check.cpp arith_instance.cpp parray.cpp)

View file

@ -0,0 +1,33 @@
/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#include "library/library_task_builder.h"
namespace lean {
struct library_scopes_imp : public delegating_task_imp {
io_state m_ios;
log_tree::node m_lt;
library_scopes_imp(std::unique_ptr<gtask_imp> && base, log_tree::node const & lt) :
delegating_task_imp(std::forward<std::unique_ptr<gtask_imp>>(base)),
m_ios(get_global_ios()), m_lt(lt) {}
// TODO(gabriel): set logtree status to cancelled?
void execute(void * result) override {
scope_global_ios scope1(m_ios);
scope_log_tree scope2(m_lt);
delegating_task_imp::execute(result);
}
};
std::unique_ptr<gtask_imp> library_scopes::operator()(std::unique_ptr<gtask_imp> && base) {
return std::unique_ptr<gtask_imp>(new library_scopes_imp(
std::forward<std::unique_ptr<gtask_imp>>(base), m_lt));
}
}

View file

@ -0,0 +1,41 @@
/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#pragma once
#include "util/task_builder.h"
#include "util/log_tree.h"
#include "util/cancellable.h"
#include "library/io_state.h"
namespace lean {
struct library_scopes {
log_tree::node m_lt;
library_scopes(log_tree::node const & n) : m_lt(n) {}
std::unique_ptr<gtask_imp> operator()(std::unique_ptr<gtask_imp> &&);
};
template <class Res>
task<Res> add_library_task(task_builder<Res> && builder, std::string const & description, bool add_producer = true) {
auto lt = logtree().mk_child({}, description, logtree().get_location());
auto task = builder.wrap(library_scopes(lt)).build();
if (add_producer) lt.set_producer(task);
return task;
}
template <class Res>
task<Res> mk_library_task(task_builder<Res> && builder, std::string const & description) {
return add_library_task(std::forward<task_builder<Res>>(builder), description, false);
}
template <class Res>
task<Res> add_library_task(task_builder<Res> && builder, bool add_producer = true) {
return add_library_task(std::forward<task_builder<Res>>(builder), {}, add_producer);
}
}

View file

@ -1,107 +0,0 @@
/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#include <string>
#include <vector>
#include "library/message_buffer.h"
namespace lean {
void message_buffer::start_bucket(message_bucket_id const &) {}
void message_buffer::report(message_bucket_id const &, message const &) {}
void message_buffer::finish_bucket(message_bucket_id const &, name_set const &) {}
bool message_buffer::is_bucket_valid(message_bucket_id const &) { return true; }
void message_buffer::report_info_manager(message_bucket_id const &, info_manager const &) {}
void stream_message_buffer::report(message_bucket_id const &, message const & msg) {
if (msg.get_severity() != INFORMATION) {
m_out << msg.get_file_name() << ":" << msg.get_pos().first << ":" << msg.get_pos().second << ": ";
switch (msg.get_severity()) {
case INFORMATION: break;
case WARNING: m_out << "warning: "; break;
case ERROR: m_out << "error: "; break;
}
if (!msg.get_caption().empty())
m_out << msg.get_caption() << ":\n";
}
m_out << msg.get_text() << "\n";
}
LEAN_THREAD_PTR(message_buffer, g_msg_buf);
scoped_message_buffer::scoped_message_buffer(message_buffer * buf) :
m_old(g_msg_buf) { g_msg_buf = buf; }
scoped_message_buffer::~scoped_message_buffer() { g_msg_buf = m_old; }
message_buffer & get_global_message_buffer() {
if (g_msg_buf) {
return *g_msg_buf;
} else {
throw exception("global message buffer not initialized");
}
}
LEAN_THREAD_PTR(scope_message_context, g_msg_ctx);
message_bucket_id get_global_msg_bucket_id() {
return get_scope_message_context().get_bucket_id();
}
void report_message(message const & msg) {
get_global_message_buffer().report(get_global_msg_bucket_id(), msg);
}
void report_info_manager(info_manager const & infom) {
get_global_message_buffer().report_info_manager(get_global_msg_bucket_id(), infom);
}
scope_message_context & get_scope_message_context() {
if (g_msg_ctx) {
return *g_msg_ctx;
} else {
throw exception("message context not initialized");
}
}
scope_message_context::scope_message_context(message_bucket_id const & bucket) :
scope_message_context(bucket, {}) {}
scope_message_context::scope_message_context(message_bucket_id const & bucket,
name_set const & sub_buckets_to_reuse) :
m_old(g_msg_ctx), m_bucket(bucket), m_sub_buckets(sub_buckets_to_reuse) {
g_msg_ctx = this;
get_global_message_buffer().start_bucket(m_bucket);
DEBUG_CODE(m_sub_buckets.for_each([=] (name const & b) {
lean_assert(b.get_prefix() == m_bucket.m_bucket); });)
}
scope_message_context::scope_message_context() :
scope_message_context(g_msg_ctx->new_sub_bucket()) {}
scope_message_context::scope_message_context(std::string const & sub_name) :
scope_message_context(g_msg_ctx->new_sub_bucket(sub_name)) {}
scope_message_context::scope_message_context(std::string const & sub_name, name_set const & sub_buckets_to_reuse) :
scope_message_context(g_msg_ctx->new_sub_bucket(sub_name), sub_buckets_to_reuse) {}
scope_message_context::~scope_message_context() {
get_global_message_buffer().finish_bucket(m_bucket, m_sub_buckets);
g_msg_ctx = m_old;
}
message_bucket_id scope_message_context::new_sub_bucket() {
unsigned i = m_sub_buckets.size();
name n;
do { n = name(m_bucket.m_bucket, i++); } while (m_sub_buckets.contains(n));
m_sub_buckets.insert(n);
return { n, m_bucket.m_version };
}
message_bucket_id scope_message_context::new_sub_bucket(std::string const & s) {
name n(m_bucket.m_bucket, s.c_str());
if (m_sub_buckets.contains(n)) {
unsigned i = 0;
name n_new;
do { n_new = n.append_after(i++); } while (m_sub_buckets.contains(n_new));
n = n_new;
}
m_sub_buckets.insert(n);
return message_bucket_id { n, m_bucket.m_version };
}
}

View file

@ -1,71 +0,0 @@
/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#pragma once
#include <string>
#include <vector>
#include "util/output_channel.h"
#include "util/exception.h"
#include "util/name_set.h"
#include "library/messages.h"
#include "util/message_definitions.h"
namespace lean {
class info_manager;
class message_buffer {
public:
virtual ~message_buffer() {}
virtual void start_bucket(message_bucket_id const & bucket);
virtual void report(message_bucket_id const & bucket, message const & msg);
virtual void finish_bucket(message_bucket_id const & bucket, name_set const & children);
virtual void report_info_manager(message_bucket_id const & bucket, info_manager const & infom);
virtual bool is_bucket_valid(message_bucket_id const & bucket);
};
using null_message_buffer = message_buffer;
class stream_message_buffer : public message_buffer {
std::ostream & m_out;
public:
stream_message_buffer(std::ostream & out) : m_out(out) {}
void report(message_bucket_id const & bucket, message const & msg) override;
};
message_buffer & get_global_message_buffer();
class scoped_message_buffer {
message_buffer * m_old;
public:
scoped_message_buffer(message_buffer * msg_buf);
~scoped_message_buffer();
};
class scope_message_context {
scope_message_context * m_old;
message_bucket_id m_bucket;
name_set m_sub_buckets;
public:
scope_message_context(message_bucket_id const &);
scope_message_context(message_bucket_id const &, name_set const & sub_buckets_to_reuse);
scope_message_context(std::string const &, name_set const & sub_buckets_to_reuse);
scope_message_context(std::string const &);
scope_message_context();
~scope_message_context();
message_bucket_id get_bucket_id() const { return m_bucket; }
name_set get_sub_buckets() const { return m_sub_buckets; }
message_bucket_id new_sub_bucket();
message_bucket_id new_sub_bucket(std::string const &);
};
message_bucket_id get_global_msg_bucket_id();
scope_message_context & get_scope_message_context();
void report_message(message const &);
void report_info_manager(info_manager const &);
}

View file

@ -7,7 +7,6 @@ Author: Gabriel Ebner
#include <string>
#include "library/message_builder.h"
#include "library/type_context.h"
#include "library/message_buffer.h"
namespace lean {

View file

@ -6,6 +6,7 @@ Author: Gabriel Ebner
*/
#include <string>
#include "library/messages.h"
#include "frontends/lean/info_manager.h"
namespace lean {
@ -13,4 +14,23 @@ message::message(parser_exception const & ex) :
message(ex.get_file_name(), *ex.get_pos(),
ERROR, ex.get_msg()) {}
std::ostream & operator<<(std::ostream & out, message const & msg) {
if (msg.get_severity() != INFORMATION) {
out << msg.get_file_name() << ":" << msg.get_pos().first << ":" << msg.get_pos().second << ": ";
switch (msg.get_severity()) {
case INFORMATION: break;
case WARNING: out << "warning: "; break;
case ERROR: out << "error: "; break;
}
if (!msg.get_caption().empty())
out << msg.get_caption() << ":\n";
}
out << msg.get_text() << "\n";
return out;
}
void report_message(message const & msg) {
logtree().add(std::make_shared<message>(msg));
}
}

View file

@ -6,6 +6,7 @@ Author: Gabriel Ebner
*/
#pragma once
#include <string>
#include "util/log_tree.h"
#include "util/message_definitions.h"
#include "util/parser_exception.h"
@ -13,7 +14,7 @@ namespace lean {
enum message_severity { INFORMATION, WARNING, ERROR };
class message {
class message : public log_entry_cell {
std::string m_file_name;
pos_info m_pos;
message_severity m_severity;
@ -38,4 +39,7 @@ public:
std::string get_text() const { return m_text; }
};
std::ostream & operator<<(std::ostream &, message const &);
void report_message(message const &);
}

View file

@ -29,12 +29,9 @@ Author: Leonardo de Moura
#include "library/kernel_serializer.h"
#include "library/unfold_macros.h"
#include "library/module_mgr.h"
#include "library/library_task_builder.h"
#include "version.h"
#ifndef LEAN_ASYNCH_IMPORT_THEOREM
#define LEAN_ASYNCH_IMPORT_THEOREM false
#endif
namespace lean {
corrupted_file_exception::corrupted_file_exception(std::string const & fname):
exception(sstream() << "failed to import '" << fname << "', file is corrupted, please regenerate the file from sources") {
@ -207,7 +204,7 @@ void write_module(loaded_module const & mod, std::ostream & out) {
unsigned h = hash(r.size(), [&](unsigned i) { return r[i]; });
s2 << g_olean_header << LEAN_VERSION_MAJOR << LEAN_VERSION_MINOR << LEAN_VERSION_PATCH;
s2 << h;
s2 << static_cast<bool>(mod.m_uses_sorry.get());
s2 << static_cast<bool>(get(mod.m_uses_sorry));
// store imported files
s2 << static_cast<unsigned>(mod.m_imports.size());
for (auto m : mod.m_imports)
@ -218,46 +215,17 @@ void write_module(loaded_module const & mod, std::ostream & out) {
s2.write_char(r[i]);
}
struct search_sorry_task : public task<bool> {
std::vector<task_result<expr>> m_exprs;
search_sorry_task(std::vector<task_result<expr>> const & exprs) : m_exprs(exprs) {}
void description(std::ostream & out) const override {
out << "Checking whether the module " << get_module_id() << " contains sorry";
}
std::vector<generic_task_result> get_dependencies() override {
std::vector<generic_task_result> deps;
for (auto & dep : m_exprs) deps.push_back(dep);
return deps;
}
bool is_tiny() const override { return true; }
bool execute() override {
for (auto & e : m_exprs) {
if (has_sorry(e.get())) return true;
}
return false;
}
};
static task_result<bool> has_sorry(modification_list const & mods) {
std::vector<task_result<expr>> introduced_exprs;
static task<bool> has_sorry(modification_list const & mods) {
std::vector<task<expr>> introduced_exprs;
for (auto & mod : mods) mod->get_introduced_exprs(introduced_exprs);
std::vector<task_result<expr>> delayed_exprs;
for (auto & e : introduced_exprs) {
if (auto e_ = e.peek()) {
if (has_sorry(*e_))
return mk_pure_task_result(true, "");
} else {
delayed_exprs.push_back(std::move(e));
}
}
return get_global_task_queue()->mk_lazy_task<search_sorry_task>(std::move(delayed_exprs));
return map<bool>(traverse(introduced_exprs),
[] (std::vector<expr> const & es) {
for (auto & e : es) {
if (has_sorry(e)) return true;
}
return false;
}).build();
}
loaded_module export_module(environment const & env, std::string const & mod_name) {
@ -334,16 +302,16 @@ struct decl_modification : public modification {
return std::make_shared<decl_modification>(std::move(decl), trust_lvl);
}
void get_introduced_exprs(std::vector<task_result<expr>> & es) const override {
es.push_back(mk_pure_task_result(m_decl.get_type(), ""));
void get_introduced_exprs(std::vector<task<expr>> & es) const override {
es.push_back(mk_pure_task(m_decl.get_type()));
if (m_decl.is_theorem()) {
es.push_back(m_decl.get_value_task());
} else if (m_decl.is_definition()) {
es.push_back(mk_pure_task_result(m_decl.get_value(), ""));
es.push_back(mk_pure_task(m_decl.get_value()));
}
}
void get_task_dependencies(std::vector<generic_task_result> & deps) const override {
void get_task_dependencies(buffer<gtask> & deps) const override {
if (m_decl.is_theorem())
deps.push_back(m_decl.get_value_task());
}
@ -368,10 +336,10 @@ struct inductive_modification : public modification {
return std::make_shared<inductive_modification>(read_certified_inductive_decl(d));
}
void get_introduced_exprs(std::vector<task_result<expr>> & es) const override {
es.push_back(mk_pure_task_result(m_decl.get_decl().m_type, ""));
void get_introduced_exprs(std::vector<task<expr>> & es) const override {
es.push_back(mk_pure_task(m_decl.get_decl().m_type));
for (auto & i : m_decl.get_decl().m_intro_rules)
es.push_back(mk_pure_task_result(i, ""));
es.push_back(mk_pure_task(i));
}
};
@ -417,57 +385,24 @@ environment update_module_defs(environment const & env, declaration const & d) {
}
}
struct add_decl_sorry_check : public task<unit> {
declaration m_decl;
pos_info m_pos;
add_decl_sorry_check(declaration const & decl, pos_info const & pos) :
m_decl(decl), m_pos(pos) {}
void description(std::ostream & out) const override {
out << "Checking added declaration " << m_decl.get_name() << " for use of sorry";
}
std::vector<generic_task_result> get_dependencies() override {
if (m_decl.is_theorem()) {
return {m_decl.get_value_task()};
} else {
return {};
}
}
bool is_tiny() const override { return true; }
task_kind get_kind() const override { return task_kind::elab; }
optional<name> should_report_sorry(name const & n) {
if (n.is_anonymous())
return optional<name>();
if (!is_internal_name(n))
return optional<name>(n);
if (!n.is_string())
return optional<name>();
if (strcmp(n.get_string(), "_example") == 0)
return optional<name>(n);
if (strcmp(n.get_string(), "_main") == 0)
return should_report_sorry(n.get_prefix());
if (strncmp(n.get_string(), "_match", 6) == 0) {
// TODO(Leo): we may report the same function multiple times,
// if it contains many nested match-expressions containing sorry.
return should_report_sorry(n.get_prefix());
}
static optional<name> should_report_sorry(name const & n) {
if (n.is_anonymous())
return optional<name>();
if (!is_internal_name(n))
return optional<name>(n);
if (!n.is_string() || n.is_atomic())
return optional<name>();
if (strcmp(n.get_string(), "_example") == 0)
return optional<name>(n);
if (strcmp(n.get_string(), "_main") == 0)
return should_report_sorry(n.get_prefix());
if (strncmp(n.get_string(), "_match", 6) == 0) {
// TODO(Leo): we may report the same function multiple times,
// if it contains many nested match-expressions containing sorry.
return should_report_sorry(n.get_prefix());
}
unit execute() override {
if (has_sorry(m_decl)) {
if (optional<name> n = should_report_sorry(m_decl.get_name())) {
report_message(message(get_module_id(), m_pos, WARNING,
(sstream() << "declaration '" << *n << "' uses sorry").str()));
}
}
return {};
}
};
return optional<name>();
}
environment add(environment const & env, certified_declaration const & d) {
environment new_env = env.add(d);
@ -476,7 +411,17 @@ environment add(environment const & env, certified_declaration const & d) {
new_env = mark_noncomputable(new_env, _d.get_name());
new_env = update_module_defs(new_env, _d);
new_env = add(new_env, std::make_shared<decl_modification>(_d, env.trust_lvl()));
get_global_task_queue()->submit<add_decl_sorry_check>(_d, pos_info {g_curr_line, g_curr_column});
pos_info pos { g_curr_line, g_curr_column };
add_library_task(task_builder<unit>([_d, pos] {
if (has_sorry(_d)) {
if (optional<name> n = should_report_sorry(_d.get_name())) {
auto file_name = "<dummy>"; // TODO(gabriel)
report_message(message(file_name, pos, WARNING,
(sstream() << "declaration '" << *n << "' uses sorry").str()));
}
}
return unit {};
}).depends_on(_d.is_theorem() ? _d.get_value_task() : nullptr));
return add_decl_pos_info(new_env, _d.get_name());
}
@ -552,7 +497,7 @@ static void import_module(environment & env, std::string const & module_file_nam
if (ext0.m_imported.contains(res->m_module_name)) return;
if (ext0.m_imported.empty() && res->m_env) {
env = res->m_env.get();
env = get(res->m_env);
} else {
for (auto &dep : res->m_imports) {
import_module(env, res->m_module_name, dep, mod_ldr, import_errors);
@ -600,43 +545,18 @@ static environment mk_preimported_module(environment const & initial_env, loaded
return env;
}
struct preimport_task : public task<environment> {
std::weak_ptr<loaded_module> m_lm;
environment m_env0;
std::function<module_loader()> m_mk_mod_ldr;
public:
preimport_task(std::weak_ptr<loaded_module> const & lm,
environment const & env0, std::function<module_loader()> mk_mod_ldr) :
m_lm(lm), m_env0(env0), m_mk_mod_ldr(mk_mod_ldr) {}
void description(std::ostream & out) const override {
out << "precompiling import ";
if (auto lm = m_lm.lock()) {
out << lm->m_module_name;
} else {
out << "<deallocated>";
}
}
std::vector<generic_task_result> get_dependencies() override {
return {};
}
environment execute() override {
if (auto lm = m_lm.lock()) {
return mk_preimported_module(m_env0, *lm, m_mk_mod_ldr());
} else {
throw exception("loaded_module got deallocated before preimporting");
}
}
};
std::shared_ptr<loaded_module const> cache_preimported_env(
loaded_module && lm_ref, environment const & env0,
std::function<module_loader()> const & mk_mod_ldr) {
auto lm = std::make_shared<loaded_module>(lm_ref);
lm->m_env = get_global_task_queue()->mk_lazy_task<preimport_task>(lm, env0, mk_mod_ldr);
std::weak_ptr<loaded_module> wlm = lm;
lm->m_env = task_builder<environment>([env0, wlm, mk_mod_ldr] {
if (auto lm = wlm.lock()) {
return mk_preimported_module(env0, *lm, mk_mod_ldr());
} else {
throw exception("loaded_module got deallocated before preimporting");
}
}).build();
return lm;
}
@ -687,7 +607,7 @@ module_loader mk_olean_loader() {
auto modifs = parse_olean_modifications(parsed.m_serialized_modifications, fn);
return std::make_shared<loaded_module>(
loaded_module { fn, parsed.m_imports, modifs,
mk_pure_task_result(parsed.m_uses_sorry, ""), {} });
mk_pure_task<bool>(parsed.m_uses_sorry), {} });
};
}

View file

@ -14,7 +14,7 @@ Author: Leonardo de Moura
#include "kernel/pos_info_provider.h"
#include "kernel/inductive/inductive.h"
#include "library/io_state.h"
#include "util/task_queue.h"
#include "util/task.h"
namespace lean {
class corrupted_file_exception : public exception {
@ -34,9 +34,9 @@ struct loaded_module {
std::string m_module_name;
std::vector<module_name> m_imports;
modification_list m_modifications;
task_result<bool> m_uses_sorry;
task<bool> m_uses_sorry;
task_result<environment> m_env;
task<environment> m_env;
};
using module_loader = std::function<std::shared_ptr<loaded_module const> (std::string const &, module_name const &)>;
module_loader mk_olean_loader();
@ -58,6 +58,8 @@ import_modules(environment const & env,
std::string const & current_mod, std::vector<module_name> const & ref,
module_loader const & mod_ldr);
using module_id = std::string;
struct import_error {
module_id m_mod;
module_name m_import;
@ -102,10 +104,10 @@ public:
virtual const char * get_key() const = 0;
virtual void perform(environment &) const = 0;
virtual void serialize(serializer &) const = 0;
virtual void get_task_dependencies(std::vector<generic_task_result> &) const {}
virtual void get_task_dependencies(buffer<gtask> &) const {}
// Used to check for sorrys.
virtual void get_introduced_exprs(std::vector<task_result<expr>> &) const {}
virtual void get_introduced_exprs(std::vector<task<expr>> &) const {}
};
#define LEAN_MODIFICATION(k) \

View file

@ -13,15 +13,15 @@ Author: Gabriel Ebner
#include "util/file_lock.h"
#include "library/module_mgr.h"
#include "library/module.h"
#include "library/versioned_msg_buf.h"
#include "frontends/lean/pp.h"
#include "frontends/lean/parser.h"
#include "library_task_builder.h"
namespace lean {
bool module_info::parse_result::is_ok() const {
try {
return m_parsed_ok.get() && m_proofs_are_correct.get();
return get(m_parsed_ok) && get(m_proofs_are_correct);
} catch (...) {
return false;
}
@ -66,11 +66,11 @@ static module_loader mk_loader(module_id const & cur_mod, std::vector<module_inf
}
}
return[deps_per_mod_ptr] (std::string const & current_module, module_name const & import) {
return[deps_per_mod_ptr] (std::string const & current_module, module_name const & import) -> std::shared_ptr<loaded_module const> {
try {
for (auto & d : deps_per_mod_ptr->at(current_module)) {
if (d.m_import_name.m_name == import.m_name && d.m_import_name.m_relative == import.m_relative) {
return d.m_mod_info->m_result.get().m_loaded_module;
return get(d.m_mod_info->m_result).m_loaded_module;
}
}
} catch (std::out_of_range) {
@ -93,119 +93,65 @@ static pos_info find_end_pos(std::string const & src) {
return {line_no, static_cast<unsigned>(utf8_strlen(line.c_str()))};
}
class parse_lean_task : public task<module_info::parse_result> {
environment m_initial_env;
std::string m_contents;
snapshot_vector m_snapshots;
bool m_use_snapshots;
std::vector<module_info::dependency> m_deps;
pos_info m_end_pos;
static module_info::parse_result parse_lean(
std::string const & file_name, std::string const & contents,
environment const & initial_env,
snapshot_vector snapshots, bool use_snapshots,
std::vector<module_info::dependency> const & deps) {
auto import_fn = mk_loader(file_name, deps);
public:
parse_lean_task(std::string const & contents, environment const & initial_env,
snapshot_vector const & snapshots, bool use_snapshots,
std::vector<module_info::dependency> const & deps) :
m_initial_env(initial_env), m_contents(contents),
m_snapshots(snapshots), m_use_snapshots(use_snapshots),
m_deps(deps), m_end_pos(find_end_pos(contents)) {}
task_kind get_kind() const override { return task_kind::parse; }
bool use_exceptions = false;
std::istringstream in(contents);
parser p(initial_env, get_global_ios(), import_fn, in, file_name,
use_exceptions,
(snapshots.empty() || !use_snapshots) ? std::shared_ptr<snapshot>() : snapshots.back(),
use_snapshots ? &snapshots : nullptr);
auto parsed_ok = p();
pos_info get_end_pos() const override { return m_end_pos; }
module_info::parse_result mod;
void description(std::ostream & out) const override {
out << "parsing " << get_module_id();
}
mod.m_snapshots = std::move(snapshots);
std::vector<generic_task_result> get_dependencies() override {
std::vector<generic_task_result> deps;
for (auto & d : m_deps)
if (d.m_mod_info)
deps.push_back(d.m_mod_info->m_result);
if (!m_deps.empty()) {
// also add the preimported environment of the first dependency
if (auto & mod_info = m_deps.front().m_mod_info) {
if (auto res = mod_info->m_result.peek()) {
deps.push_back(res->m_loaded_module->m_env);
}
}
}
return deps;
}
mod.m_loaded_module = cache_preimported_env(
export_module(p.env(), file_name),
initial_env, [=] { return import_fn; });
module_info::parse_result execute() override {
auto import_fn = mk_loader(get_module_id(), m_deps);
mod.m_opts = p.ios().get_options();
bool use_exceptions = false;
std::istringstream in(m_contents);
parser p(m_initial_env, get_global_ios(), import_fn, in, get_module_id(),
use_exceptions,
(m_snapshots.empty() || !m_use_snapshots) ? std::shared_ptr<snapshot>() : m_snapshots.back(),
m_use_snapshots ? &m_snapshots : nullptr);
auto parsed_ok = p();
mod.m_parsed_ok = parsed_ok;
mod.m_proofs_are_correct = p.env().is_correct();
module_info::parse_result mod;
return mod;
}
mod.m_snapshots = std::move(m_snapshots);
static gtask compile_olean(std::shared_ptr<module_info const> const & mod) {
gtask mod_dep = mk_deep_dependency(mod->m_result, [] (buffer<gtask> & deps, module_info::parse_result const & res) {
for (auto & mdf : res.m_loaded_module->m_modifications)
mdf->get_task_dependencies(deps);
deps.push_back(res.m_loaded_module->m_uses_sorry);
deps.push_back(res.m_parsed_ok);
deps.push_back(res.m_proofs_are_correct);
});
mod.m_loaded_module = cache_preimported_env(
export_module(p.env(), get_module_id()),
m_initial_env, [=] { return import_fn; });
std::vector<gtask> olean_deps;
for (auto & dep : mod->m_deps)
olean_deps.push_back(dep.m_mod_info->m_olean_task);
mod.m_opts = p.ios().get_options();
mod.m_parsed_ok = parsed_ok;
mod.m_proofs_are_correct = p.env().is_correct();
return mod;
}
};
class olean_compilation_task : public task<unit> {
std::shared_ptr<module_info const> m_mod;
public:
olean_compilation_task(std::shared_ptr<module_info const> const & mod) : m_mod(mod) {}
task_kind get_kind() const override { return task_kind::parse; }
std::vector<generic_task_result> get_dependencies() override {
std::vector<generic_task_result> deps;
// Write the olean files in the correct order, so that they have the right mtime.
for (auto & d : m_mod->m_deps)
if (d.m_mod_info)
deps.push_back(d.m_mod_info->m_olean_task);
deps.push_back(m_mod->m_result);
if (auto res = m_mod->m_result.peek()) {
for (auto & mdf : res->m_loaded_module->m_modifications)
mdf->get_task_dependencies(deps);
deps.push_back(res->m_loaded_module->m_uses_sorry);
deps.push_back(res->m_parsed_ok);
deps.push_back(res->m_proofs_are_correct);
}
return deps;
}
void description(std::ostream & out) const override {
out << "saving object code for " << get_module_id();
}
unit execute() override {
if (m_mod->m_source != module_src::LEAN)
return add_library_task(task_builder<unit>([mod] {
if (mod->m_source != module_src::LEAN)
throw exception("cannot build olean from olean");
auto res = m_mod->m_result.get();
auto res = get(mod->m_result);
if (!res.is_ok())
throw exception("not creating olean file because of errors");
auto olean_fn = olean_of_lean(m_mod->m_mod);
auto olean_fn = olean_of_lean(mod->m_mod);
exclusive_file_lock output_lock(olean_fn);
std::ofstream out(olean_fn, std::ios_base::binary);
write_module(*res.m_loaded_module, out);
return {};
}
};
return unit();
}).depends_on(mod_dep).depends_on(olean_deps), std::string("saving olean"));
}
// TODO(gabriel): adapt to vfs
static module_id resolve(module_id const & module_file_name, module_name const & ref) {
@ -222,10 +168,7 @@ void module_mgr::build_module(module_id const & id, bool can_use_olean, name_set
if (!existing_mod->m_out_of_date) return;
scope_global_ios scope_ios(m_ios);
scoped_message_buffer scoped_msg_buf(m_msg_buf);
scoped_task_context scope_task_ctx(id, {1, 0});
message_bucket_id bucket_id { id, m_current_period };
scope_message_context scope_msg_ctx(bucket_id);
scope_log_tree lt(m_lt.mk_child(id, {}, { id, {{1, 0}, {static_cast<unsigned>(-1), 0}} }, true));
scope_traces_as_messages scope_trace_msgs(id, {1, 0});
try {
@ -254,7 +197,6 @@ void module_mgr::build_module(module_id const & id, bool can_use_olean, name_set
mod->m_mod = id;
mod->m_source = module_src::OLEAN;
mod->m_version = m_current_period;
mod->m_trans_mtime = mod->m_mtime = mtime;
for (auto & d : parsed_olean.m_imports) {
@ -275,39 +217,35 @@ void module_mgr::build_module(module_id const & id, bool can_use_olean, name_set
res.m_loaded_module = cache_preimported_env(
{ id, parsed_olean.m_imports,
parse_olean_modifications(parsed_olean.m_serialized_modifications, id),
mk_pure_task_result(parsed_olean.m_uses_sorry, ""), {} },
mk_pure_task<bool>(parsed_olean.m_uses_sorry), {} },
m_initial_env, [=] { return mk_loader(id, deps); });
res.m_parsed_ok = mk_pure_task_result(true, "olean parse success");
res.m_proofs_are_correct = mk_pure_task_result(true, "");
mod->m_result = mk_pure_task_result(res, "Loading " + olean_fn);
res.m_parsed_ok = mk_pure_task(true);
res.m_proofs_are_correct = mk_pure_task(true);
mod->m_result = mk_pure_task<module_info::parse_result>(res);
get_global_task_queue()->cancel_if(
[=] (generic_task * t) {
return t->get_version() < m_current_period && t->get_module_id() == id;
});
if (auto & old_mod = m_modules[id])
cancel(old_mod->m_cancel);
m_modules[id] = mod;
} else if (src == module_src::LEAN) {
std::istringstream in(contents);
auto bucket_name = "_build_module";
scope_log_tree lt2(lt.get().mk_child({}, {}, { id, {{1,0}, find_end_pos(contents)} }));
snapshot_vector snapshots;
if (m_use_snapshots) {
if (get_snapshots_or_unchanged_module(id, contents, mtime, snapshots)) {
m_modules[id]->m_out_of_date = false;
scope_msg_ctx.new_sub_bucket(bucket_name);
return;
}
}
auto task_pos = snapshots.empty() ? pos_info {1, 0} : snapshots.back()->m_pos;
scoped_task_context scope_task_ctx2(id, task_pos);
scope_message_context scope_msg_ctx2(bucket_name);
auto imports = get_direct_imports(id, contents);
auto mod = std::make_shared<module_info>();
mod->m_cancel = mk_cancellation_token(
snapshots.empty() ? nullptr : snapshots.back()->m_cancellation_token);
scope_cancellation_token scope_cancel(mod->m_cancel);
mod->m_mod = id;
mod->m_source = module_src::LEAN;
mod->m_trans_mtime = mod->m_mtime = mtime;
@ -330,19 +268,34 @@ void module_mgr::build_module(module_id const & id, bool can_use_olean, name_set
mod->m_lean_contents = optional<std::string>(contents);
mod->m_still_valid_snapshots = snapshots;
}
mod->m_version = m_current_period;
mod->m_result = get_global_task_queue()->submit<parse_lean_task>(
contents, m_initial_env,
snapshots, m_use_snapshots,
mod->m_deps);
std::vector<gtask> deps;
for (auto & d : mod->m_deps)
if (d.m_mod_info)
deps.push_back(d.m_mod_info->m_result);
if (!mod->m_deps.empty()) {
// also add the preimported environment of the first dependency
if (auto & mod_info = mod->m_deps.front().m_mod_info) {
deps.push_back(mk_deep_dependency(mod_info->m_result,
[] (buffer<gtask> & ds, module_info::parse_result const & res) {
ds.push_back(res.m_loaded_module->m_env);
}));
}
}
auto initial_env = m_initial_env;
auto use_snapshots = m_use_snapshots;
auto mod_deps = mod->m_deps;
mod->m_result = add_library_task(task_builder<module_info::parse_result>(
[=] { return parse_lean(id, contents, initial_env, snapshots, use_snapshots, mod_deps); })
.depends_on(deps), std::string("parsing"));
if (m_save_olean)
mod->m_olean_task = get_global_task_queue()->submit<olean_compilation_task>(mod);
mod->m_olean_task = compile_olean(mod);
get_global_task_queue()->cancel_if([=] (generic_task * t) {
return t->get_version() < m_current_period && t->get_module_id() == id && t->get_pos() >= task_pos;
});
if (auto & old_mod = m_modules[id]) {
cancel(old_mod->m_result); // TODO(gabriel): remove after parser refactoring
}
m_modules[id] = mod;
} else {
throw exception("unknown module source");
@ -364,7 +317,6 @@ std::shared_ptr<module_info const> module_mgr::get_module(module_id const & id)
void module_mgr::invalidate(module_id const & id) {
unique_lock<mutex> lock(m_mutex);
m_current_period++;
if (auto & mod = m_modules[id]) {
buffer<module_id> to_rebuild;
@ -392,7 +344,7 @@ static optional<pos_info> get_first_diff_pos(std::string const & a, std::string
}
snapshot_vector module_mgr::get_snapshots(module_id const & id) {
return get_module(id)->m_result.get().m_snapshots;
return get(get_module(id)->m_result).m_snapshots;
}
bool
@ -415,7 +367,7 @@ module_mgr::get_snapshots_or_unchanged_module(module_id const &id, std::string c
if (*mod->m_lean_contents == contents) return true;
if (mod->m_result) {
if (auto parse_res = mod->m_result.peek())
if (auto parse_res = peek(mod->m_result))
mod->m_still_valid_snapshots = parse_res->m_snapshots;
}
if (mod->m_still_valid_snapshots.empty()) return false;
@ -427,6 +379,7 @@ module_mgr::get_snapshots_or_unchanged_module(module_id const &id, std::string c
it++;
if (it != snaps.begin())
it--;
if (it != snaps.end()) cancel((*it)->m_cancellation_token); // TODO(gabriel): move somewhere better
snaps.erase(it, snaps.end());
vector = snaps;
return false;
@ -438,7 +391,7 @@ module_mgr::get_snapshots_or_unchanged_module(module_id const &id, std::string c
std::vector<module_name> module_mgr::get_direct_imports(module_id const & id, std::string const & contents) {
std::vector<module_name> imports;
try {
scope_message_context scope("dependencies");
scope_log_tree lt;
std::istringstream in(contents);
bool use_exceptions = true;
parser p(get_initial_env(), m_ios, nullptr, in, id, use_exceptions);
@ -448,6 +401,14 @@ std::vector<module_name> module_mgr::get_direct_imports(module_id const & id, st
return imports;
}
void module_mgr::cancel_all() {
for (auto & m : m_modules) {
if (auto mod = m.second) {
cancel(mod->m_cancel);
}
}
}
std::tuple<std::string, module_src, time_t> fs_module_vfs::load_module(module_id const & id, bool can_use_olean) {
auto lean_fn = id;
auto lean_mtime = get_mtime(lean_fn);

View file

@ -9,10 +9,10 @@ Author: Gabriel Ebner
#include <utility>
#include <vector>
#include <unordered_map>
#include "util/unit.h"
#include "util/name.h"
#include "kernel/environment.h"
#include "util/task_queue.h"
#include "library/message_buffer.h"
#include "util/task.h"
#include "library/io_state.h"
#include "library/trace.h"
#include "frontends/lean/parser.h"
@ -24,8 +24,6 @@ enum class module_src {
LEAN,
};
struct unit {};
struct module_info {
bool m_out_of_date = false;
@ -42,13 +40,11 @@ struct module_info {
optional<std::string> m_lean_contents;
period m_version = 0;
struct parse_result {
options m_opts;
task_result<bool> m_parsed_ok;
task_result<bool> m_proofs_are_correct;
task<bool> m_parsed_ok;
task<bool> m_proofs_are_correct;
bool is_ok() const;
std::shared_ptr<loaded_module const> m_loaded_module;
@ -56,13 +52,15 @@ struct module_info {
snapshot_vector m_snapshots;
};
task_result<parse_result> m_result;
task<parse_result> m_result;
snapshot_vector m_still_valid_snapshots;
task_result<unit> m_olean_task;
gtask m_olean_task;
cancellation_token m_cancel;
environment const & get_produced_env() const {
return m_result.get().m_loaded_module->m_env.get();
return get(get(m_result).m_loaded_module->m_env);
}
};
@ -81,15 +79,13 @@ public:
};
class module_mgr {
period m_current_period = 1;
bool m_use_snapshots = false;
bool m_save_olean = false;
environment m_initial_env;
io_state m_ios;
module_vfs * m_vfs;
message_buffer * m_msg_buf;
log_tree::node m_lt;
mutex m_mutex;
std::unordered_map<module_id, std::shared_ptr<module_info>> m_modules;
@ -101,8 +97,8 @@ class module_mgr {
module_id const & id, std::string const & contents, time_t mtime, snapshot_vector &vector);
public:
module_mgr(module_vfs * vfs, message_buffer * msg_buf, environment const & initial_env, io_state const & ios) :
m_initial_env(initial_env), m_ios(ios), m_vfs(vfs), m_msg_buf(msg_buf) {}
module_mgr(module_vfs * vfs, log_tree::node const & lt, environment const & initial_env, io_state const & ios) :
m_initial_env(initial_env), m_ios(ios), m_vfs(vfs), m_lt(lt) {}
void invalidate(module_id const & id);
@ -110,6 +106,8 @@ public:
snapshot_vector get_snapshots(module_id const & id);
void cancel_all();
void set_use_snapshots(bool use_snapshots) { m_use_snapshots = use_snapshots; }
bool get_use_snapshots() const { return m_use_snapshots; }

View file

@ -10,21 +10,13 @@ Author: Gabriel Ebner
#include "library/mt_task_queue.h"
#include "util/interrupt.h"
#include "util/flet.h"
#include "library/task_helper.h"
#if defined(LEAN_MULTI_THREAD)
namespace lean {
LEAN_THREAD_PTR(generic_task_result, g_current_task);
struct scoped_current_task {
generic_task_result * m_old;
scoped_current_task(generic_task_result * t) :
m_old(g_current_task) {
g_current_task = t;
}
~scoped_current_task() {
g_current_task = m_old;
}
LEAN_THREAD_PTR(gtask, g_current_task);
struct scoped_current_task : flet<gtask *> {
scoped_current_task(gtask * t) : flet(g_current_task, t) {}
};
template <class T>
@ -39,56 +31,9 @@ struct scoped_add {
}
};
mt_task_queue::mt_task_queue(unsigned num_workers) :
mt_task_queue(num_workers, [=] (generic_task *) { // NOLINT
task_priority p;
p.m_prio = 0;
return p;
}) {}
mt_task_queue::mt_task_queue(unsigned num_workers, mt_tq_prioritizer const & prioritizer) :
m_required_workers(num_workers), m_prioritizer(prioritizer),
m_ios(get_global_ios()), m_msg_buf(&get_global_message_buffer()) {
mt_task_queue::mt_task_queue(unsigned num_workers) : m_required_workers(num_workers) {
for (unsigned i = 0; i < num_workers; i++)
spawn_worker();
m_monitor_thr.reset(new lthread([=] {
unique_lock<mutex> lock(m_mutex);
while (true) {
m_queue_changed.wait(lock, [=] { return m_monitor_needs_to_run; });
if (m_shutting_down) return;
m_monitor_needs_to_run = false;
mt_tq_status st;
if (m_status_cb) {
for (auto & w : m_workers)
if (w->m_current_task)
st.m_executing.push_back(unwrap(w->m_current_task)->m_task);
for (auto & q : m_queue)
for (auto & tr : q.second)
if (auto t = unwrap(tr)->m_task)
st.m_queued.push_back(t);
for (auto & tr : m_waiting)
if (auto t = unwrap(tr)->m_task)
st.m_waiting.push_back(t);
}
generic_task * cur_task = nullptr;
if (m_progress_cb) {
for (auto & w : m_workers) {
if (w->m_current_task) {
cur_task = unwrap(w->m_current_task)->m_task;
break;
}
}
}
if (m_status_cb) m_status_cb(st);
if (m_progress_cb) m_progress_cb(cur_task);
m_shut_down_cv.wait_for(lock, m_monitor_ival);
}
}));
}
mt_task_queue::~mt_task_queue() {
@ -96,18 +41,23 @@ mt_task_queue::~mt_task_queue() {
unique_lock<mutex> lock(m_mutex);
m_queue_changed.wait(lock, [=] { return empty_core(); });
m_shutting_down = true;
m_monitor_needs_to_run = true;
m_queue_added.notify_all();
m_queue_changed.notify_all();
m_wake_up_worker.notify_all();
m_shut_down_cv.notify_all();
}
for (auto & w : m_workers) w->m_thread->join();
m_monitor_thr->join();
}
bool mt_task_queue::empty_core() {
for (auto & w : m_workers) {
if (w->m_current_task)
return false;
}
return m_queue.empty() && m_waiting.empty();
}
void mt_task_queue::notify_queue_changed() {
m_monitor_needs_to_run = true;
m_queue_changed.notify_all();
}
@ -116,11 +66,6 @@ void mt_task_queue::spawn_worker() {
auto this_worker = std::make_shared<worker_info>();
this_worker->m_thread.reset(new lthread([=]() {
save_stack_info(false);
this_worker->m_interrupt_flag = get_interrupt_flag();
scope_global_task_queue scope_tq(this);
scope_global_ios scope_ios(m_ios);
scoped_message_buffer scope_msg_buf(m_msg_buf);
unique_lock<mutex> lock(m_mutex);
scoped_add<int> dec_required(m_required_workers, -1);
@ -142,70 +87,76 @@ void mt_task_queue::spawn_worker() {
}
auto t = dequeue();
if (unwrap(t)->m_state.load() != task_result_state::QUEUED) continue;
if (get_state(t).load() != task_state::Queued) continue;
unwrap(t)->m_state = task_result_state::EXECUTING;
bool is_ok;
reset_interrupt();
get_state(t) = task_state::Running;
reset_heartbeat();
{
flet<generic_task_result> _(this_worker->m_current_task, t);
flet<gtask> _(this_worker->m_current_task, t);
scoped_current_task scope_cur_task(&t);
notify_queue_changed();
lock.unlock();
is_ok = execute_task_with_scopes(unwrap(t));
execute(t);
lock.lock();
}
reset_interrupt();
reset_heartbeat();
unwrap(t)->m_state = is_ok ? task_result_state::FINISHED : task_result_state::FAILED;
get_data(t).m_has_finished.notify_all();
handle_finished(t);
if (is_ok) {
for (auto & rdep : get_data(t).m_reverse_deps) {
if (unwrap(rdep)->has_evaluated()) {
m_waiting.erase(rdep);
} else {
switch (unwrap(rdep)->m_state.load()) {
case task_result_state::WAITING:
if (check_deps(rdep)) {
m_waiting.erase(rdep);
if (!unwrap(rdep)->has_evaluated())
enqueue(rdep);
}
break;
case task_result_state::QUEUED: break;
case task_result_state::FAILED: break;
default:
lean_unreachable();
}
}
}
} else {
propagate_failure(t);
}
unwrap(t)->clear_task();
notify_queue_changed();
}
}));
m_workers.push_back(this_worker);
}
void mt_task_queue::propagate_failure(generic_task_result const & tr) {
lean_assert(unwrap(tr)->m_state.load() == task_result_state::FAILED);
m_waiting.erase(tr);
void mt_task_queue::handle_finished(gtask const & t) {
get_sched_info(t).m_has_finished.notify_all();
if (auto t = unwrap(tr)->m_task) {
get_data(tr).m_has_finished.notify_all();
if (get_state(t).load() == task_state::Success) {
for (auto & rdep : get_sched_info(t).m_reverse_deps) {
switch (get_state(rdep).load()) {
case task_state::Waiting:
if (check_deps(rdep)) {
m_waiting.erase(rdep);
if (get_state(rdep).load() < task_state::Running) {
lean_assert(get_data(rdep));
if (get_data(rdep)->m_flags.m_needs_execution) {
enqueue(rdep);
} else {
get_state(rdep) = task_state::Success;
handle_finished(rdep);
}
}
}
break;
case task_state::Failed: case task_state::Queued:
// TODO(gabriel): why???
m_waiting.erase(rdep);
break;
default:
lean_unreachable();
}
}
} else {
propagate_failure(t);
}
for (auto & rdep : get_data(t).m_reverse_deps) {
switch (unwrap(rdep)->m_state.load()) {
case task_result_state::WAITING:
case task_result_state::QUEUED:
unwrap(rdep)->m_ex = unwrap(tr)->m_ex;
unwrap(rdep)->m_state = task_result_state::FAILED;
clear(t);
}
void mt_task_queue::propagate_failure(gtask const & t) {
lean_assert(get_state(t).load() == task_state::Failed);
m_waiting.erase(t);
if (get_data(t) && get_data(t)->m_sched_info) {
get_sched_info(t).m_has_finished.notify_all();
for (auto & rdep : get_sched_info(t).m_reverse_deps) {
if (get_data(rdep) && !get_data(rdep)->m_flags.m_propagate_errors_from_dependencies)
continue;
switch (get_state(rdep).load()) {
case task_state::Waiting: case task_state::Queued:
fail(rdep, t);
propagate_failure(rdep);
break;
default: break;
@ -213,103 +164,108 @@ void mt_task_queue::propagate_failure(generic_task_result const & tr) {
}
}
unwrap(tr)->clear_task();
clear(t);
}
void mt_task_queue::submit(generic_task_result const & t) {
if (!t || unwrap(t)->m_state.load() >= task_result_state::QUEUED) return;
void mt_task_queue::submit(gtask const & t, unsigned prio) {
if (!t || get_state(t).load() >= task_state::Running) return;
unique_lock<mutex> lock(m_mutex);
check_interrupted();
submit_core(t);
submit_core(t, prio);
}
void mt_task_queue::submit_core(generic_task_result const & t) {
if (!t || unwrap(t)->m_state.load() >= task_result_state::QUEUED) return;
get_prio(t) = m_prioritizer(unwrap(t)->m_task);
if (check_deps(t)) {
if (!unwrap(t)->has_evaluated()) {
enqueue(t);
}
} else {
unwrap(t)->m_state = task_result_state::WAITING;
m_waiting.insert(t);
notify_queue_changed();
void mt_task_queue::submit_core(gtask const & t, unsigned prio) {
if (!t) return;
switch (get_state(t).load()){
case task_state::Created:
get_data(t)->m_sched_info.reset(new mt_sched_info(prio));
if (check_deps(t)) {
if (get_state(t).load() < task_state::Running)
enqueue(t);
} else {
get_state(t) = task_state::Waiting;
m_waiting.insert(t);
notify_queue_changed();
}
break;
case task_state::Waiting: case task_state::Queued:
bump_prio(t, prio);
break;
case task_state::Running: case task_state::Failed: case task_state::Success:
break;
}
lean_assert(unwrap(t)->m_state.load() >= task_result_state::QUEUED);
lean_assert(get_state(t).load() >= task_state::Waiting);
}
void mt_task_queue::bump_prio(generic_task_result const & t, task_priority const & new_prio) {
if (unwrap(t)->m_task && new_prio < get_prio(t)) {
switch (unwrap(t)->m_state.load()) {
case task_result_state::QUEUED: {
auto prio = get_prio(t).m_prio;
void mt_task_queue::bump_prio(gtask const & t, unsigned new_prio) {
if (get_data(t) && new_prio < get_prio(t)) {
switch (get_state(t).load()) {
case task_state::Queued: {
auto prio = get_prio(t);
auto &q = m_queue[prio];
auto it = std::find(q.begin(), q.end(), t);
lean_assert(it != q.end());
q.erase(it);
if (q.empty()) m_queue.erase(prio);
get_prio(t).bump(new_prio);
get_prio(t) = std::min(get_prio(t), new_prio);
check_deps(t);
enqueue(t);
break;
}
case task_result_state::WAITING:
get_prio(t).bump(new_prio);
case task_state::Waiting:
get_prio(t) = std::min(get_prio(t), new_prio);
check_deps(t);
break;
case task_result_state::EXECUTING:
case task_result_state::FINISHED:
case task_result_state::FAILED:
case task_state::Running: case task_state::Failed: case task_state::Success:
break;
default: lean_unreachable();
}
}
}
bool mt_task_queue::check_deps(generic_task_result const & t) {
std::vector<generic_task_result> deps;
bool mt_task_queue::check_deps(gtask const & t) {
lean_assert(get_data(t));
buffer<gtask> deps;
try {
deps = unwrap(t)->m_task->get_dependencies();
get_data(t)->m_imp->get_dependencies(deps);
} catch (...) {}
auto do_prio_inv = unwrap(t)->m_task->do_priority_inversion();
auto do_prio_inv = get_data(t)->m_flags.m_do_priority_inversion;
auto prio = get_prio(t);
for (auto & dep : deps) {
if (dep) {
submit_core(dep);
if (do_prio_inv) bump_prio(dep, get_prio(t));
submit_core(dep, prio);
if (do_prio_inv) bump_prio(dep, prio);
}
}
for (auto & dep : deps) {
if (!dep) continue;
switch (unwrap(dep)->m_state.load()) {
case task_result_state::WAITING:
case task_result_state::QUEUED:
case task_result_state::EXECUTING:
lean_assert(unwrap(dep)->m_task);
get_data(dep).m_reverse_deps.push_back(t);
switch (get_state(dep).load()) {
case task_state::Waiting: case task_state::Queued: case task_state::Running:
lean_assert(get_imp(dep));
get_sched_info(dep).m_reverse_deps.push_back(t);
return false;
case task_result_state::FINISHED:
case task_state::Success:
break;
case task_state::Failed:
if (get_data(t)->m_flags.m_propagate_errors_from_dependencies) {
fail(t, dep);
propagate_failure(t);
return true;
}
break;
case task_result_state::FAILED:
unwrap(t)->m_ex = unwrap(dep)->m_ex;
unwrap(t)->m_state = task_result_state::FAILED;
propagate_failure(t);
return true;
default: lean_unreachable();
}
}
return true;
}
void mt_task_queue::wait(generic_task_result const & t) {
if (!t) return;
void mt_task_queue::wait_for_finish(gtask const & t) {
if (!t || get_state(t).load() > task_state::Running) return;
unique_lock<mutex> lock(m_mutex);
submit_core(t);
if (g_current_task && unwrap(t)->m_task && get_prio(*g_current_task) < get_prio(t)) {
bump_prio(t, get_prio(*g_current_task));
}
while (!unwrap(t)->has_evaluated()) {
submit_core(t, get_default_prio());
while (get_state(t).load() <= task_state::Running) {
if (g_current_task) {
// std::cerr << "waiting: " << g_current_task->description() << " -> " << t.description() << std::endl;
scoped_add<int> inc_required(m_required_workers, +1);
@ -318,60 +274,30 @@ void mt_task_queue::wait(generic_task_result const & t) {
} else {
m_wake_up_worker.notify_one();
}
get_data(t).m_has_finished.wait(lock);
get_sched_info(t).m_has_finished.wait(lock);
} else {
get_data(t).m_has_finished.wait(lock);
get_sched_info(t).m_has_finished.wait(lock);
}
}
switch (unwrap(t)->m_state.load()) {
case task_result_state::FAILED: std::rethrow_exception(unwrap(t)->m_ex);
case task_result_state::FINISHED: return;
switch (get_state(t).load()) {
case task_state::Failed: case task_state::Success: return;
default: throw exception("invalid task state");
}
}
void mt_task_queue::cancel_if(const std::function<bool(generic_task *)> & pred) { // NOLINT
std::vector<generic_task_result> to_cancel;
unique_lock<mutex> lock(m_mutex);
for (auto & w : m_workers)
if (w->m_current_task && pred(unwrap(w->m_current_task)->m_task))
to_cancel.push_back(w->m_current_task);
for (auto & q : m_queue)
for (auto & t : q.second)
if (unwrap(t)->m_task && pred(unwrap(t)->m_task))
to_cancel.push_back(t);
for (auto & t : m_waiting)
if (unwrap(t)->m_task && pred(unwrap(t)->m_task))
to_cancel.push_back(t);
for (auto & t : to_cancel)
cancel_core(t);
}
void mt_task_queue::cancel_core(generic_task_result const & t) {
switch (unwrap(t)->m_state.load()) {
case task_result_state::WAITING:
void mt_task_queue::cancel_core(gtask const & t) {
if (!t) return;
switch (get_state(t).load()) {
case task_state::Waiting:
m_waiting.erase(t);
case task_result_state::QUEUED:
unwrap(t)->m_ex = std::make_exception_ptr(task_cancellation_exception(t));
unwrap(t)->m_state = task_result_state::FAILED;
case task_state::Created: case task_state::Queued:
fail(t, std::make_exception_ptr(cancellation_exception()));
propagate_failure(t);
unwrap(t)->clear_task();
return;
case task_result_state::EXECUTING:
for (auto & w : m_workers) {
if (w->m_current_task == t) {
w->m_interrupt_flag->store(true);
}
}
return;
default: return;
}
}
void mt_task_queue::cancel(generic_task_result const & t) {
void mt_task_queue::fail_and_dispose(gtask const & t) {
if (!t) return;
unique_lock<mutex> lock(m_mutex);
cancel_core(t);
@ -382,30 +308,7 @@ void mt_task_queue::join() {
m_queue_changed.wait(lock, [=] { return empty_core(); });
}
bool mt_task_queue::empty_core() {
for (auto & w : m_workers) {
if (w->m_current_task)
return false;
}
return m_queue.empty() && m_waiting.empty();
}
bool mt_task_queue::empty() {
unique_lock<mutex> lock(m_mutex);
return empty_core();
}
optional<generic_task_result> mt_task_queue::get_current_task() {
unique_lock<mutex> lock(m_mutex);
for (auto & w : m_workers) {
if (w->m_current_task) {
return optional<generic_task_result>(w->m_current_task);
}
}
return optional<generic_task_result>();
}
generic_task_result mt_task_queue::dequeue() {
gtask mt_task_queue::dequeue() {
lean_assert(!m_queue.empty());
auto it = m_queue.begin();
auto & highest_prio = it->second;
@ -415,67 +318,37 @@ generic_task_result mt_task_queue::dequeue() {
if (highest_prio.empty()) {
m_queue.erase(it);
}
return result;
return std::move(result);
}
void mt_task_queue::enqueue(generic_task_result const & t) {
lean_assert(unwrap(t)->m_state.load() < task_result_state::EXECUTING);
lean_assert(unwrap(t)->m_task);
unwrap(t)->m_state = task_result_state::QUEUED;
m_queue[get_prio(t).m_prio].push_back(t);
void mt_task_queue::enqueue(gtask const & t) {
lean_assert(get_state(t).load() < task_state::Running);
lean_assert(get_imp(t));
get_state(t) = task_state::Queued;
m_queue[get_prio(t)].push_back(t);
m_queue_added.notify_one();
notify_queue_changed();
}
void mt_task_queue::reprioritize(mt_tq_prioritizer const & p) {
void mt_task_queue::evacuate() {
unique_lock<mutex> lock(m_mutex);
m_prioritizer = p;
reprioritize_core();
for (auto & q : m_queue) for (auto & t : q.second) cancel_core(t);
buffer<gtask> to_cancel; // copy because of iterator invalidation
for (auto & t : m_waiting) to_cancel.push_back(t);
for (auto & t : to_cancel) cancel_core(t);
}
void mt_task_queue::reprioritize() {
unique_lock<mutex> lock(m_mutex);
reprioritize_core();
void mt_task_queue::submit(gtask const & t) {
submit(t, get_default_prio());
}
void mt_task_queue::reprioritize_core() {
auto old_queues = m_queue;
m_queue.clear();
for (auto & q : old_queues) {
for (auto & t : q.second) {
if (unwrap(t)) {
get_prio(t) = m_prioritizer(unwrap(t)->m_task);
enqueue(t);
}
}
unsigned mt_task_queue::get_default_prio() {
if (g_current_task && get_imp(*g_current_task)) {
return get_prio(*g_current_task);
} else {
return 0;
}
for (auto & q : old_queues) for (auto & t : q.second) check_deps(t);
for (auto & t : m_waiting) {
if (unwrap(t)->m_task) {
get_prio(t) = m_prioritizer(unwrap(t)->m_task);
check_deps(t);
}
}
}
void mt_task_queue::set_monitor_interval(chrono::milliseconds const & ival) {
unique_lock<mutex> lock(m_mutex);
m_monitor_ival = ival;
}
void mt_task_queue::set_progress_callback(progress_cb const & cb) {
unique_lock<mutex> lock(m_mutex);
m_progress_cb = cb;
}
void mt_task_queue::set_status_callback(mt_tq_status_cb const &cb) {
unique_lock<mutex> lock(m_mutex);
m_status_cb = cb;
}
void mt_task_queue::prepare_task(generic_task_result const & t) {
set_bucket(t, get_scope_message_context().new_sub_bucket());
}
}

View file

@ -6,41 +6,20 @@ Author: Gabriel Ebner
*/
#pragma once
#include <deque>
#include <string>
#include <vector>
#include <unordered_set>
#include <map>
#include <functional>
#include <unordered_map>
#include "util/optional.h"
#include "library/io_state.h"
#include "util/task_queue.h"
#include "library/message_buffer.h"
#include "util/task.h"
namespace lean {
#if defined(LEAN_MULTI_THREAD)
using mt_tq_prioritizer = std::function<task_priority(generic_task *)>; // NOLINT
// disabling lint because it thinks that this is a cast ^^^
struct mt_tq_status {
std::vector<generic_task const *> m_executing, m_queued, m_waiting, m_delayed;
size_t size() const { return m_executing.size() + m_queued.size() + m_waiting.size() + m_delayed.size(); }
template <class F> void for_each(F && f) const {
for (auto & t : m_executing) f(t);
for (auto & t : m_queued) f(t);
for (auto & t : m_waiting) f(t);
for (auto & t : m_delayed) f(t);
}
};
using mt_tq_status_cb = std::function<void(mt_tq_status const &)>;
class mt_task_queue : public task_queue {
mutex m_mutex;
std::map<unsigned, std::deque<generic_task_result>> m_queue;
std::unordered_set<generic_task_result, generic_task_result::hash> m_waiting;
std::map<unsigned, std::deque<gtask>> m_queue;
std::unordered_set<gtask> m_waiting;
condition_variable m_queue_added, m_queue_changed;
void notify_queue_changed();
@ -49,8 +28,7 @@ class mt_task_queue : public task_queue {
struct worker_info {
std::unique_ptr<lthread> m_thread;
generic_task_result m_current_task;
atomic_bool * m_interrupt_flag = nullptr;
gtask m_current_task;
};
std::vector<std::shared_ptr<worker_info>> m_workers;
void spawn_worker();
@ -59,56 +37,49 @@ class mt_task_queue : public task_queue {
int m_required_workers;
condition_variable m_wake_up_worker;
mt_tq_prioritizer m_prioritizer;
chrono::milliseconds m_monitor_ival = chrono::milliseconds(100);
progress_cb m_progress_cb;
mt_tq_status_cb m_status_cb;
std::unique_ptr<lthread> m_monitor_thr;
bool m_monitor_needs_to_run = false;
io_state m_ios;
message_buffer * m_msg_buf;
gtask dequeue();
void enqueue(gtask const &);
bool check_deps(gtask const &);
void propagate_failure(gtask const &);
void submit_core(gtask const &, unsigned);
void bump_prio(gtask const &, unsigned);
void cancel_core(gtask const &);
bool empty_core();
generic_task_result dequeue();
void enqueue(generic_task_result const &);
void handle_finished(gtask const &);
void prepare_task(generic_task_result const &result) override;
struct mt_sched_info : public scheduling_info {
unsigned m_prio;
std::vector<gtask> m_reverse_deps;
condition_variable m_has_finished;
bool check_deps(generic_task_result const &);
void propagate_failure(generic_task_result const &);
void submit_core(generic_task_result const &);
void bump_prio(generic_task_result const &, task_priority const &);
void cancel_core(generic_task_result const &);
mt_sched_info(unsigned prio) : m_prio(prio) {}
};
mt_sched_info & get_sched_info(gtask const & t) {
return static_cast<mt_sched_info &>(*get_data(t)->m_sched_info);
}
void reprioritize_core();
unsigned & get_prio(gtask const & t) { return get_sched_info(t).m_prio; }
gtask_imp * get_imp(gtask const & t) { return get_data(t)->m_imp.get(); }
task_priority & get_prio(generic_task_result const & tr) { return get_data(tr).m_prio; }
unsigned get_default_prio();
public:
mt_task_queue(unsigned num_workers);
mt_task_queue(unsigned num_workers, mt_tq_prioritizer const & prioritizer);
~mt_task_queue();
optional<generic_task_result> get_current_task() override;
void wait_for_finish(gtask const & t) override;
void fail_and_dispose(gtask const &t) override;
void submit(gtask const & t, unsigned prio);
void submit(gtask const & t);
void evacuate() override;
void join() override;
bool empty() override;
void submit(generic_task_result const &) override;
void wait(generic_task_result const & t) override;
void cancel(generic_task_result const & t) override;
void cancel_if(std::function<bool(generic_task *)> const & pred) override; // NOLINT
void set_monitor_interval(chrono::milliseconds const &);
void set_progress_callback(progress_cb const & cb) override;
void set_status_callback(mt_tq_status_cb const & cb);
void reprioritize(mt_tq_prioritizer const & p);
void reprioritize();
};
#endif
}

View file

@ -4,75 +4,31 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#include <vector>
#include "library/st_task_queue.h"
#include "library/task_helper.h"
#include "library/message_buffer.h"
namespace lean {
st_task_queue::st_task_queue() {}
void st_task_queue::wait(generic_task_result const & t) {
submit(t);
switch (unwrap(t)->m_state.load()) {
case task_result_state::FAILED:
std::rethrow_exception(unwrap(t) -> m_ex);
case task_result_state::FINISHED:
return;
default:
lean_unreachable();
void st_task_queue::wait_for_finish(gtask const & t) {
if (t && get_state(t).load() <= task_state::Running) {
get_state(t).store(task_state::Running);
execute(t);
clear(t);
}
}
void st_task_queue::fail_and_dispose(gtask const & t) {
if (t && get_state(t).load() < task_state::Running) {
fail(t, std::make_exception_ptr(cancellation_exception()));
clear(t);
}
}
void st_task_queue::submit(gtask const & t) {
wait_for_finish(t);
}
void st_task_queue::evacuate() {}
void st_task_queue::join() {}
bool st_task_queue::empty() {
return true;
}
optional<generic_task_result> st_task_queue::get_current_task() {
return optional<generic_task_result>();
}
void st_task_queue::submit(generic_task_result const & t) {
if (!t) return;
if (unwrap(t)->m_state.load() >= task_result_state::QUEUED) return;
std::vector<generic_task_result> deps;
try { deps = unwrap(t)->m_task->get_dependencies(); } catch (...) {}
for (auto & d : deps) {
if (!d) continue;
submit(d);
switch (unwrap(d)->m_state.load()) {
case task_result_state::FAILED:
unwrap(t)->m_state = task_result_state::FAILED;
unwrap(t)->m_ex = unwrap(d)->m_ex;
unwrap(t)->clear_task();
return;
case task_result_state::FINISHED:
break;
default:
lean_unreachable();
}
}
if (m_progress_cb) m_progress_cb(unwrap(t)->m_task);
bool is_ok = execute_task_with_scopes(unwrap(t));
unwrap(t)->m_state = is_ok ? task_result_state::FINISHED : task_result_state::FAILED;
unwrap(t)->clear_task();
}
void st_task_queue::cancel(generic_task_result const &) {}
void st_task_queue::set_progress_callback(progress_cb const & cb) {
m_progress_cb = cb;
}
void st_task_queue::prepare_task(generic_task_result const & t) {
set_bucket(t, get_scope_message_context().new_sub_bucket());
}
void st_task_queue::cancel_if(const std::function<bool(generic_task *)> &) {} // NOLINT
}

View file

@ -5,28 +5,20 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#pragma once
#include "util/task_queue.h"
#include "util/task.h"
namespace lean {
class st_task_queue : public task_queue {
progress_cb m_progress_cb;
void prepare_task(generic_task_result const &result) override;
public:
st_task_queue();
void wait_for_finish(gtask const &) override;
void fail_and_dispose(gtask const &) override;
void submit(gtask const &) override;
void evacuate() override;
optional<generic_task_result> get_current_task() override;
bool empty() override;
void join() override;
void wait(generic_task_result const & t) override;
void cancel(generic_task_result const & t) override;
void submit(generic_task_result const &) override;
void cancel_if(std::function<bool(generic_task *)> const & pred) override; // NOLINT
void set_progress_callback(progress_cb const &) override;
};
}

View file

@ -1,45 +0,0 @@
/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#include <string>
#include "library/trace.h"
#include "library/message_builder.h"
#include "library/task_helper.h"
#include "library/message_buffer.h"
namespace lean {
bool execute_task_with_scopes(generic_task_result_cell * task) {
lean_assert(!task->has_evaluated());
try {
scoped_task_context ctx(task->m_task->get_module_id(), task->m_task->get_task_pos());
scope_message_context scope_msg_ctx(task->m_task->get_bucket());
try {
scope_traces_as_messages scope_traces(task->m_task->get_module_id(), task->m_task->get_pos());
task->execute_and_store_result();
} catch (task_cancellation_exception) {
throw;
} catch (interrupted) {
throw;
} catch (throwable & ex) {
environment env;
message_builder builder(env, get_global_ios(), task->m_task->get_module_id(), task->m_task->get_pos(), ERROR);
builder.set_exception(ex);
builder.report();
throw;
}
return true;
} catch (interrupted) {
task->m_ex = std::make_exception_ptr(
task_cancellation_exception(generic_task_result(task)));
return false;
} catch (...) {
task->m_ex = std::current_exception();
return false;
}
}
}

View file

@ -1,14 +0,0 @@
/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#pragma once
#include "util/task_queue.h"
namespace lean {
bool execute_task_with_scopes(generic_task_result_cell * task);
}

View file

@ -12,7 +12,6 @@ Author: Leonardo de Moura
#include "library/io_state.h"
#include "library/trace.h"
#include "library/messages.h"
#include "library/message_buffer.h"
namespace lean {
static name_set * g_trace_classes = nullptr;

View file

@ -7,6 +7,7 @@ Author: Leonardo de Moura
#pragma once
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include "util/flet.h"
#include "util/lbool.h"
#include "kernel/environment.h"

View file

@ -1,127 +0,0 @@
/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#include <utility>
#include <string>
#include <vector>
#include "util/name_set.h"
#include "library/versioned_msg_buf.h"
#include "frontends/lean/info_manager.h"
namespace lean {
void versioned_msg_buf::start_bucket(message_bucket_id const & bucket) {
unique_lock<mutex> lock(m_mutex);
if (is_bucket_valid_core(bucket)) {
auto & buf = m_buf[bucket.m_bucket];
if (buf.m_version < bucket.m_version) {
buf.m_version = bucket.m_version;
buf.m_msgs.clear();
buf.m_infom.reset();
on_cleared(bucket.m_bucket);
}
}
}
void versioned_msg_buf::report(message_bucket_id const & bucket, message const & msg) {
unique_lock<mutex> lock(m_mutex);
auto & buf = m_buf[bucket.m_bucket];
if (buf.m_version == bucket.m_version) {
buf.m_msgs.push_back(msg);
on_reported(bucket.m_bucket, msg);
}
}
void versioned_msg_buf::finish_bucket(message_bucket_id const & bucket, name_set const & children) {
unique_lock<mutex> lock(m_mutex);
auto & buf = m_buf[bucket.m_bucket];
if (buf.m_version != bucket.m_version) return;
auto old_children = buf.m_children;
buf.m_children.clear();
children.for_each([&] (name const & c) { buf.m_children.insert(c); });
old_children.for_each([&] (name const & c) {
if (!buf.m_children.contains(c)) {
if (m_buf[c].m_version < bucket.m_version)
erase_bucket(c);
}
});
}
void versioned_msg_buf::erase_bucket(name const & bucket) {
auto & bck_buf = m_buf[bucket];
bck_buf.m_children.for_each([&] (name const & c) { erase_bucket(c); });
m_buf.erase(bucket);
on_cleared(bucket);
}
bool versioned_msg_buf::is_bucket_valid_core(message_bucket_id const &bucket) {
if (!bucket.m_bucket.is_atomic()) {
name parent = bucket.m_bucket.get_prefix();
auto & parent_buf = m_buf[parent];
if (parent_buf.m_version > bucket.m_version &&
!parent_buf.m_children.contains(bucket.m_bucket))
return false;
}
return m_buf[bucket.m_bucket].m_version <= bucket.m_version;
}
bool versioned_msg_buf::is_bucket_valid(message_bucket_id const & bucket) {
unique_lock<mutex> lock(m_mutex);
return is_bucket_valid_core(bucket);
}
void versioned_msg_buf::report_info_manager(message_bucket_id const & bucket, info_manager const & infom) {
unique_lock<mutex> lock(m_mutex);
auto & buf = m_buf[bucket.m_bucket];
if (buf.m_version == bucket.m_version) {
buf.m_infom = std::unique_ptr<info_manager>(new info_manager(infom));
}
}
std::vector<message> versioned_msg_buf::get_messages() {
unique_lock<mutex> lock(m_mutex);
return get_messages_core();
}
std::vector<message> versioned_msg_buf::get_messages_core() {
std::vector<message> msgs;
for (auto & buf : m_buf) {
for (auto & msg : buf.second.m_msgs)
msgs.push_back(msg);
}
return msgs;
}
std::vector<info_manager> versioned_msg_buf::get_info_managers() {
unique_lock<mutex> lock(m_mutex);
std::vector<info_manager> result;
for (auto & buf : m_buf) {
if (buf.second.m_infom)
result.push_back(*buf.second.m_infom);
}
return result;
}
void versioned_msg_buf::on_cleared(name const &) {}
void versioned_msg_buf::on_reported(name const &, message const &) {}
std::vector<name> versioned_msg_buf::get_nonempty_buckets_core() {
std::vector<name> res;
for (auto & buf : m_buf) {
if (!buf.second.m_msgs.empty())
res.push_back(buf.first);
}
return res;
}
}

View file

@ -1,54 +0,0 @@
/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#pragma once
#include <vector>
#include <unordered_map>
#include "util/name.h"
#include "library/message_buffer.h"
#include "frontends/lean/info_manager.h"
namespace lean {
class versioned_msg_buf : public message_buffer {
// TODO(gabriel): structure as tree?
struct msg_bucket {
std::vector<message> m_msgs;
std::unique_ptr<info_manager> m_infom;
period m_version = 0;
name_set m_children;
};
mutex m_mutex;
std::unordered_map<name, msg_bucket, name_hash> m_buf;
void erase_bucket(name const & bucket);
bool is_bucket_valid_core(message_bucket_id const & bucket);
protected:
virtual void on_cleared(name const &bucket);
virtual void on_reported(name const & bucket, message const & msg);
std::vector<message> get_messages_core();
std::vector<name> get_nonempty_buckets_core();
unique_lock<mutex> get_lock() { return unique_lock<mutex>(m_mutex); }
public:
versioned_msg_buf() {}
void start_bucket(message_bucket_id const & bucket) override;
void report(message_bucket_id const & bucket, message const & msg) override;
void finish_bucket(message_bucket_id const & bucket, name_set const & children) override;
bool is_bucket_valid(message_bucket_id const & bucket) override;
void report_info_manager(message_bucket_id const & bucket, info_manager const & infom) override;
std::vector<message> get_messages();
std::vector<info_manager> get_info_managers();
};
}

View file

@ -175,6 +175,7 @@ class ts_vm_obj {
static void steal_ptr(vm_obj & o) { o.steal_ptr(); }
std::shared_ptr<data> m_data;
public:
ts_vm_obj() = default;
ts_vm_obj(vm_obj const & o);
vm_obj to_vm_obj() const;
};

View file

@ -22,7 +22,6 @@ Author: Sebastian Ullrich
#include "library/vm/vm_nat.h"
#include "library/vm/vm_pos_info.h"
#include "library/vm/interaction_state.h"
#include "util/task_queue.h"
namespace lean {

View file

@ -16,47 +16,17 @@ Author: Gabriel Ebner
#include "library/vm/vm.h"
#include "library/vm/vm_string.h"
#include "library/vm/vm_expr.h"
#include "util/task_queue.h"
#include "library/library_task_builder.h"
namespace lean {
template <class A, class B, class Fn>
class map_task : public task<B> {
Fn m_fn;
task_result<A> m_in;
public:
map_task(Fn const & fn, task_result<A> const & in) :
m_fn(fn), m_in(in) {}
void description(std::ostream & out) const override {
out << "mapping";
}
bool is_tiny() const override { return true; }
std::vector<generic_task_result> get_dependencies() override {
return {m_in};
}
B execute() override {
return m_fn(m_in.get());
}
};
template <class B, class A, class Fn>
static task_result<B> mk_map_task(task_result<A> const & in, Fn const & fn) {
return get_global_task_queue()->mk_lazy_task<map_task<A, B, Fn>>(fn, in);
};
struct vm_task : public vm_external {
task_result<ts_vm_obj> m_val;
vm_task(task_result<ts_vm_obj> const & v) : m_val(v) {}
task<ts_vm_obj> m_val;
vm_task(task<ts_vm_obj> const & v) : m_val(v) {}
virtual ~vm_task() {}
virtual void dealloc() override { this->~vm_task(); get_vm_allocator().deallocate(sizeof(vm_task), this); }
virtual vm_external * ts_clone(vm_clone_fn const &) override {
/* A possible workaround is to use get() here and wait for the task to be done. */
throw exception("vm_task object cannot be copied to thread safe storage");
return new vm_task(m_val);
}
virtual vm_external * clone(vm_clone_fn const &) override {
lean_unreachable();
@ -67,98 +37,60 @@ bool is_task(vm_obj const & o) {
return is_external(o) && dynamic_cast<vm_task *>(to_external(o));
}
task_result<ts_vm_obj> const & to_task(vm_obj const & o) {
task<ts_vm_obj> const & to_task(vm_obj const & o) {
lean_vm_check(dynamic_cast<vm_task *>(to_external(o)));
return static_cast<vm_task*>(to_external(o))->m_val;
}
vm_obj to_obj(task_result<ts_vm_obj> const & n) {
vm_obj to_obj(task<ts_vm_obj> const & n) {
return mk_vm_external(new (get_vm_allocator().allocate(sizeof(vm_task))) vm_task(n));
}
vm_obj to_obj(task_result<expr> const & n) {
return to_obj(mk_map_task<ts_vm_obj>(n, [] (expr const & e) { return ts_vm_obj(to_obj(e)); }));
vm_obj to_obj(task<expr> const & n) {
return to_obj(map<ts_vm_obj>(n, [] (expr const & e) { return ts_vm_obj(to_obj(e)); }).does_not_require_own_thread().build());
}
task_result<expr> to_expr_task(vm_obj const & o) {
return mk_map_task<expr>(to_task(o), [] (ts_vm_obj const & o) { return to_expr(o.to_vm_obj()); });
task<expr> to_expr_task(vm_obj const & o) {
return map<expr>(to_task(o), [] (ts_vm_obj const & o) { return to_expr(o.to_vm_obj()); }).does_not_require_own_thread().build();
}
vm_obj vm_task_get(vm_obj const &, vm_obj const & t) {
return to_task(t).get().to_vm_obj();
return get(to_task(t)).to_vm_obj();
}
vm_obj vm_task_pure(vm_obj const &, vm_obj const & t) {
return to_obj(mk_pure_task_result(ts_vm_obj(t), "task.pure"));
return to_obj(mk_pure_task(ts_vm_obj(t)));
}
struct vm_map_task : public task<ts_vm_obj> {
environment m_env;
options m_opts;
bool m_use_infom;
ts_vm_obj m_fn;
task_result<ts_vm_obj> m_arg;
public:
vm_map_task(environment const & env, options const & opts, bool use_infom,
ts_vm_obj const & fn, task_result<ts_vm_obj> const & arg) :
m_env(env), m_opts(opts), m_use_infom(use_infom), m_fn(fn), m_arg(arg) {}
void description(std::ostream & out) const override {
out << "task.map";
}
std::vector<generic_task_result> get_dependencies() override {
return {m_arg};
}
ts_vm_obj execute() override {
// Tracing
type_context tc(m_env);
scope_trace_env scope_trace(m_env, m_opts, tc);
// Info manager
auto_reporting_info_manager_scope scope_infom(get_module_id(), m_use_infom);
vm_state state(m_env, m_opts);
scope_vm_state scope_vm(state);
return ts_vm_obj(state.invoke(m_fn.to_vm_obj(), m_arg.get().to_vm_obj()));
}
};
struct vm_flatten_task : public task<ts_vm_obj> {
task_result<ts_vm_obj> m_task;
public:
vm_flatten_task(task_result<ts_vm_obj> const & t) : m_task(t) {}
void description(std::ostream & out) const override {
out << "task.flatten";
}
bool is_tiny() const override { return true; }
std::vector<generic_task_result> get_dependencies() override {
std::vector<generic_task_result> deps = { m_task };
if (auto res = m_task.peek())
deps.push_back(to_task(res->to_vm_obj()));
return deps;
}
ts_vm_obj execute() override {
return to_task(m_task.get().to_vm_obj()).get();
}
};
vm_obj vm_task_map(vm_obj const &, vm_obj const &, vm_obj const & fn, vm_obj const & t) {
return to_obj(get_global_task_queue()->submit<vm_map_task>(
get_vm_state().env(), get_vm_state().get_options(),
get_global_info_manager() != nullptr,
ts_vm_obj(fn), to_task(t)));
auto env = get_vm_state().env();
auto opts = get_vm_state().get_options();
auto has_infom = get_global_info_manager() != nullptr;
auto ts_fn = ts_vm_obj(fn);
return to_obj(add_library_task(map<ts_vm_obj>(to_task(t), [env, opts, has_infom, ts_fn] (ts_vm_obj const & arg) {
// Tracing
type_context tc(env);
scope_trace_env scope_trace(env, opts, tc);
// Info manager
auto file_name = logtree().get_location().m_file_name; // TODO(gabriel)
auto_reporting_info_manager_scope scope_infom(file_name, has_infom);
vm_state state(env, opts);
scope_vm_state scope_vm(state);
return ts_vm_obj(state.invoke(ts_fn.to_vm_obj(), arg.to_vm_obj()));
}), false));
}
vm_obj vm_task_flatten(vm_obj const &, vm_obj const & t) {
return to_obj(get_global_task_queue()->submit<vm_flatten_task>(to_task(t)));
vm_obj vm_task_flatten(vm_obj const &, vm_obj const & o) {
auto t = to_task(o);
return to_obj(task_builder<ts_vm_obj>([t] {
return get(to_task(get(t).to_vm_obj()));
}).depends_on_fn([t] (buffer<gtask> & deps) {
deps.push_back(t);
if (auto res = peek(t))
deps.push_back(to_task(res->to_vm_obj()));
}).does_not_require_own_thread().build());
}
void initialize_vm_task() {

View file

@ -8,11 +8,11 @@ Author: Gabriel Ebner
#include "library/vm/vm.h"
namespace lean {
vm_obj to_obj(task_result<ts_vm_obj> const & n);
vm_obj to_obj(task_result<expr> const & n);
vm_obj to_obj(task<ts_vm_obj> const & n);
vm_obj to_obj(task<expr> const & n);
bool is_task(vm_obj const & o);
task_result<expr> to_expr_task(vm_obj const & o);
task_result<ts_vm_obj> const & to_task(vm_obj const & o);
task<expr> to_expr_task(vm_obj const & o);
task<ts_vm_obj> const & to_task(vm_obj const & o);
void initialize_vm_task();
void finalize_vm_task();

View file

@ -12,6 +12,7 @@ Author: Leonardo de Moura
#include <string>
#include <utility>
#include <vector>
#include <util/timer.h>
#include "util/realpath.h"
#include "util/stackinfo.h"
#include "util/macros.h"
@ -215,10 +216,14 @@ public:
}
};
class progress_message_stream : public stream_message_buffer {
class progress_message_stream {
mutex m_mutex;
bool m_showing_task = false;
std::ostream & m_out;
bool m_use_json;
bool m_show_progress;
std::unique_ptr<single_timer> m_timer;
void clear_shown_task() {
if (m_showing_task) {
@ -228,21 +233,70 @@ class progress_message_stream : public stream_message_buffer {
}
public:
progress_message_stream(std::ostream & out) :
stream_message_buffer(out), m_out(out) {}
progress_message_stream(std::ostream & out, bool use_json, bool show_progress) :
m_out(out), m_use_json(use_json), m_show_progress(show_progress) {
#if defined(LEAN_MULTI_THREAD)
if (show_progress) {
m_timer.reset(new single_timer);
}
#endif
}
~progress_message_stream() {
m_timer.reset();
clear_shown_task();
}
void report(message_bucket_id const & bucket, message const & msg) override {
void on_event(std::vector<log_tree::event> const & events) {
unique_lock<mutex> lock(m_mutex);
clear_shown_task();
stream_message_buffer::report(bucket, msg);
log_tree::event const * cur_task = nullptr;
for (auto & e : events) {
switch (e.m_kind) {
case log_tree::event::EntryAdded:
case log_tree::event::EntryRemoved:
if (auto msg = dynamic_cast<message const *>(e.m_entry.get())) {
clear_shown_task();
if (m_use_json) {
#if defined(LEAN_JSON)
print_json(m_out, *msg);
#endif
} else {
m_out << *msg;
}
}
break;
case log_tree::event::ProducerSet:
taskq().submit(e.m_node.get_producer());
break;
case log_tree::event::Finished:
if (e.m_node.get_description().size())
cur_task = &e;
break;
}
}
if (m_show_progress && cur_task) {
std::ostringstream fmt;
fmt << cur_task->m_node.get_location().m_file_name << ": " << cur_task->m_node.get_description();
auto fmt_str = fmt.str();
#if defined(LEAN_MULTI_THREAD)
if (m_timer) {
m_timer->set(chrono::steady_clock::now() + chrono::milliseconds(100),
[=] { show_current_task(fmt_str); }, false);
} else {
show_current_task_core(fmt_str);
}
#else
show_current_task_core(fmt_str);
#endif
}
}
void show_current_task(std::string const & desc) {
unique_lock<mutex> lock(m_mutex);
show_current_task_core(desc);
}
void show_current_task_core(std::string const & desc) {
if (m_use_json) return;
clear_shown_task();
#if defined(LEAN_EMSCRIPTEN)
m_out << desc << std::endl;
@ -419,18 +473,13 @@ int main(int argc, char ** argv) {
}
#endif
std::shared_ptr<message_buffer> msg_buf =
std::make_shared<progress_message_stream>(std::cout);
#if defined(LEAN_JSON)
if (json_output) {
msg_buf = std::make_shared<json_message_stream>(std::cout);
ios.set_regular_channel(ios.get_diagnostic_channel_ptr());
}
#endif
progress_message_stream msg_stream(std::cout, json_output, make_mode);
if (json_output) ios.set_regular_channel(ios.get_diagnostic_channel_ptr());
log_tree lt;
lt.add_listener([&] (std::vector<log_tree::event> const & evs) { msg_stream.on_event(evs); });
scope_global_ios scope_ios(ios);
scoped_message_buffer scope_msg_buf(msg_buf.get());
scope_message_context scope_msg_ctx(message_bucket_id { "_global", 1 });
if (smt2) {
// Note: the smt2 flag may override other flags
@ -483,14 +532,15 @@ int main(int argc, char ** argv) {
#else
tq = std::make_shared<st_task_queue>();
#endif
scope_global_task_queue scope(tq.get());
set_task_queue(tq.get());
if (make_mode) {
if (auto prog_msg_buf = std::dynamic_pointer_cast<progress_message_stream>(msg_buf))
tq->set_progress_callback([=](generic_task * t) {
if (t && !t->is_tiny())
prog_msg_buf->show_current_task(t->description());
});
// TODO(gabriel)
// if (auto prog_msg_buf = std::dynamic_pointer_cast<progress_message_stream>(message_handler))
// tq->set_progress_callback([=](generic_task * t) {
// if (t && !t->is_tiny())
// prog_msg_buf->show_current_task(t->description());
// });
}
fs_module_vfs vfs;
@ -499,7 +549,7 @@ int main(int argc, char ** argv) {
vfs.m_modules_to_load_from_source.insert(mod_id);
}
module_mgr mod_mgr(&vfs, msg_buf.get(), env, ios);
module_mgr mod_mgr(&vfs, lt.get_root(), env, ios);
mod_mgr.set_save_olean(make_mode);
bool ok = true;
@ -525,14 +575,16 @@ int main(int argc, char ** argv) {
mods.push_back({mod, mod_info});
}
// TODO(gabriel)
tq->join();
for (auto & mod : mods) {
try {
auto res = mod.second->m_result.get();
auto res = get(mod.second->m_result);
ok = ok && res.is_ok();
} catch (...) {
ok = false;
throw;
// exception has already been reported
}
}
@ -540,7 +592,7 @@ int main(int argc, char ** argv) {
// Options appear to be empty, pretty sure I'm making a mistake here.
if (compile && !mods.empty()) {
auto final_env = mods.front().second->get_produced_env();
auto final_opts = mods.front().second->m_result.get().m_opts;
auto final_opts = get(mods.front().second->m_result).m_opts;
type_context tc(final_env, final_opts);
lean::scope_trace_env scope2(final_env, final_opts, tc);
lean::native::scope_config scoped_native_config(

View file

@ -25,19 +25,15 @@ private:
st_task_queue m_tq;
json_message_stream m_msg_buf;
public:
emscripten_shell(): m_env(mk_environment(LEAN_BELIEVER_TRUST_LEVEL + 1)),
m_ios(options({"trace", "as_messages"}, true),
mk_pretty_formatter_factory()),
m_server(0, m_env, m_ios),
m_msg_buf(std::cout) { }
m_server(0, m_env, m_ios) {}
int process_request(std::string msg) {
scope_global_task_queue scope_tq(&m_tq);
scope_global_ios scoped_ios(m_ios);
scoped_message_buffer scope_msg_buf(&m_msg_buf);
scope_message_context msg_ctx(message_bucket_id { "lean.js", 0 });
scope_log_tree lt(m_server.get_log_tree().get_root().mk_child("_server", {}, {}, {}, true));
try {
m_server.handle_request(json::parse(msg));
return 0;

View file

@ -9,7 +9,8 @@ Authors: Gabriel Ebner, Leonardo de Moura, Sebastian Ullrich
#include <string>
#include <vector>
#include <algorithm>
#include "util/lean_path.h"
#include <clocale>
#include <library/library_task_builder.h>
#include "util/sexpr/option_declarations.h"
#include "util/timer.h"
#include "library/mt_task_queue.h"
@ -48,49 +49,74 @@ struct all_messages_msg {
}
};
class server::msg_buf : public versioned_msg_buf {
class server::message_handler {
server * m_srv;
log_tree * m_lt;
std::unordered_set<name, name_hash> m_nonempty_buckets;
mutex m_mutex;
std::unordered_set<std::string> m_dirty_files;
bool m_full_refresh_scheduled = false;
std::unique_ptr<single_timer> m_timer;
public:
msg_buf(server * srv, bool use_timer) : m_srv(srv) {
message_handler(server * srv, log_tree * lt, bool use_timer) : m_srv(srv), m_lt(lt) {
if (use_timer) m_timer.reset(new single_timer);
}
static void get_messages(log_tree::node const & n, std::vector<message> & msgs) {
for (auto & e : n.get_entries()) {
if (auto msg = dynamic_cast<message const *>(e.get())) {
msgs.push_back(*msg);
}
}
n.get_children().for_each([&] (name const &, log_tree::node const & c) {
get_messages(c, msgs);
});
}
std::vector<message> get_messages_core() {
std::vector<message> msgs;
get_messages(m_lt->get_root(), msgs);
return msgs;
}
void schedule_refresh() {
#if defined(LEAN_MULTI_THREAD)
if (m_timer) {
m_full_refresh_scheduled = true;
m_timer->set(chrono::steady_clock::now() + chrono::milliseconds(100), [&] {
auto lock = get_lock();
m_full_refresh_scheduled = false;
{
unique_lock<mutex> lock(m_mutex);
m_full_refresh_scheduled = false;
m_dirty_files.clear();
}
m_srv->send_msg(all_messages_msg{get_messages_core()});
}, false);
}
#endif
}
void on_cleared(name const & bucket) override {
if (m_nonempty_buckets.count(bucket)) {
m_nonempty_buckets.clear();
for (auto & b : get_nonempty_buckets_core()) m_nonempty_buckets.insert(b);
void on_event(std::vector<log_tree::event> const & events) {
unique_lock<mutex> lock(m_mutex);
for (auto & e : events) {
switch (e.m_kind) {
case log_tree::event::EntryAdded:
case log_tree::event::EntryRemoved:
if (auto msg = dynamic_cast<message const *>(e.m_entry.get())) {
m_dirty_files.insert(msg->get_file_name());
}
break;
// We need to remove a message, so let's send everything again
schedule_refresh();
if (!m_full_refresh_scheduled)
m_srv->send_msg(all_messages_msg{get_messages_core()});
default: break;
}
}
if (!m_dirty_files.empty()) {
schedule_refresh();
if (!m_full_refresh_scheduled) {
m_dirty_files.clear();
m_srv->send_msg(all_messages_msg{get_messages_core()});
}
}
}
void on_reported(name const & bucket, message const & msg) override {
m_nonempty_buckets.insert(bucket);
schedule_refresh();
if (!m_full_refresh_scheduled)
m_srv->send_msg(msg_reported_msg{msg});
}
};
@ -108,31 +134,98 @@ struct current_tasks_msg {
return j;
}
json json_of_task(generic_task const * t) {
static json json_of_task(log_tree::node const & t) {
json j;
j["file_name"] = t->get_module_id();
auto pos = t->get_pos();
j["file_name"] = t.get_location().m_file_name;
auto pos = t.get_location().m_range.m_begin;
j["pos_line"] = pos.first;
j["pos_col"] = pos.second;
auto end_pos = t->get_end_pos();
auto end_pos = t.get_location().m_range.m_end;
j["end_pos_line"] = end_pos.first;
j["end_pos_col"] = end_pos.second;
j["desc"] = t->description();
j["desc"] = t.get_description();
return j;
}
};
#if defined(LEAN_MULTI_THREAD)
current_tasks_msg(mt_tq_status const & st, std::unordered_set<std::string> const & visible_files) {
m_is_running = st.size() > 0;
st.for_each([&] (generic_task const * t) {
if (!t->is_tiny() && visible_files.count(t->get_module_id())) {
auto j = json_of_task(t);
if (!m_cur_task) m_cur_task = {j};
m_tasks.push_back(j);
}
class server::tasks_handler {
server * m_srv;
log_tree * m_lt;
mutex m_mutex;
std::unique_ptr<single_timer> m_timer;
public:
tasks_handler(server * srv, log_tree * lt, bool use_timer) : m_srv(srv), m_lt(lt) {
if (use_timer) m_timer.reset(new single_timer);
}
void mk_tasks_msg(log_tree::node const & n, current_tasks_msg & msg) {
if (n.get_producer()) {
msg.m_is_running = true;
msg.m_tasks.push_back(current_tasks_msg::json_of_task(n));
return;
}
n.get_children().for_each([&] (name const &, log_tree::node const & c) {
mk_tasks_msg(c, msg);
});
}
void submit_core(log_tree::node const & n) {
// TODO(gabriel): priorities & ROI
// if (n.get_producer() && m_srv->m_visible_files.count(n.get_location().m_file_name)) {
if (auto prod = n.get_producer()) {
taskq().submit(prod);
}
}
void resubmit_core(log_tree::node const & n) {
submit_core(n);
n.get_children().for_each([&] (name const &, log_tree::node const & c) {
resubmit_core(c);
});
}
void resubmit_core() {
resubmit_core(m_srv->m_lt.get_root());
}
current_tasks_msg mk_tasks_msg() {
current_tasks_msg msg;
mk_tasks_msg(m_lt->get_root(), msg);
return msg;
}
void schedule_refresh() {
#if defined(LEAN_MULTI_THREAD)
if (m_timer) {
m_timer->set(chrono::steady_clock::now() + chrono::milliseconds(200), [&] {
m_srv->send_msg(mk_tasks_msg());
}, false);
}
#endif
}
void on_event(std::vector<log_tree::event> const & events) {
bool need_refresh = false;
for (auto & e : events) {
switch (e.m_kind) {
case log_tree::event::ProducerSet:
submit_core(e.m_node);
need_refresh = true;
break;
case log_tree::event::Finished:
need_refresh = true;
break;
default:
break;
}
}
if (need_refresh) {
unique_lock<mutex> lock(m_mutex);
schedule_refresh();
}
}
};
server::server(unsigned num_threads, environment const & initial_env, io_state const & ios) :
@ -140,34 +233,35 @@ server::server(unsigned num_threads, environment const & initial_env, io_state c
m_ios.set_regular_channel(std::make_shared<stderr_channel>());
m_ios.set_diagnostic_channel(std::make_shared<stderr_channel>());
m_msg_buf.reset(new msg_buf(this, num_threads > 0));
m_msg_handler.reset(new message_handler(this, &m_lt, num_threads > 0));
m_tasks_handler.reset(new tasks_handler(this, &m_lt, num_threads > 0));
m_lt.add_listener([&] (std::vector<log_tree::event> const & evs) {
m_msg_handler->on_event(evs);
m_tasks_handler->on_event(evs);
});
scope_global_ios scoped_ios(m_ios);
scoped_message_buffer scope_msg_buf(m_msg_buf.get());
#if defined(LEAN_MULTI_THREAD)
if (num_threads == 0) {
m_tq.reset(new st_task_queue());
m_tq.reset(new st_task_queue);
} else {
auto tq = new mt_task_queue(num_threads);
m_tq.reset(tq);
tq->set_monitor_interval(chrono::milliseconds(300));
tq->set_status_callback([this] (mt_tq_status const & st) {
unique_lock<mutex> _(m_visible_files_mutex);
send_msg(current_tasks_msg(st, m_visible_files));
});
m_tq.reset(new mt_task_queue(num_threads));
}
#else
m_tq.reset(new st_task_queue());
#endif
scope_global_task_queue scope_tq(m_tq.get());
m_mod_mgr.reset(new module_mgr(this, m_msg_buf.get(), m_initial_env, m_ios));
set_task_queue(m_tq.get());
m_mod_mgr.reset(new module_mgr(this, m_lt.get_root(), m_initial_env, m_ios));
m_mod_mgr->set_use_snapshots(true);
m_mod_mgr->set_save_olean(false);
}
server::~server() {
m_tq->cancel_if([] (generic_task *) { return true; }); // NOLINT
m_mod_mgr->cancel_all();
cancel(m_bg_task_ctok);
m_tq->evacuate();
}
struct server::cmd_req {
@ -211,9 +305,7 @@ struct unrelated_error_msg {
};
void server::run() {
scope_global_task_queue scope_tq(m_tq.get());
scope_global_ios scoped_ios(m_ios);
scoped_message_buffer scope_msg_buf(m_msg_buf.get());
/* Leo: we use std::setlocale to make sure decimal period is displayed as ".".
We added this hack because the json library code used for ensuring this property
@ -275,16 +367,14 @@ server::cmd_res server::handle_sync(server::cmd_req const & req) {
auto mtime = time(nullptr);
#if defined(LEAN_MULTI_THREAD)
{
unique_lock<mutex> _(m_visible_files_mutex);
unique_lock<mutex> l(m_visible_files_mutex);
if (m_visible_files.count(new_file_name) == 0) {
m_visible_files.insert(new_file_name);
if (auto tq = dynamic_cast<mt_task_queue *>(m_tq.get()))
tq->reprioritize(mk_interactive_prioritizer(m_visible_files));
l.unlock();
m_tasks_handler->resubmit_core();
}
}
#endif
bool needs_invalidation = !m_open_files.count(new_file_name);
@ -309,8 +399,8 @@ server::cmd_res server::handle_sync(server::cmd_req const & req) {
std::shared_ptr<snapshot const> get_closest_snapshot(std::shared_ptr<module_info const> const & mod_info, pos_info p) {
auto ret = std::shared_ptr<snapshot const>();
if (auto res = mod_info->m_result.peek()) {
auto snapshots = mod_info.get()->m_result.get().m_snapshots;
if (auto res = peek(mod_info->m_result)) {
auto snapshots = res->m_snapshots;
if (snapshots.size()) {
ret = snapshots.front();
for (auto &snap : snapshots) {
@ -327,11 +417,10 @@ void parse_breaking_at_pos(module_id const & mod_id, std::shared_ptr<module_info
std::istringstream in(*mod_info->m_lean_contents);
if (auto snap = get_closest_snapshot(mod_info, pos)) {
snapshot s = *snap;
s.m_sub_buckets.clear(); // HACK
// ignore messages from reparsing
null_message_buffer null_msg_buf;
scoped_message_buffer scope(&null_msg_buf);
log_tree null;
scope_log_tree scope_lt(null.get_root());
bool use_exceptions = true;
parser p(s.m_env, get_global_ios(), mk_dummy_loader(), in, mod_id,
@ -342,128 +431,113 @@ void parse_breaking_at_pos(module_id const & mod_id, std::shared_ptr<module_info
}
}
class server::auto_complete_task : public task<unit> {
server * m_server;
unsigned m_seq_num;
std::shared_ptr<module_info const> m_mod_info;
bool m_skip_completions;
json server::autocomplete(std::shared_ptr<module_info const> const & mod_info, bool skip_completions,
pos_info const & pos0) {
auto pos = pos0;
if (pos.second == 0)
pos.first--;
pos.second--;
json j;
public:
auto_complete_task(server * server, unsigned seq_num, std::shared_ptr<module_info const> const & mod_info,
bool skip_completions) :
m_server(server), m_seq_num(seq_num), m_mod_info(mod_info), m_skip_completions(skip_completions) {}
// TODO(gabriel): find cleaner way to give it high prio
task_kind get_kind() const override { return task_kind::parse; }
virtual bool is_tiny() const override { return true; }
void description(std::ostream & out) const override {
out << "generating auto-completion at (" << get_pos().first << ", " << get_pos().second << ")";
}
std::vector<generic_task_result> get_dependencies() override {
return {m_mod_info->m_result};
}
// TODO(gabriel): handle cancellation
unit execute() override {
if (auto snap = get_closest_snapshot(mod_info, pos)) {
try {
pos_info pos = get_pos();
if (pos.second == 0)
pos.first--;
pos.second--;
json j;
if (auto snap = get_closest_snapshot(m_mod_info, pos)) {
try {
parse_breaking_at_pos(get_module_id(), m_mod_info, pos, true);
} catch (break_at_pos_exception & e) {
report_completions(snap->m_env, snap->m_options, get_pos(), m_skip_completions, m_mod_info->m_mod.c_str(),
e, j);
} catch (throwable & ex) {}
}
m_server->send_msg(cmd_res(m_seq_num, j));
} catch (throwable & ex) {
m_server->send_msg(cmd_res(m_seq_num, std::string(ex.what())));
}
return {};
parse_breaking_at_pos(mod_info->m_mod, mod_info, pos, true);
} catch (break_at_pos_exception & e) {
report_completions(snap->m_env, snap->m_options, pos, skip_completions, mod_info->m_mod.c_str(),
e, j);
} catch (throwable & ex) {}
}
};
return j;
}
void server::handle_complete(cmd_req const & req) {
cancel(m_bg_task_ctok);
m_bg_task_ctok = mk_cancellation_token();
std::string fn = req.m_payload.at("file_name");
pos_info pos = {req.m_payload.at("line"), req.m_payload.at("column")};
bool skip_completions = false;
if (req.m_payload.count("skip_completions"))
skip_completions = req.m_payload.at("skip_completions");
scope_message_context scope_msg_ctx(message_bucket_id { "_server", 0 });
scoped_task_context scope_task_ctx(fn, pos);
auto mod_info = m_mod_mgr->get_module(fn);
get_global_task_queue()->submit<auto_complete_task>(this, req.m_seq_num, mod_info, skip_completions);
auto complete_gen_task =
task_builder<json>([=] { return autocomplete(mod_info, skip_completions, pos); })
.set_cancellation_token(m_bg_task_ctok)
// .wrap(library_scopes())
.build();
taskq().submit(task_builder<unit>([this, req, complete_gen_task] {
try {
send_msg(cmd_res(req.m_seq_num, get(complete_gen_task)));
} catch (throwable & ex) {
send_msg(cmd_res(req.m_seq_num, std::string(ex.what())));
}
return unit{};
}).depends_on(complete_gen_task).ignore_dependency_errors().build());
}
class server::info_task : public task<unit> {
server * m_server;
unsigned m_seq_num;
std::shared_ptr<module_info const> m_mod_info;
public:
info_task(server * server, unsigned seq_num, std::shared_ptr<module_info const> const & mod_info):
m_server(server), m_seq_num(seq_num), m_mod_info(mod_info) {}
// TODO(gabriel): find cleaner way to give it high prio
task_kind get_kind() const override { return task_kind::parse; }
virtual bool is_tiny() const override { return true; }
void description(std::ostream & out) const override {
out << "info at (" << get_pos().first << ", " << get_pos().second << ")";
}
// TODO(gabriel): handle cancellation
unit execute() override {
try {
pos_info pos = get_pos();
json j;
try {
parse_breaking_at_pos(get_module_id(), m_mod_info, pos);
} catch (break_at_pos_exception & e) {
auto opts = m_server->m_ios.get_options();
auto env = m_server->m_initial_env;
if (auto snap = get_closest_snapshot(m_mod_info, e.m_token_info.m_pos)) {
env = snap->m_env;
opts = snap->m_options;
}
report_info(env, opts, m_server->m_ios, *m_mod_info, m_server->m_msg_buf->get_info_managers(), e, j);
} catch (throwable & ex) {}
m_server->send_msg(cmd_res(m_seq_num, j));
} catch (throwable & ex) {
m_server->send_msg(cmd_res(m_seq_num, std::string(ex.what())));
static void get_info_managers(log_tree::node const & n, std::vector<info_manager> & infoms) {
for (auto & e : n.get_entries()) {
if (auto infom = dynamic_cast<info_manager const *>(e.get())) {
infoms.push_back(*infom);
}
return {};
}
};
n.get_children().for_each([&] (name const &, log_tree::node const & c) {
get_info_managers(c, infoms);
});
}
std::vector<info_manager> get_info_managers(log_tree const & t) {
std::vector<info_manager> infoms;
get_info_managers(t.get_root(), infoms);
return infoms;
}
json server::info(std::shared_ptr<module_info const> const & mod_info, pos_info const & pos) {
json j;
try {
parse_breaking_at_pos(mod_info->m_mod, mod_info, pos);
} catch (break_at_pos_exception & e) {
auto opts = m_ios.get_options();
auto env = m_initial_env;
if (auto snap = get_closest_snapshot(mod_info, e.m_token_info.m_pos)) {
env = snap->m_env;
opts = snap->m_options;
}
report_info(env, opts, m_ios, *mod_info, get_info_managers(m_lt), e, j);
} catch (throwable & ex) {}
return j;
}
void server::handle_info(server::cmd_req const & req) {
m_info_result.cancel();
cancel(m_bg_task_ctok);
m_bg_task_ctok = mk_cancellation_token();
std::string fn = req.m_payload.at("file_name");
pos_info pos = {req.m_payload.at("line"), req.m_payload.at("column")};
scope_message_context scope_msg_ctx(message_bucket_id { "_server", 0 });
scoped_task_context scope_task_ctx(fn, pos);
auto mod_info = m_mod_mgr->get_module(fn);
m_info_result = {get_global_task_queue()->submit<info_task>(this, req.m_seq_num, mod_info)};
auto info_gen_task = task_builder<json>([=] {
return info(mod_info, pos);
}).depends_on(mk_deep_dependency(
mod_info->m_result, [] (buffer<gtask> & deps, module_info::parse_result const & res) {
deps.push_back(res.m_loaded_module->m_env);
})).set_cancellation_token(m_bg_task_ctok)
// .wrap(library_scopes())
.build();
taskq().submit(task_builder<unit>([this, req, info_gen_task] {
try {
send_msg(cmd_res(req.m_seq_num, get(info_gen_task)));
} catch (throwable & ex) {
send_msg(cmd_res(req.m_seq_num, std::string(ex.what())));
}
return unit{};
}).depends_on(info_gen_task).ignore_dependency_errors().build());
}
std::tuple<std::string, module_src, time_t> server::load_module(module_id const & id, bool can_use_olean) {
@ -487,42 +561,5 @@ void initialize_server() {
void finalize_server() {
}
#if defined(LEAN_MULTI_THREAD)
mt_tq_prioritizer mk_interactive_prioritizer(std::unordered_set<module_id> const & roi) {
const unsigned
ROI_PARSING_PRIO = 10,
ROI_PRINT_PRIO = 11,
ROI_ELAB_PRIO = 13,
DEFAULT_PRIO = 20,
PARSING_PRIO = 20,
PRINT_PRIO = 21,
ELAB_PRIO = 23;
return[=] (generic_task * t) {
task_priority p;
p.m_prio = DEFAULT_PRIO;
bool in_roi = roi.count(t->get_module_id()) > 0;
if (!in_roi)
p.m_not_before = { chrono::steady_clock::now() + chrono::seconds(10) };
switch (t->get_kind()) {
case task_kind::parse:
p.m_prio = in_roi ? ROI_PARSING_PRIO : PARSING_PRIO;
break;
case task_kind::elab:
p.m_prio = in_roi ? ROI_ELAB_PRIO : ELAB_PRIO;
break;
case task_kind::print:
p.m_prio = in_roi ? ROI_PRINT_PRIO : PRINT_PRIO;
break;
}
return p;
};
}
#endif
}
#endif

View file

@ -9,16 +9,14 @@ Authors: Gabriel Ebner, Sebastian Ullrich
#include "kernel/pos_info_provider.h"
#include "kernel/environment.h"
#include "library/io_state.h"
#include "library/versioned_msg_buf.h"
#include "library/module_mgr.h"
#include "frontends/lean/json.h"
#include "library/mt_task_queue.h"
#include "util/cancellable.h"
namespace lean {
#if defined(LEAN_MULTI_THREAD)
mt_tq_prioritizer mk_interactive_prioritizer(std::unordered_set<module_id> const & roi);
#endif
unsigned get_auto_completion_max_results(options const &);
class server : public module_vfs {
options m_opts;
@ -35,14 +33,19 @@ class server : public module_vfs {
std::unordered_set<std::string> m_visible_files;
mutex m_out_mutex;
class msg_buf;
std::unique_ptr<msg_buf> m_msg_buf;
log_tree m_lt;
class message_handler;
std::unique_ptr<message_handler> m_msg_handler;
class tasks_handler;
std::unique_ptr<tasks_handler> m_tasks_handler;
std::unique_ptr<module_mgr> m_mod_mgr;
std::unique_ptr<task_queue> m_tq;
fs_module_vfs m_fs_vfs;
task_result<unit> m_info_result;
cancellation_token m_bg_task_ctok;
template <class Msg>
void send_msg(Msg const &);
@ -57,6 +60,9 @@ class server : public module_vfs {
class info_task;
void handle_info(cmd_req const & req);
json autocomplete(std::shared_ptr<module_info const> const & mod_info, bool skip_completions, pos_info const & pos);
json info(std::shared_ptr<module_info const> const & mod_info, pos_info const & pos);
public:
server(unsigned num_threads, environment const & intial_env, io_state const & ios);
~server();
@ -65,6 +71,8 @@ public:
void run();
void handle_request(json const & req);
log_tree & get_log_tree() { return m_lt; }
};
void initialize_server();

View file

@ -4,6 +4,7 @@ add_library(util OBJECT debug.cpp name.cpp name_set.cpp fresh_name.cpp
stackinfo.cpp lean_path.cpp serializer.cpp lbool.cpp
bitap_fuzzy_search.cpp init_module.cpp thread.cpp memory_pool.cpp
utf8.cpp name_map.cpp list_fn.cpp null_ostream.cpp file_lock.cpp
task_queue.cpp timer.cpp
timer.cpp task.cpp task_builder.cpp cancellable.cpp
log_tree.cpp
small_object_allocator.cpp subscripted_name_set.cpp process.cpp
parser_exception.cpp)

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

@ -0,0 +1,58 @@
/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#include "cancellable.h"
#include <algorithm>
namespace lean {
cancellation_token_cell::cancellation_token_cell(cancellation_token const & parent) :
m_cancelled(false), m_parent(parent) {}
cancellation_token_cell::~cancellation_token_cell() {
if (m_parent) m_parent->gc();
}
void cancellation_token_cell::cancel(std::shared_ptr<cancellable> const &) {
unique_lock<mutex> lock(m_mutex);
m_cancelled.store(true);
auto children = m_children;
lock.unlock();
for (auto & child : children)
cancelw(child);
}
void cancellation_token_cell::gc() {
unique_lock<mutex> lock(m_mutex);
m_children.erase(std::remove_if(m_children.begin(), m_children.end(),
[] (std::weak_ptr<cancellable> const & c) { return c.expired(); }),
m_children.end());
}
void cancellation_token_cell::add_child(std::weak_ptr<cancellable> const & c) {
unique_lock<mutex> lock(m_mutex);
m_children.push_back(c);
if (m_cancelled.load()) {
lock.unlock();
cancelw(c);
}
}
cancellation_token mk_cancellation_token(cancellation_token const & parent) {
auto ctok = std::make_shared<cancellation_token_cell>(parent);
if (parent) parent->add_child(ctok);
return ctok;
}
LEAN_THREAD_PTR(cancellation_token const, g_cancellation_token);
scope_cancellation_token::scope_cancellation_token(cancellation_token const * tok) :
flet<cancellation_token const *>(g_cancellation_token, tok) {}
cancellation_token global_cancellation_token() {
return g_cancellation_token ? *g_cancellation_token : nullptr;
}
}

53
src/util/cancellable.h Normal file
View file

@ -0,0 +1,53 @@
/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#pragma once
#include <vector>
#include "util/thread.h"
#include "flet.h"
namespace lean {
struct cancellable {
virtual ~cancellable() {}
virtual void cancel(std::shared_ptr<cancellable> const & self) = 0;
};
class cancellation_token_cell;
using cancellation_token = std::shared_ptr<cancellation_token_cell>;
class cancellation_token_cell : public cancellable {
mutex m_mutex;
atomic_bool m_cancelled;
std::vector<std::weak_ptr<cancellable>> m_children;
cancellation_token m_parent;
friend cancellation_token mk_cancellation_token(cancellation_token const &);
public:
cancellation_token_cell(cancellation_token const & parent);
~cancellation_token_cell();
void add_child(std::weak_ptr<cancellable> const &);
void gc();
void cancel(std::shared_ptr<cancellable> const & self) override;
};
cancellation_token mk_cancellation_token(cancellation_token const & parent);
inline cancellation_token mk_cancellation_token() { return mk_cancellation_token(nullptr); }
inline void cancel(std::shared_ptr<cancellable> const & c) { if (c) c->cancel(c); }
inline void cancelw(std::weak_ptr<cancellable> const & wc) { if (auto c = wc.lock()) cancel(c); }
struct scope_cancellation_token : flet<cancellation_token const *> {
scope_cancellation_token(cancellation_token const *);
scope_cancellation_token(cancellation_token & t) : scope_cancellation_token(&t) {}
};
cancellation_token global_cancellation_token();
}

View file

@ -33,23 +33,16 @@ void check_heartbeat() {
throw heartbeat_exception();
}
MK_THREAD_LOCAL_GET(atomic_bool, get_g_interrupt, false);
LEAN_THREAD_VALUE(atomic_bool *, g_interrupt_flag, nullptr);
void request_interrupt() {
get_g_interrupt().store(true);
}
scoped_interrupt_flag::scoped_interrupt_flag(atomic_bool * flag) : flet(g_interrupt_flag, flag) {}
void reset_interrupt() {
get_g_interrupt().store(false);
}
bool interrupt_requested() {
return get_g_interrupt().load();
static bool interrupt_requested() {
return g_interrupt_flag && g_interrupt_flag->load();
}
void check_interrupted() {
if (interrupt_requested() && !std::uncaught_exception()) {
reset_interrupt();
throw interrupted();
}
}
@ -75,38 +68,16 @@ void sleep_for(unsigned ms, unsigned step_ms) {
check_interrupted();
}
atomic_bool * get_interrupt_flag() {
return &get_g_interrupt();
}
atomic_bool * interruptible_thread::get_flag_addr() {
return &get_g_interrupt();
}
bool interruptible_thread::interrupted() const {
atomic_bool * f = m_flag_addr.load();
if (f == nullptr)
return false;
return f->load();
return m_flag.load();
}
void interruptible_thread::request_interrupt(unsigned try_ms) {
while (true) {
atomic_bool * f = m_flag_addr.load();
if (f != nullptr) {
f->store(true);
return;
}
this_thread::sleep_for(chrono::milliseconds(try_ms));
check_interrupted();
}
void interruptible_thread::request_interrupt() {
m_flag.store(true);
}
void interruptible_thread::join() {
m_thread.join();
}
bool interruptible_thread::joinable() {
return m_thread.joinable();
}
}

View file

@ -10,6 +10,7 @@ Author: Leonardo de Moura
#include "util/thread.h"
#include "util/stackinfo.h"
#include "util/exception.h"
#include "flet.h"
namespace lean {
/** \brief Increment thread local counter for approximating elapsed time. */
@ -42,21 +43,9 @@ public:
void check_heartbeat();
atomic_bool * get_interrupt_flag();
/**
\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();
struct scoped_interrupt_flag : flet<atomic_bool *> {
scoped_interrupt_flag(atomic_bool *);
};
/**
\brief Throw an interrupted exception if the (interrupt) flag is set.
@ -82,38 +71,16 @@ void sleep_for(unsigned ms, unsigned step_ms = g_small_sleep);
*/
class interruptible_thread {
public:
#if !defined(LEAN_USE_BOOST)
template<typename Function, typename... Args>
interruptible_thread(Function && fun, Args &&... args):
m_flag_addr(nullptr),
m_thread(
[&](Function&& fun, Args&&... args) {
m_flag_addr.store(get_flag_addr());
template<typename Function>
interruptible_thread(Function && fun):
m_thread([&, fun]() {
save_stack_info(false);
fun(std::forward<Args>(args)...);
m_flag_addr.store(&m_dummy_addr); // see comment before m_dummy_addr
scoped_interrupt_flag scope_int_flag(&m_flag);
fun();
run_thread_finalizers();
run_post_thread_finalizers();
},
std::forward<Function>(fun),
std::forward<Args>(args)...)
})
{}
#else
// Simpler version that works with Boost, and set stack size
private:
std::function<void()> 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<typename Function>
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.
@ -122,35 +89,13 @@ public:
/**
\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 request_interrupt();
void join();
bool joinable();
private:
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
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();
atomic_bool m_flag;
lthread m_thread;
};
#if !defined(LEAN_MULTI_THREAD)

221
src/util/log_tree.cpp Normal file
View file

@ -0,0 +1,221 @@
#include "log_tree.h"
namespace lean {
void log_tree::notify_core(std::vector<log_tree::event> const & events) {
if (events.empty()) return;
for (auto & l : m_listeners)
l(events);
}
void log_tree::add_listener(log_tree::listener const & l) {
unique_lock<mutex> lock(m_mutex);
m_listeners.push_back(l);
}
log_tree::log_tree() {
auto cell = new node_cell;
cell->m_tree = this;
m_root = node(cell);
}
log_tree::~log_tree() {
std::vector<event> evs;
m_root.detach_core(evs);
m_root = node();
}
void log_tree::print_to(std::ostream & out) const {
get_root().print_to(out, 0);
}
void log_tree::print() const {
print_to(std::cerr);
};
void log_tree::node::detach_core(std::vector<log_tree::event> & events) const {
if (m_ptr->m_detached) return;
m_ptr->m_detached = true;
m_ptr->m_children.for_each([&] (name const &, node const & c) { c.detach_core(events); });
for (auto & e : m_ptr->m_entries) events.push_back({ event::EntryRemoved, m_ptr, e });
if (m_ptr->m_producer) events.push_back({event::Finished, m_ptr, {}});
}
void log_tree::node::notify(std::vector<log_tree::event> const & events, unique_lock<mutex> & l) const {
if (!m_ptr->m_detached) {
l.unlock();
m_ptr->m_tree->notify_core(events);
}
}
log_tree::node log_tree::node::clone_core() const {
auto copy = node(new node_cell);
copy.m_ptr->m_children = m_ptr->m_children;
m_ptr->m_children.clear();
copy.m_ptr->m_tree = m_ptr->m_tree;
copy.m_ptr->m_detached = m_ptr->m_detached;
copy.m_ptr->m_entries = m_ptr->m_entries;
m_ptr->m_detached = true;
return std::move(copy);
}
void log_tree::node::clear_entries() const {
auto l = lock();
std::vector<event> events;
for (auto & e : m_ptr->m_entries) {
events.push_back({event::EntryRemoved, *this, std::move(e)});
}
m_ptr->m_entries.clear();
notify(events, l);
}
void log_tree::node::add(log_entry const & entry) const {
auto l = lock();
m_ptr->m_entries.push_back(entry);
notify({{event::EntryAdded, *this, entry}}, l);
}
log_tree::node log_tree::node::mk_child(name n, std::string const & description, location const & loc,
bool overwrite) {
auto l = lock();
std::vector<event> events;
if (!overwrite && (n.is_anonymous() || m_ptr->m_used_names.contains(n))) {
for (unsigned i = 0;; i++) {
name n_(n, i);
if (!m_ptr->m_used_names.contains(n_)) {
n = n_;
break;
}
}
}
m_ptr->m_used_names.insert(n);
node child;
if (auto existing = m_ptr->m_children.find(n)) {
child = existing->clone_core();
existing->detach_core(events);
} else {
child = node(new node_cell);
child.m_ptr->m_tree = m_ptr->m_tree;
child.m_ptr->m_detached = m_ptr->m_detached;
}
child.m_ptr->m_description = description;
child.m_ptr->m_location = loc;
m_ptr->m_children.insert(n, child);
notify(events, l);
return child;
}
void log_tree::node::set_producer(gtask const & prod) {
auto l = lock();
m_ptr->m_producer = prod;
notify({{event::ProducerSet, *this, {}}}, l);
}
void log_tree::node::finish() const {
auto l = lock();
std::vector<event> events;
m_ptr->m_producer.reset();
buffer<pair<name, node>> to_delete;
m_ptr->m_children.for_each([&] (name const & n, node const & c) {
if (!m_ptr->m_used_names.contains(n))
to_delete.emplace_back(n, c);
});
for (auto & c : to_delete) {
m_ptr->m_children.erase(c.first);
c.second.detach_core(events);
}
events.push_back({event::Finished, *this, {}});
notify(events, l);
}
std::vector<log_entry> log_tree::node::get_entries() const {
auto l = lock();
return m_ptr->m_entries;
}
name_map<lean::log_tree::node> log_tree::node::get_children() const {
auto l = lock();
return m_ptr->m_children;
}
void log_tree::node::reuse(name const & n) const {
auto l = lock();
m_ptr->m_used_names.insert(n);
}
name_set log_tree::node::get_used_names() const {
auto l = lock();
return m_ptr->m_used_names;
}
gtask log_tree::node::get_producer() const {
auto l = lock();
return m_ptr->m_producer;
}
void log_tree::node::print() const {
print_to(std::cerr, 0);
}
void log_tree::node::print_to(std::ostream & out, unsigned indent) const {
auto l = lock();
auto begin = m_ptr->m_location.m_range.m_begin, end = m_ptr->m_location.m_range.m_end;
out << m_ptr->m_location.m_file_name
<< ": " << begin.first << ":" << begin.second << " -- " << end.first << ":" << end.second << ": "
<< m_ptr->m_description << " ("
<< m_ptr->m_entries.size()
<< " entries, producer = "
<< std::hex << reinterpret_cast<uintptr_t>(m_ptr->m_producer.get()) << std::dec
<< ")" << std::endl;
auto children = m_ptr->m_children;
auto used_names = m_ptr->m_used_names;
l.unlock();
indent += 2;
children.for_each([&] (name const & n, log_tree::node const & c) {
for (unsigned i = 0; i < indent; i++) out << ' ';
out << n;
if (!used_names.contains(n))
out << " (unused)";
out << ": ";
c.print_to(out, indent);
});
}
LEAN_THREAD_PTR(log_tree::node, g_log_tree);
scope_log_tree_core::scope_log_tree_core(log_tree::node * lt) : flet<log_tree::node *>(g_log_tree, lt) {}
log_tree::node & logtree() {
if (g_log_tree) {
return *g_log_tree;
} else {
throw exception("no log tree in scope");
}
}
scope_log_tree::scope_log_tree(log_tree::node const & lt) : m_node(lt), m_scope(&m_node) {
m_node.clear_entries();
}
scope_log_tree::~scope_log_tree() {
logtree().finish();
}
scope_log_tree::scope_log_tree(std::string const & desc, pos_range const & pos) :
scope_log_tree(logtree().mk_child({}, desc, {logtree().get_location().m_file_name, pos})) {}
scope_log_tree::scope_log_tree(std::string const & desc) :
scope_log_tree(logtree().mk_child({}, desc, logtree().get_location())) {}
scope_log_tree::scope_log_tree() : scope_log_tree(std::string()) {}
}

135
src/util/log_tree.h Normal file
View file

@ -0,0 +1,135 @@
#pragma once
#include <vector>
#include <functional>
#include <memory>
#include "util/thread.h"
#include "util/int64.h"
#include "name_map.h"
#include "task.h"
#include "message_definitions.h"
#include "name_set.h"
#include "flet.h"
namespace lean {
struct log_entry_cell {
virtual ~log_entry_cell() {}
};
using log_entry = std::shared_ptr<log_entry_cell const>;
class log_tree {
public:
class node;
struct event;
using listener = std::function<void(std::vector<event> const &)>;
struct node_cell {
MK_LEAN_RC()
void dealloc() { delete this; }
name_map<node> m_children;
name_set m_used_names;
log_tree * m_tree = nullptr;
bool m_detached = false;
std::vector<log_entry> m_entries;
location m_location;
std::string m_description;
gtask m_producer;
node_cell() : m_rc(0) {}
};
class node {
friend class log_tree;
node_cell * m_ptr;
void detach_core(std::vector<event> & events) const;
void notify(std::vector<event> const & events, unique_lock<mutex> & lock) const;
node clone_core() const;
unique_lock<mutex> lock() const { return unique_lock<mutex>(m_ptr->m_tree->m_mutex); }
node(node_cell * ptr) : m_ptr(ptr) { if (m_ptr) m_ptr->inc_ref(); }
public:
node() : m_ptr(nullptr) {}
node(node const & n) : node(n.m_ptr) {}
node(node && n) : m_ptr(n.m_ptr) { n.m_ptr = nullptr; }
~node() { if (m_ptr) m_ptr->dec_ref(); }
node & operator=(node const & n) { LEAN_COPY_REF(n); }
node & operator=(node && n) { LEAN_MOVE_REF(n); }
void clear_entries() const;
void add(log_entry const & entry) const;
std::vector<log_entry> get_entries() const;
name_map<node> get_children() const;
node mk_child(name n, std::string const & description, location const & loc, bool overwrite = false);
void reuse(name const & n) const;
void finish() const;
void set_producer(gtask const &);
location const & get_location() const { return m_ptr->m_location; }
std::string const & get_description() const { return m_ptr->m_description; }
name_set get_used_names() const;
gtask get_producer() const;
void print_to(std::ostream &, unsigned) const;
void print() const;
operator bool() const { return m_ptr != nullptr; }
};
struct event {
enum { ProducerSet, EntryAdded, EntryRemoved, Finished } m_kind;
node m_node;
log_entry m_entry;
};
private:
mutex m_mutex;
std::vector<listener> m_listeners;
node m_root;
void notify_core(std::vector<event> const & events);
public:
log_tree();
~log_tree();
node get_root() const { return m_root; }
void print_to(std::ostream &) const;
void print() const;
void add_listener(listener const & l);
};
log_tree::node & logtree();
struct scope_log_tree_core : flet<log_tree::node *> {
scope_log_tree_core(log_tree::node * lt);
};
class scope_log_tree {
log_tree::node m_node;
scope_log_tree_core m_scope;
public:
scope_log_tree(log_tree::node const & lt);
scope_log_tree();
scope_log_tree(std::string const & desc);
scope_log_tree(std::string const & desc, pos_range const & pos);
~scope_log_tree();
log_tree::node & get() { return m_node; }
};
}

View file

@ -13,14 +13,13 @@ namespace lean {
typedef pair<unsigned, unsigned> pos_info; //!< Line and column information
typedef uint64 period;
struct pos_range {
pos_info m_begin, m_end;
};
struct message_bucket_id {
message_bucket_id() {}
message_bucket_id(name const & bucket, period version) : m_bucket(bucket), m_version(version) {}
name m_bucket;
period m_version = 0;
struct location {
std::string m_file_name;
pos_range m_range;
};
}

84
src/util/task.cpp Normal file
View file

@ -0,0 +1,84 @@
/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#include "util/task.h"
namespace lean {
gtask mk_gtask(std::unique_ptr<gtask_imp> && imp, task_flags flags) {
flags.m_needs_execution = false;
return std::make_shared<gtask_cell>(gtask_cell::called_from_friend(), imp.release(), flags);
}
void gtask_cell::cancel(std::shared_ptr<cancellable> const & self) {
if (auto self_task = std::dynamic_pointer_cast<gtask_cell>(self)) {
taskq().fail_and_dispose(self_task);
}
}
void task_queue::wait_for_success(gtask const & t) {
while (true) {
switch (t->m_state.load()) {
case task_state::Success:
return; // OK
case task_state::Failed:
std::rethrow_exception(t->m_exception);
default:
wait_for_finish(t);
}
}
}
void task_queue::execute(gtask const & t) {
lean_assert(t);
lean_assert(t->m_state.load() == task_state::Running);
lean_assert(t->m_data);
lean_assert(t->m_data->m_imp);
try {
{
buffer<gtask> deps;
t->m_data->m_imp->get_dependencies(deps);
if (t->m_data->m_flags.m_propagate_errors_from_dependencies) {
for (auto & dep : deps) if (dep) wait_for_success(dep);
} else {
for (auto & dep : deps) if (dep) wait_for_finish(dep);
}
}
t->execute();
t->m_state = task_state::Success;
} catch (...) {
t->m_exception = std::current_exception();
t->m_state = task_state::Failed;
}
}
void task_queue::fail(gtask const & t, std::exception_ptr const & ex) {
lean_assert(t->m_state.load() < task_state::Running);
t->m_exception = ex;
t->m_state = task_state::Failed;
}
void task_queue::fail(gtask const & t, gtask const & failed) {
lean_assert(failed->m_state.load() == task_state::Failed);
fail(t, failed->m_exception);
}
static task_queue * g_taskq = nullptr;
void set_task_queue(task_queue * q) {
if (g_taskq) throw exception("cannot set task queue twice");
g_taskq = q;
}
task_queue & taskq() {
return *g_taskq;
}
}

167
src/util/task.h Normal file
View file

@ -0,0 +1,167 @@
/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#pragma once
#include "util/buffer.h"
#include "util/thread.h"
#include "util/cancellable.h"
#include <memory>
namespace lean {
class gtask_cell;
using gtask = std::shared_ptr<gtask_cell>;
using weak_gtask = std::weak_ptr<gtask_cell>;
template <class Res> class task_cell;
template <class Res> using task = std::shared_ptr<task_cell<Res>>;
class task_queue;
struct scheduling_info {
virtual ~scheduling_info() {}
};
struct task_flags {
bool m_do_priority_inversion = true;
bool m_propagate_errors_from_dependencies = true;
bool m_needs_separate_thread = true;
bool m_needs_execution = true;
};
using task_dep_fn = std::function<void(buffer<gtask>&)>;
struct gtask_imp {
gtask_imp() {}
virtual ~gtask_imp() {}
virtual void get_dependencies(buffer<gtask> &) {}
virtual void execute(void * result) = 0;
};
struct gtask_data {
std::unique_ptr<gtask_imp> m_imp;
std::unique_ptr<scheduling_info> m_sched_info;
task_flags m_flags;
gtask_data(gtask_imp * imp, task_flags flags) : m_imp(imp), m_flags(flags) {}
};
enum class task_state {
Created = 0,
Waiting = 1,
Queued = 2,
Running = 3,
Failed = 4,
Success = 5,
};
class gtask_cell : public cancellable {
friend class task_queue;
template <class Res> friend class task_cell;
friend gtask mk_gtask(std::unique_ptr<gtask_imp> &&, task_flags);
template <class Res> friend task<Res> mk_task(std::unique_ptr<gtask_imp> &&, task_flags);
virtual void execute() {};
gtask_cell(task_state state) : m_state(state) {}
atomic<task_state> m_state;
std::unique_ptr<gtask_data> m_data;
std::exception_ptr m_exception;
struct called_from_friend {};
public:
gtask_cell(called_from_friend, gtask_imp * imp, task_flags flags) : m_state(task_state::Created) {
m_data.reset(new gtask_data(imp, flags));
}
void cancel(std::shared_ptr<cancellable> const &self) override;
virtual ~gtask_cell() {}
};
gtask mk_gtask(std::unique_ptr<gtask_imp> && imp, task_flags flags);
template <class Res>
class task_cell : public gtask_cell {
friend class task_queue;
optional<Res> m_result;
void execute() override {
m_result = optional<Res>(Res());
m_data->m_imp->execute(&*m_result);
}
public:
task_cell(called_from_friend cap, gtask_imp * imp, task_flags flags) : gtask_cell(cap, imp, flags) {}
task_cell(Res const & res) : gtask_cell(task_state::Success), m_result(res) {}
task_cell(Res && res) : gtask_cell(task_state::Success), m_result(res) {}
task_cell(std::exception_ptr const & ex) : gtask_cell(task_state::Failed) { m_exception = ex; }
optional<Res> peek() {
if (m_state.load() == task_state::Success) {
return m_result;
} else {
return optional<Res>();
}
}
};
template <class Res>
task<Res> mk_task(std::unique_ptr<gtask_imp> && imp, task_flags flags) {
flags.m_needs_execution = true;
return std::make_shared<task_cell<Res>>(gtask_cell::called_from_friend(), imp.release(), flags);
}
struct cancellation_exception {};
class task_queue {
protected:
atomic<task_state> & get_state(gtask const & t) const { return t->m_state; }
gtask_data * get_data(gtask const & t) const { return t->m_data.get(); }
void clear(gtask const & t) const { t->m_data.reset(nullptr); }
void execute(gtask const & t);
void fail(gtask const & t, std::exception_ptr const & ex);
void fail(gtask const & t, gtask const & failed);
public:
virtual ~task_queue() {}
virtual void wait_for_finish(gtask const &) = 0;
void wait_for_success(gtask const & t);
template <class Res>
Res const & get(task<Res> const & t) {
wait_for_success(t);
return *t->m_result;
}
virtual void fail_and_dispose(gtask const &) = 0;
virtual void evacuate() = 0;
virtual void join() = 0;
virtual void submit(gtask const &) = 0;
};
void set_task_queue(task_queue *);
task_queue & taskq();
template <class Res>
Res const & get(task<Res> const & t) {
return taskq().get(t);
}
template <class Res>
optional<Res> peek(task<Res> const & t) {
return t->peek();
}
}

45
src/util/task_builder.cpp Normal file
View file

@ -0,0 +1,45 @@
/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#include "util/task_builder.h"
namespace lean {
void test() {
task<int> a, b;
task<int> c = task_builder<int>([] () { return 5; }).build();
task<int> d = map<int>(a, [] (int x) { return x + 1; }).build();
task<int> e = map<int>(a, [] (int x) { return x + 1; }).build();
auto f = map<unsigned>(a, [] (int x) { return x + 1; }).build();
task<std::pair<task<unsigned>, task<char>>> g;
auto h = mk_deep_dependency(g, [] (buffer<gtask> & deps, std::pair<task<unsigned>, task<char>> const & val) {
deps.push_back(val.first);
deps.push_back(val.second);
});
}
struct cancellable_task_imp : public delegating_task_imp {
cancellation_token m_ctok;
cancellable_task_imp(std::unique_ptr<gtask_imp> && base, cancellation_token const & ctok) :
delegating_task_imp(std::forward<std::unique_ptr<gtask_imp>>(base)), m_ctok(ctok) {}
~cancellable_task_imp() { m_ctok->gc(); }
void execute(void * result) override {
scope_cancellation_token scope_cancel_tok(&m_ctok);
m_base->execute(result);
}
};
std::unique_ptr<gtask_imp> cancellation_support::operator()(std::unique_ptr<gtask_imp> && base) {
return std::unique_ptr<gtask_imp>(new cancellable_task_imp(
std::forward<std::unique_ptr<gtask_imp>>(base), m_ctok));
}
}

192
src/util/task_builder.h Normal file
View file

@ -0,0 +1,192 @@
/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#pragma once
#include "util/task.h"
#include "util/unit.h"
#include "util/list.h"
#include "util/cancellable.h"
#include <vector>
#include <type_traits>
namespace lean {
template <class Res> task<Res> mk_pure_task(Res && res) { return std::make_shared<task_cell<Res>>(res); }
template <class Res> task<Res> mk_pure_task(Res const & res) { return std::make_shared<task_cell<Res>>(res); }
struct delegating_task_imp : public gtask_imp {
std::unique_ptr<gtask_imp> m_base;
delegating_task_imp(std::unique_ptr<gtask_imp> && base) : m_base(std::forward<std::unique_ptr<gtask_imp>>(base)) {}
void get_dependencies(buffer<gtask> & deps) override { m_base->get_dependencies(deps); }
void execute(void * result) override { return m_base->execute(result); }
};
struct cancellation_support {
cancellation_token m_ctok;
cancellation_support(cancellation_token const & ctok) : m_ctok(ctok) {}
std::unique_ptr<gtask_imp> operator()(std::unique_ptr<gtask_imp> &&);
};
template <class Fn>
struct depends_on_fn_wrapper : public delegating_task_imp {
Fn m_fn;
depends_on_fn_wrapper(std::unique_ptr<gtask_imp> && base, Fn && fn) :
delegating_task_imp(std::forward<std::unique_ptr<gtask_imp>>(base)), m_fn(std::forward<Fn>(fn)) {}
void get_dependencies(buffer<gtask> & deps) override {
m_fn(deps);
delegating_task_imp::get_dependencies(deps);
}
};
template <class Res>
class task_builder {
std::unique_ptr<gtask_imp> m_imp;
task_flags m_flags;
cancellation_token m_cancel_tok;
template <class Fn>
struct base_task_imp : public gtask_imp {
Fn m_fn;
base_task_imp(Fn && fn) : m_fn(fn) {}
void execute(void * result) override {
*static_cast<Res *>(result) = m_fn();
}
};
public:
task_builder() : task_builder([] { return Res(); }) {}
template <class Fn> task_builder(Fn && fn) :
m_imp(new base_task_imp<Fn>(std::forward<Fn>(fn))),
m_cancel_tok(global_cancellation_token()) {}
task_builder<Res> disable_priority_inversion() {
lean_assert(m_imp);
m_flags.m_do_priority_inversion = false;
return std::move(*this);
}
task_builder<Res> ignore_dependency_errors() {
lean_assert(m_imp);
m_flags.m_propagate_errors_from_dependencies = false;
return std::move(*this);
}
task_builder<Res> does_not_require_own_thread() {
lean_assert(m_imp);
m_flags.m_needs_separate_thread = false;
return std::move(*this);
}
template <class Wrapper>
task_builder<Res> wrap(Wrapper && wrapper) {
lean_assert(m_imp);
m_imp = wrapper(std::move(m_imp));
lean_assert(m_imp);
return std::move(*this);
}
task_builder<Res> set_cancellation_token(cancellation_token const & ctok) {
lean_assert(m_imp);
m_cancel_tok = ctok;
return std::move(*this);
}
template <class Fn>
task_builder<Res> depends_on_fn(Fn && fn) {
lean_assert(m_imp);
m_imp.reset(new depends_on_fn_wrapper<Fn>(std::move(m_imp), std::forward<Fn>(fn)));
lean_assert(m_imp);
return std::move(*this);
}
task_builder<Res> depends_on(gtask const & t) {
lean_assert(m_imp);
return depends_on_fn([t] (buffer<gtask> & deps) { deps.push_back(t); });
}
template <class Task>
task_builder<Res> depends_on(std::vector<Task> && ts) {
lean_assert(m_imp);
return depends_on_fn([ts] (buffer<gtask> & deps) {
for (auto & t : ts) deps.push_back(t);
});
}
template <class Task>
task_builder<Res> depends_on(std::vector<Task> const & ts) {
lean_assert(m_imp);
std::vector<Task> ts_ = ts;
return depends_on(std::move(ts_));
}
template <class Task>
task_builder<Res> depends_on(list<Task> const & ts) {
lean_assert(m_imp);
return depends_on_fn([ts] (buffer<gtask> & deps) {
for (auto & t : ts) deps.push_back(t);
});
}
gtask build_dep_task() { return mk_gtask(std::move(m_imp), m_flags); }
task<Res> build() {
auto ctok = mk_cancellation_token(m_cancel_tok);
auto cancellable = wrap(cancellation_support(ctok));
auto task = mk_task<Res>(std::move(cancellable.m_imp), cancellable.m_flags);
ctok->add_child(task);
return task;
}
};
template <class Res, class Fn, class Arg>
task_builder<Res> map(task<Arg> const & arg, Fn && fn) {
return std::move(task_builder<Res>([arg, fn] { return fn(get(arg)); }).depends_on(arg));
};
template <class Res>
task<std::vector<Res>> traverse(std::vector<task<Res>> const & ts) {
return task_builder<std::vector<Res>>([ts] {
std::vector<Res> vs;
for (auto &t : ts) vs.push_back(get(t));
return std::move(vs);
}).depends_on_fn([ts](buffer<gtask> & deps) {
for (auto &t : ts) deps.push_back(t);
}).does_not_require_own_thread().build();
}
template <class Res>
task<list<Res>> reverse_traverse(list<task<Res>> const & ts) {
return task_builder<list<Res>>([ts] {
list<Res> vs;
for (auto & t : ts) vs = cons(get(t), vs);
return std::move(vs);
}).depends_on_fn([ts](buffer<gtask> & deps) {
for (auto & t : ts) deps.push_back(t);
}).does_not_require_own_thread().build();
}
template <class Fn>
gtask mk_dependency_task(Fn && fn) {
return task_builder<unit>().depends_on_fn(std::forward<Fn>(fn)).build_dep_task();
}
template <class Arg, class Fn>
gtask mk_deep_dependency(task<Arg> const & arg, Fn && fn) {
return mk_dependency_task([fn, arg] (buffer<gtask> & deps) {
deps.push_back(arg);
if (auto v = peek(arg)) {
fn(deps, *v);
}
});
};
}

View file

@ -1,72 +0,0 @@
/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#include <string>
#include "util/task_queue.h"
namespace lean {
std::string generic_task::description() const {
std::ostringstream out;
description(out);
return out.str();
}
generic_task::generic_task() : m_mod(get_current_module()), m_pos(get_current_task_pos()) {}
generic_task_result_cell::generic_task_result_cell(generic_task * t) :
m_rc(0), m_task(t), m_desc(t->description()) {}
void generic_task_result_cell::clear_task() {
if (m_task) {
delete m_task;
m_task = nullptr;
}
}
LEAN_THREAD_PTR(task_queue, g_tq);
scope_global_task_queue::scope_global_task_queue(task_queue * tq) {
m_old_tq = g_tq;
g_tq = tq;
}
scope_global_task_queue::~scope_global_task_queue() {
g_tq = m_old_tq;
}
task_queue * get_global_task_queue() {
return g_tq;
}
task_cancellation_exception::task_cancellation_exception(generic_task_result const & cancelled_task) {
std::ostringstream out;
if (cancelled_task) {
out << "task cancelled: " << cancelled_task.description();
} else {
out << "task cancelled";
}
m_msg = out.str();
}
char const *task_cancellation_exception::what() const noexcept {
return m_msg.c_str();
}
LEAN_THREAD_PTR(module_id, g_cur_mod);
LEAN_THREAD_PTR(pos_info, g_cur_task_pos);
scoped_task_context::scoped_task_context(module_id const & mod, pos_info const & pos) : m_id(mod), m_pos(pos) {
m_old_id = g_cur_mod;
m_old_pos = g_cur_task_pos;
g_cur_mod = &m_id;
g_cur_task_pos = &m_pos;
}
scoped_task_context::~scoped_task_context() {
g_cur_mod = m_old_id;
g_cur_task_pos = m_old_pos;
}
module_id get_current_module() { return *g_cur_mod; }
pos_info get_current_task_pos() { return *g_cur_task_pos; }
}

View file

@ -1,296 +0,0 @@
/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#pragma once
#include <sstream>
#include <string>
#include <vector>
#include <unordered_set>
#include "util/thread.h"
#include "util/optional.h"
#include "util/rc.h"
#include "util/message_definitions.h"
namespace lean {
enum class task_result_state {
CREATED,
QUEUED, WAITING,
EXECUTING,
FINISHED, FAILED
};
class generic_task;
struct generic_task_result_cell {
MK_LEAN_RC()
void dealloc() { delete this; }
generic_task * m_task = nullptr;
atomic<task_result_state> m_state { task_result_state::CREATED };
std::string m_desc;
std::exception_ptr m_ex;
virtual ~generic_task_result_cell() { clear_task(); }
void clear_task();
generic_task_result_cell(generic_task * t);
generic_task_result_cell(std::string const & desc) :
m_rc(0), m_state(task_result_state::FINISHED), m_desc(desc) {}
bool has_evaluated() const {
return m_state.load() > task_result_state::EXECUTING;
}
virtual void execute_and_store_result() = 0;
};
class generic_task_result {
friend class task_queue;
template <class T> friend class task_result;
generic_task_result_cell * m_ptr = nullptr;
public:
generic_task_result(generic_task_result_cell * t) : m_ptr(t) { if (t) t->inc_ref(); }
generic_task_result() {}
generic_task_result(generic_task_result && t) : m_ptr(t.m_ptr) { t.m_ptr = nullptr; }
generic_task_result(generic_task_result const & t) : m_ptr(t.m_ptr) { if (m_ptr) m_ptr->inc_ref(); }
~generic_task_result() { if (m_ptr) m_ptr->dec_ref(); m_ptr = nullptr; }
generic_task_result & operator=(generic_task_result const & t) { LEAN_COPY_REF(t); }
generic_task_result & operator=(generic_task_result && t) { LEAN_MOVE_REF(t); }
bool operator==(generic_task_result const & t) const { return m_ptr == t.m_ptr; }
bool operator!=(generic_task_result const & t) const { return !(*this == t); }
operator bool() const { return m_ptr != nullptr; }
struct hash {
size_t operator()(generic_task_result const & t) const {
return std::hash<generic_task_result_cell *>()(t.m_ptr);
}
};
std::string description() const { return m_ptr->m_desc; }
void cancel() const;
void reset() { *this = nullptr; }
};
struct task_priority {
unsigned m_prio = static_cast<unsigned>(-1);
optional<chrono::steady_clock::time_point> m_not_before;
bool operator<(task_priority const & p) const {
if (m_prio < p.m_prio) return true;
if (m_not_before && p.m_not_before && *m_not_before < *p.m_not_before) return true;
if (!m_not_before && p.m_not_before) return true;
return false;
}
void bump(task_priority const & p) {
if (p.m_prio < m_prio) m_prio = p.m_prio;
if (m_not_before && p.m_not_before && *p.m_not_before < *m_not_before)
*m_not_before = *p.m_not_before;
}
};
typedef std::string module_id;
enum class task_kind { parse, elab, print };
module_id get_current_module();
pos_info get_current_task_pos();
class scoped_task_context {
module_id * m_old_id;
pos_info * m_old_pos;
module_id m_id;
pos_info m_pos;
public:
scoped_task_context(module_id const & mod, pos_info const & pos);
~scoped_task_context();
};
struct task_scheduling_data {
task_priority m_prio;
std::vector<generic_task_result> m_reverse_deps;
condition_variable m_has_finished;
};
class generic_task {
friend class task_queue;
task_scheduling_data m_data;
// metadata
message_bucket_id m_bucket;
module_id m_mod;
pos_info m_pos;
public:
generic_task();
virtual ~generic_task() {}
virtual void description(std::ostream &) const = 0;
std::string description() const;
virtual std::vector<generic_task_result> get_dependencies() { return {}; }
virtual bool is_tiny() const { return false; }
virtual bool do_priority_inversion() const { return true; }
virtual task_kind get_kind() const { return task_kind::elab; }
virtual pos_info get_pos() const { return get_task_pos(); }
virtual pos_info get_end_pos() const { return get_pos(); }
message_bucket_id const & get_bucket() const { return m_bucket; }
period get_version() const { return m_bucket.m_version; }
module_id const & get_module_id() const { return m_mod; }
pos_info const & get_task_pos() const { return m_pos; }
};
template <class T>
class task : public generic_task {
public:
typedef T result;
virtual ~task() {}
virtual T execute() = 0;
};
template <class T>
struct task_result_cell : public generic_task_result_cell {
optional<T> m_result;
task<T> * get_ptr() { return static_cast<task<T> *>(m_task); }
virtual void execute_and_store_result() override {
m_result = { get_ptr()->execute() };
}
task_result_cell(task<T> * t) : generic_task_result_cell(t) {}
task_result_cell(T const & t, std::string const & desc) :
generic_task_result_cell(desc), m_result(t) {}
};
template <class T>
class task_result : public generic_task_result {
friend class task_queue;
optional<T> const & get_current_result() const { return static_cast<task_result_cell<T> *>(m_ptr)->m_result; }
public:
task_result(task_result_cell<T> * t) : generic_task_result(t) {}
task_result() : generic_task_result() {}
task_result(task_result<T> const & t) : generic_task_result(t) {}
task_result(task_result<T> && t) : generic_task_result(t) {}
task_result<T> & operator=(task_result<T> const & t) { LEAN_COPY_REF(t); }
task_result<T> & operator=(task_result<T> && t) { LEAN_MOVE_REF(t); }
T const & get() const;
optional<T> peek() const {
if (m_ptr->m_state.load() == task_result_state::FINISHED) {
return get_current_result();
} else {
return optional<T>();
}
}
};
template <class T>
task_result<T> mk_pure_task_result(T const & t, std::string const & desc) {
return task_result<T>(new task_result_cell<T>(t, desc));
}
class task_cancellation_exception : public std::exception {
std::string m_msg;
public:
task_cancellation_exception() : task_cancellation_exception(generic_task_result()) {}
task_cancellation_exception(generic_task_result const & cancelled_task);
char const * what() const noexcept override;
};
class task_queue {
virtual void prepare_task(generic_task_result const &) = 0;
protected:
task_queue() {}
// Friendship forwarding.
generic_task_result_cell * unwrap(generic_task_result const & tr) const { return tr.m_ptr; }
task_scheduling_data & get_data(generic_task * t) const { return t->m_data; }
task_scheduling_data & get_data(generic_task_result const & tr) const { return get_data(unwrap(tr)->m_task); }
void set_bucket(generic_task_result const & tr, message_bucket_id const & id) const {
tr.m_ptr->m_task->m_bucket = id;
}
public:
virtual ~task_queue() {}
virtual optional<generic_task_result> get_current_task() = 0;
virtual bool empty() = 0;
virtual void join() = 0;
virtual void submit(generic_task_result const &) = 0;
template <typename T, typename... As>
task_result<typename T::result> mk_lazy_task(As... args) {
task_result<typename T::result> task(
new task_result_cell<typename T::result>(
new T(std::forward<As>(args)...)));
prepare_task(task);
return task;
}
template <typename T, typename... As>
task_result<typename T::result> submit(As... args) {
task_result<typename T::result> task =
mk_lazy_task<T, As...>(std::forward<As>(args)...);
submit(task);
return task;
}
template <typename T>
T const & get_result(task_result<T> const & t) {
while (true) {
switch (unwrap(t)->m_state.load()) {
case task_result_state::FINISHED:
return *t.get_current_result();
case task_result_state::FAILED:
std::rethrow_exception(unwrap(t)->m_ex);
default:
wait(t);
}
}
}
virtual void wait(generic_task_result const & t) = 0;
virtual void cancel(generic_task_result const & t) = 0;
virtual void cancel_if(std::function<bool(generic_task *)> const & pred) = 0; // NOLINT
using progress_cb = std::function<void(generic_task *)>; // NOLINT
// disabling lint because it this this is cast ^^^
virtual void set_progress_callback(progress_cb const &) = 0;
};
class scope_global_task_queue {
task_queue * m_old_tq;
public:
scope_global_task_queue(task_queue * tq);
~scope_global_task_queue();
};
task_queue * get_global_task_queue();
template <class T>
T const & task_result<T>::get() const {
return get_global_task_queue()->get_result(*this);
}
inline void generic_task_result::cancel() const {
get_global_task_queue()->cancel(*this);
}
}

13
src/util/unit.h Normal file
View file

@ -0,0 +1,13 @@
/*
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Gabriel Ebner
*/
#pragma once
namespace lean {
struct unit {};
}