refactor(*): task<T>, log_tree, cancellation_token
This commit is contained in:
parent
6887a99f08
commit
595cbb8fe9
60 changed files with 2080 additions and 2302 deletions
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 (...) {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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> &);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
33
src/library/library_task_builder.cpp
Normal file
33
src/library/library_task_builder.cpp
Normal 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));
|
||||
}
|
||||
|
||||
}
|
||||
41
src/library/library_task_builder.h
Normal file
41
src/library/library_task_builder.h
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 &);
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 &);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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), {} });
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
58
src/util/cancellable.cpp
Normal 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
53
src/util/cancellable.h
Normal 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();
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
221
src/util/log_tree.cpp
Normal 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
135
src/util/log_tree.h
Normal 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; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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
84
src/util/task.cpp
Normal 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
167
src/util/task.h
Normal 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
45
src/util/task_builder.cpp
Normal 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
192
src/util/task_builder.h
Normal 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);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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; }
|
||||
|
||||
}
|
||||
|
|
@ -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
13
src/util/unit.h
Normal 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 {};
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue