From 2905180c48e4b8974d4dee1949a00fc8e7bcafc6 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 5 Apr 2023 08:07:47 +0200 Subject: Allow creating context with bare minimum of initializations This is used by bpkg to detect forwarded configurations without incurring the full context creation overhead. --- libbuild2/adhoc-rule-cxx.cxx | 2 +- libbuild2/algorithm.cxx | 50 ++++++++++++++--------------- libbuild2/algorithm.ixx | 6 ++-- libbuild2/cc/compile-rule.cxx | 8 ++--- libbuild2/cc/link-rule.cxx | 4 +-- libbuild2/context.cxx | 68 +++++++++++++++++++++++++++++++--------- libbuild2/context.hxx | 17 +++++++--- libbuild2/context.ixx | 2 +- libbuild2/diagnostics.cxx | 14 ++++----- libbuild2/module.cxx | 12 +++---- libbuild2/operation.cxx | 8 ++--- libbuild2/script/run.cxx | 2 +- libbuild2/test/rule.cxx | 32 +++++++++---------- libbuild2/test/script/parser.cxx | 26 +++++++-------- libbuild2/variable.cxx | 4 +-- libbuild2/variable.txx | 4 +-- 16 files changed, 153 insertions(+), 106 deletions(-) diff --git a/libbuild2/adhoc-rule-cxx.cxx b/libbuild2/adhoc-rule-cxx.cxx index fbe967e..db5c5ab 100644 --- a/libbuild2/adhoc-rule-cxx.cxx +++ b/libbuild2/adhoc-rule-cxx.cxx @@ -302,7 +302,7 @@ namespace build2 // auto_thread_env penv (nullptr); context& ctx (*t.ctx.module_context); - scheduler::phase_guard pg (ctx.sched); + scheduler::phase_guard pg (*ctx.sched); uint16_t verbosity (3); // Project creation command verbosity. diff --git a/libbuild2/algorithm.cxx b/libbuild2/algorithm.cxx index 597ab6c..4489e2b 100644 --- a/libbuild2/algorithm.cxx +++ b/libbuild2/algorithm.cxx @@ -278,7 +278,7 @@ namespace build2 // unless we release the phase. // phase_unlock u (ct.ctx, true /* unlock */, true /* delay */); - e = ctx.sched.wait (busy - 1, task_count, u, *wq); + e = ctx.sched->wait (busy - 1, task_count, u, *wq); } // We don't lock already applied or executed targets. @@ -327,7 +327,7 @@ namespace build2 // this target. // task_count.store (offset + ctx.count_base (), memory_order_release); - ctx.sched.resume (task_count); + ctx.sched->resume (task_count); } target& @@ -1101,7 +1101,7 @@ namespace build2 // Also pass our diagnostics and lock stacks (this is safe since we // expect the caller to wait for completion before unwinding its stack). // - if (ct.ctx.sched.async ( + if (ct.ctx.sched->async ( start_count, *task_count, [a, try_match] (const diag_frame* ds, @@ -2388,7 +2388,7 @@ namespace build2 target::offset_busy - target::offset_executed, memory_order_release)); assert (tc == ctx.count_busy ()); - ctx.sched.resume (s.task_count); + ctx.sched->resume (s.task_count); return ts; } @@ -2459,7 +2459,7 @@ namespace build2 : s.state; s.task_count.store (exec, memory_order_release); - ctx.sched.resume (s.task_count); + ctx.sched->resume (s.task_count); } else { @@ -2470,15 +2470,15 @@ namespace build2 // Pass our diagnostics stack (this is safe since we expect the // caller to wait for completion before unwinding its diag stack). // - if (ctx.sched.async (start_count, - *task_count, - [a] (const diag_frame* ds, target& t) - { - diag_frame::stack_guard dsg (ds); - execute_impl (a, t); - }, - diag_frame::stack (), - ref (t))) + if (ctx.sched->async (start_count, + *task_count, + [a] (const diag_frame* ds, target& t) + { + diag_frame::stack_guard dsg (ds); + execute_impl (a, t); + }, + diag_frame::stack (), + ref (t))) return target_state::unknown; // Queued. // Executed synchronously, fall through. @@ -2527,15 +2527,15 @@ namespace build2 r = execute_impl (a, t); else { - if (ctx.sched.async (start_count, - *task_count, - [a] (const diag_frame* ds, target& t) - { - diag_frame::stack_guard dsg (ds); - execute_impl (a, t); - }, - diag_frame::stack (), - ref (t))) + if (ctx.sched->async (start_count, + *task_count, + [a] (const diag_frame* ds, target& t) + { + diag_frame::stack_guard dsg (ds); + execute_impl (a, t); + }, + diag_frame::stack (), + ref (t))) return target_state::unknown; // Queued. // Executed synchronously, fall through. @@ -2551,7 +2551,7 @@ namespace build2 : s.state; s.task_count.store (exec, memory_order_release); - ctx.sched.resume (s.task_count); + ctx.sched->resume (s.task_count); } } else @@ -3096,7 +3096,7 @@ namespace build2 target_state gs (execute_impl (a, g, 0, nullptr)); if (gs == target_state::busy) - ctx.sched.wait (ctx.count_executed (), + ctx.sched->wait (ctx.count_executed (), g[a].task_count, scheduler::work_none); diff --git a/libbuild2/algorithm.ixx b/libbuild2/algorithm.ixx index b87a535..6029290 100644 --- a/libbuild2/algorithm.ixx +++ b/libbuild2/algorithm.ixx @@ -753,7 +753,7 @@ namespace build2 if (r == target_state::busy) { - t.ctx.sched.wait (t.ctx.count_executed (), + t.ctx.sched->wait (t.ctx.count_executed (), t[a].task_count, scheduler::work_none); @@ -789,7 +789,7 @@ namespace build2 // If the target is still busy, wait for its completion. // - ctx.sched.wait (ctx.count_executed (), + ctx.sched->wait (ctx.count_executed (), t[a].task_count, scheduler::work_none); @@ -806,7 +806,7 @@ namespace build2 if (r == target_state::busy) { - t.ctx.sched.wait (t.ctx.count_executed (), + t.ctx.sched->wait (t.ctx.count_executed (), t[a].task_count, scheduler::work_none); diff --git a/libbuild2/cc/compile-rule.cxx b/libbuild2/cc/compile-rule.cxx index e76cb7c..7a357d9 100644 --- a/libbuild2/cc/compile-rule.cxx +++ b/libbuild2/cc/compile-rule.cxx @@ -3449,7 +3449,7 @@ namespace build2 msvc_sanitize_cl (args); - psrc = ctx.fcache.create (t.path () + pext, !modules); + psrc = ctx.fcache->create (t.path () + pext, !modules); if (fc) { @@ -3601,7 +3601,7 @@ namespace build2 // Preprocessor output. // - psrc = ctx.fcache.create (t.path () + pext, !modules); + psrc = ctx.fcache->create (t.path () + pext, !modules); args.push_back ("-o"); args.push_back (psrc.path ().string ().c_str ()); } @@ -3896,7 +3896,7 @@ namespace build2 if (modules && (ctype != compiler_type::msvc || md.type != unit_type::module_intf)) { - result.first = ctx.fcache.create_existing (t.path () + pext); + result.first = ctx.fcache->create_existing (t.path () + pext); result.second = true; } @@ -7521,7 +7521,7 @@ namespace build2 // Compressed preprocessed file extension. // - string cpext (t.ctx.fcache.compressed_extension (pext)); + string cpext (t.ctx.fcache->compressed_extension (pext)); clean_extras extras; switch (ctype) diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index f860fdc..490e0b4 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -3964,7 +3964,7 @@ namespace build2 auto i (find_option_prefix ("-flto", args.rbegin (), args.rend ())); if (i != args.rend () && strcmp (*i, "-flto=auto") == 0) { - jobs_extra = scheduler::alloc_guard (ctx.sched, 0); + jobs_extra = scheduler::alloc_guard (*ctx.sched, 0); jobs_arg = "-flto=" + to_string (1 + jobs_extra.n); *i = jobs_arg.c_str (); } @@ -3983,7 +3983,7 @@ namespace build2 strcmp (*i, "-flto=thin") == 0 && !find_option_prefix ("-flto-jobs=", args)) { - jobs_extra = scheduler::alloc_guard (ctx.sched, 0); + jobs_extra = scheduler::alloc_guard (*ctx.sched, 0); jobs_arg = "-flto-jobs=" + to_string (1 + jobs_extra.n); args.insert (i.base (), jobs_arg.c_str ()); // After -flto=thin. } diff --git a/libbuild2/context.cxx b/libbuild2/context.cxx index b7429e0..90c4f6c 100644 --- a/libbuild2/context.cxx +++ b/libbuild2/context.cxx @@ -86,9 +86,9 @@ namespace build2 optional mc, const loaded_modules_lock* ml) : data_ (new data (*this)), - sched (s), - mutexes (ms), - fcache (fc), + sched (&s), + mutexes (&ms), + fcache (&fc), match_only (mo), no_external_modules (nem), dry_run_option (dr), @@ -111,6 +111,8 @@ namespace build2 ? optional> (nullptr) : nullopt) { + // NOTE: see also the bare minimum version below if adding anything here. + tracer trace ("context"); l6 ([&]{trace << "initializing build state";}); @@ -673,6 +675,42 @@ namespace build2 } context:: + context () + : data_ (new data (*this)), + sched (nullptr), + mutexes (nullptr), + fcache (nullptr), + match_only (false), + no_external_modules (true), + dry_run_option (false), + no_diag_buffer (false), + keep_going (false), + phase_mutex (*this), + scopes (data_->scopes), + targets (data_->targets), + var_pool (data_->var_pool), + var_patterns (data_->var_patterns), + var_overrides (data_->var_overrides), + functions (data_->functions), + global_scope (create_global_scope (data_->scopes)), + global_target_types (data_->global_target_types), + global_override_cache (data_->global_override_cache), + global_var_overrides (data_->global_var_overrides), + modules_lock (nullptr), + module_context (nullptr) + { + variable_pool& vp (data_->var_pool); + + var_src_root = &vp.insert ("src_root"); + var_out_root = &vp.insert ("out_root"); + + var_project = &vp.insert ("project"); + var_amalgamation = &vp.insert ("amalgamation"); + + load_generation = 1; + } + + context:: ~context () { // Cannot be inline since context::data is undefined. @@ -812,11 +850,11 @@ namespace build2 { ++contention; // Protected by m_. - ctx_.sched.deactivate (false /* external */); + ctx_.sched->deactivate (false /* external */); for (; ctx_.phase != n; v->wait (l)) ; r = !fail_; l.unlock (); // Important: activate() can block. - ctx_.sched.activate (false /* external */); + ctx_.sched->activate (false /* external */); } else r = !fail_; @@ -828,9 +866,9 @@ namespace build2 { if (!lm_.try_lock ()) { - ctx_.sched.deactivate (false /* external */); + ctx_.sched->deactivate (false /* external */); lm_.lock (); - ctx_.sched.activate (false /* external */); + ctx_.sched->activate (false /* external */); ++contention_load; // Protected by lm_. } @@ -880,9 +918,9 @@ namespace build2 // relock(). // if (o == run_phase::match && n == run_phase::execute) - ctx_.sched.push_phase (); + ctx_.sched->push_phase (); else if (o == run_phase::execute && n == run_phase::match) - ctx_.sched.pop_phase (); + ctx_.sched->pop_phase (); if (v != nullptr) { @@ -937,9 +975,9 @@ namespace build2 // unlock(). // if (o == run_phase::match && n == run_phase::execute) - ctx_.sched.push_phase (); + ctx_.sched->push_phase (); else if (o == run_phase::execute && n == run_phase::match) - ctx_.sched.pop_phase (); + ctx_.sched->pop_phase (); // Notify others that could be waiting for this phase. // @@ -953,11 +991,11 @@ namespace build2 { ++contention; // Protected by m_. - ctx_.sched.deactivate (false /* external */); + ctx_.sched->deactivate (false /* external */); for (; ctx_.phase != n; v->wait (l)) ; r = !fail_; l.unlock (); // Important: activate() can block. - ctx_.sched.activate (false /* external */); + ctx_.sched->activate (false /* external */); } } @@ -972,9 +1010,9 @@ namespace build2 // s = false; - ctx_.sched.deactivate (false /* external */); + ctx_.sched->deactivate (false /* external */); lm_.lock (); - ctx_.sched.activate (false /* external */); + ctx_.sched->activate (false /* external */); ++contention_load; // Protected by lm_. } diff --git a/libbuild2/context.hxx b/libbuild2/context.hxx index c040927..35ba11d 100644 --- a/libbuild2/context.hxx +++ b/libbuild2/context.hxx @@ -211,9 +211,11 @@ namespace build2 unique_ptr data_; public: - scheduler& sched; - global_mutexes& mutexes; - file_cache& fcache; + // These are only NULL for the "bare minimum" context (see below). + // + scheduler* sched; + global_mutexes* mutexes; + file_cache* fcache; // Match only flag (see --match-only but also dist). // @@ -677,7 +679,6 @@ namespace build2 reserves (size_t t, size_t v): targets (t), variables (v) {} }; - explicit context (scheduler&, global_mutexes&, file_cache&, @@ -691,6 +692,14 @@ namespace build2 optional module_context = nullptr, const loaded_modules_lock* inherited_mudules_lock = nullptr); + // Special context with bare minimum of initializations. It is only + // guaranteed to be sufficiently initialized to call extract_variable(). + // + // Note that for this purpose you may omit calls to init_diag() and + // init(). + // + context (); + // Reserve elements in containers to avoid re-allocation/re-hashing. Zero // values are ignored (that is, the corresponding container reserve() // function is not called). Can only be called in the load phase. diff --git a/libbuild2/context.ixx b/libbuild2/context.ixx index 95c5416..7b2a405 100644 --- a/libbuild2/context.ixx +++ b/libbuild2/context.ixx @@ -57,7 +57,7 @@ namespace build2 wait () { phase_unlock u (*ctx, phase, true /* delay */); - ctx->sched.wait (start_count, *task_count, u); + ctx->sched->wait (start_count, *task_count, u); task_count = nullptr; } } diff --git a/libbuild2/diagnostics.cxx b/libbuild2/diagnostics.cxx index b31ff82..e164f10 100644 --- a/libbuild2/diagnostics.cxx +++ b/libbuild2/diagnostics.cxx @@ -18,11 +18,11 @@ using namespace butl; namespace build2 { - // Diagnostics state (verbosity level, progress, etc). Keep disabled until - // set from options. + // Diagnostics state (verbosity level, progress, etc). Keep default/disabled + // until set from options. // - uint16_t verb = 0; - bool silent = true; + uint16_t verb = 1; + bool silent = false; optional diag_progress_option; optional diag_color_option; @@ -621,7 +621,7 @@ namespace build2 int diag_buffer:: pipe (context& ctx, bool force) { - return (ctx.sched.serial () || ctx.no_diag_buffer) && !force ? 2 : -1; + return (ctx.sched->serial () || ctx.no_diag_buffer) && !force ? 2 : -1; } void diag_buffer:: @@ -629,7 +629,7 @@ namespace build2 { assert (state_ == state::closed && args0 != nullptr); - serial = ctx_.sched.serial (); + serial = ctx_.sched->serial (); nobuf = !serial && ctx_.no_diag_buffer; if (fd != nullfd) @@ -653,7 +653,7 @@ namespace build2 { assert (state_ == state::closed && args0 != nullptr); - serial = ctx_.sched.serial (); + serial = ctx_.sched->serial (); nobuf = !serial && ctx_.no_diag_buffer; this->args0 = args0; state_ = state::eof; diff --git a/libbuild2/module.cxx b/libbuild2/module.cxx index 234b469..0928307 100644 --- a/libbuild2/module.cxx +++ b/libbuild2/module.cxx @@ -81,9 +81,9 @@ namespace build2 // adding a reasonable margin for future growth. // ctx.module_context_storage->reset ( - new context (ctx.sched, - ctx.mutexes, - ctx.fcache, + new context (*ctx.sched, + *ctx.mutexes, + *ctx.fcache, false, /* match_only */ false, /* no_external_modules */ false, /* dry_run */ @@ -144,8 +144,8 @@ namespace build2 // keep it in case things change. Actually, we may need it, if the // scheduler was started up in a tuned state, like in bpkg). // - auto sched_tune (ctx.sched.serial () - ? scheduler::tune_guard (ctx.sched, 0) + auto sched_tune (ctx.sched->serial () + ? scheduler::tune_guard (*ctx.sched, 0) : scheduler::tune_guard ()); // Remap verbosity level 0 to 1 unless we were requested to be silent. @@ -429,7 +429,7 @@ namespace build2 // auto_thread_env penv (nullptr); context& ctx (*bs.ctx.module_context); - scheduler::phase_guard pg (ctx.sched); + scheduler::phase_guard pg (*ctx.sched); // Load the imported project in the module context. // diff --git a/libbuild2/operation.cxx b/libbuild2/operation.cxx index 2e5886c..d84db4f 100644 --- a/libbuild2/operation.cxx +++ b/libbuild2/operation.cxx @@ -278,11 +278,11 @@ namespace build2 // through a few hoops to make sure we don't overindulge. // md.incr = stderr_term // Scale depending on output type. - ? (ctx.sched.serial () ? 1 : 5) + ? (ctx.sched->serial () ? 1 : 5) : 100; md.what = " targets to " + diag_do (ctx, a); - mg = ctx.sched.monitor ( + mg = ctx.sched->monitor ( ctx.target_count, md.incr, [&md] (size_t c) -> size_t @@ -571,7 +571,7 @@ namespace build2 switch (ctx.current_inner_oif->concurrency) { - case 0: sched_tune = tune_guard (ctx.sched, 1); break; // Run serially. + case 0: sched_tune = tune_guard (*ctx.sched, 1); break; // Run serially. case 1: break; // Run as is. default: assert (false); // Not supported. } @@ -594,7 +594,7 @@ namespace build2 { what = "% of targets " + diag_did (ctx, a); - mg = ctx.sched.monitor ( + mg = ctx.sched->monitor ( ctx.target_count, init - incr, [init, incr, &what, &ctx] (size_t c) -> size_t diff --git a/libbuild2/script/run.cxx b/libbuild2/script/run.cxx index 1106421..6d73a7e 100644 --- a/libbuild2/script/run.cxx +++ b/libbuild2/script/run.cxx @@ -2984,7 +2984,7 @@ namespace build2 // If/when required we could probably support the precise sleep // mode (e.g., via an option). // - env.context.sched.sleep (t); + env.context.sched->sleep (t); } }; diff --git a/libbuild2/test/rule.cxx b/libbuild2/test/rule.cxx index 2d02d39..81bf50a 100644 --- a/libbuild2/test/rule.cxx +++ b/libbuild2/test/rule.cxx @@ -563,22 +563,22 @@ namespace build2 { scope_state& r (res.back ()); - if (!ctx.sched.async (ctx.count_busy (), - t[a].task_count, - [this] (const diag_frame* ds, - scope_state& r, - const target& t, - const testscript& ts, - const dir_path& wd) - { - diag_frame::stack_guard dsg (ds); - r = perform_script_impl (t, ts, wd, *this); - }, - diag_frame::stack (), - ref (r), - cref (t), - cref (ts), - cref (wd))) + if (!ctx.sched->async (ctx.count_busy (), + t[a].task_count, + [this] (const diag_frame* ds, + scope_state& r, + const target& t, + const testscript& ts, + const dir_path& wd) + { + diag_frame::stack_guard dsg (ds); + r = perform_script_impl (t, ts, wd, *this); + }, + diag_frame::stack (), + ref (r), + cref (t), + cref (ts), + cref (wd))) { // Executed synchronously. If failed and we were not asked to // keep going, bail out. diff --git a/libbuild2/test/script/parser.cxx b/libbuild2/test/script/parser.cxx index 60656a1..b712c21 100644 --- a/libbuild2/test/script/parser.cxx +++ b/libbuild2/test/script/parser.cxx @@ -1831,19 +1831,19 @@ namespace build2 // UBSan workaround. // const diag_frame* df (diag_frame::stack ()); - if (!ctx->sched.async (task_count, - [] (const diag_frame* ds, - scope& s, - script& scr, - runner& r) - { - diag_frame::stack_guard dsg (ds); - execute_impl (s, scr, r); - }, - df, - ref (*chain), - ref (*script_), - ref (*runner_))) + if (!ctx->sched->async (task_count, + [] (const diag_frame* ds, + scope& s, + script& scr, + runner& r) + { + diag_frame::stack_guard dsg (ds); + execute_impl (s, scr, r); + }, + df, + ref (*chain), + ref (*script_), + ref (*runner_))) { // Bail out if the scope has failed and we weren't instructed // to keep going. diff --git a/libbuild2/variable.cxx b/libbuild2/variable.cxx index d55737b..8c52e22 100644 --- a/libbuild2/variable.cxx +++ b/libbuild2/variable.cxx @@ -369,8 +369,8 @@ namespace build2 // Typification is kind of like caching so we reuse that mutex shard. // shared_mutex& m ( - ctx.mutexes.variable_cache[ - hash () (&v) % ctx.mutexes.variable_cache_size]); + ctx.mutexes->variable_cache[ + hash () (&v) % ctx.mutexes->variable_cache_size]); // Note: v.type is rechecked by typify() under lock. // diff --git a/libbuild2/variable.txx b/libbuild2/variable.txx index 2950ea0..2c1265a 100644 --- a/libbuild2/variable.txx +++ b/libbuild2/variable.txx @@ -1019,8 +1019,8 @@ namespace build2 : 0); shared_mutex& m ( - ctx.mutexes.variable_cache[ - hash () (this) % ctx.mutexes.variable_cache_size]); + ctx.mutexes->variable_cache[ + hash () (this) % ctx.mutexes->variable_cache_size]); slock sl (m); ulock ul (m, defer_lock); -- cgit v1.1