From cf1670f9a6541e42f22d58e2de5940e64cb6637e Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 15 May 2020 06:50:05 +0200 Subject: Implement ad hoc recipe sharing and locking --- libbuild2/module.cxx | 177 ++++++++++++++++++++++++++------------------------- 1 file changed, 89 insertions(+), 88 deletions(-) (limited to 'libbuild2/module.cxx') diff --git a/libbuild2/module.cxx b/libbuild2/module.cxx index 4972d7b..ce6c9d6 100644 --- a/libbuild2/module.cxx +++ b/libbuild2/module.cxx @@ -66,14 +66,9 @@ namespace build2 // Note: also used by ad hoc recipes thus not static. // void - create_module_context (context& ctx, const location& loc, const char* what) + create_module_context (context& ctx, const location& loc) { assert (ctx.module_context == nullptr); - - if (!ctx.module_context_storage) - fail (loc) << "unable to update " << what << - info << "updating of " << what << "s is disabled"; - assert (*ctx.module_context_storage == nullptr); // Since we are using the same scheduler, it makes sense to reuse the @@ -98,7 +93,11 @@ namespace build2 // Setup the context to perform update. In a sense we have a long-running // perform meta-operation batch (indefinite, in fact, since we never call // the meta-operation's *_post() callbacks) in which we periodically - // execute the update operation. + // execute update operations. + // + // Note that we perform each build in a separate update operation. Failed + // that, if the same target is update twice (which may happen with ad hoc + // recipes) we will see the old state. // if (mo_perform.meta_operation_pre != nullptr) mo_perform.meta_operation_pre ({} /* parameters */, loc); @@ -107,88 +106,74 @@ namespace build2 if (mo_perform.operation_pre != nullptr) mo_perform.operation_pre ({} /* parameters */, update_id); - - ctx.module_context->current_operation (op_update); } // Note: also used by ad hoc recipes thus not static. // const target& update_in_module_context (context& ctx, const scope& rs, names tgt, - const location& loc, const path& bf, - const char* what, const char* name) + const location& loc, const path& bf) { - action_targets tgs; - { - action a (perform_id, update_id); - - // Cutoff the existing diagnostics stack and push our own entry. - // - diag_frame::stack_guard diag_cutoff (nullptr); - - auto df = make_diag_frame ( - [&loc, what, name] (const diag_record& dr) - { - dr << info (loc) << "while " << what; + // New update operation. + // + ctx.module_context->current_operation (op_update); - if (name != nullptr) - dr << ' ' << name; - }); + // Un-tune the scheduler. + // + // Note that we can only do this if we are running serially because + // otherwise we cannot guarantee the scheduler is idle (we could have + // waiting threads from the outer context). This is fine for now since the + // only two tuning level we use are serial and full concurrency (turns out + // currently we don't really need this: we will always be called during + // load or match phases and we always do parallel match; but let's keep it + // in case things change). + // + 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. + // Failed that, we may have long periods of seemingly nothing happening + // while we quietly update the module, which may look like things have + // hung up. + // + // @@ CTX: modifying global verbosity level won't work if we have multiple + // top-level contexts running in parallel. + // + auto verbg = make_guard ( + [z = !silent && verb == 0 ? (verb = 1, true) : false] () + { + if (z) + verb = 0; + }); + + // Note that for now we suppress progress since it would clash with the + // progress of what we are already doing (maybe in the future we can do + // save/restore but then we would need some sort of diagnostics that we + // have switched to another task). + // + action a (perform_update_id); + action_targets tgs; - // Un-tune the scheduler. - // - // Note that we can only do this if we are running serially because - // otherwise we cannot guarantee the scheduler is idle (we could have - // waiting threads from the outer context). This is fine for now since - // the only two tuning level we use are serial and full concurrency - // (turns out currently we don't really need this: we will always be - // called during load or match phases and we always do parallel match; - // but let's keep it in case things change). - // - 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. - // Failed that, we may have long periods of seemingly nothing happening - // while we quietly update the module, which may look like things have - // hung up. - // - // @@ CTX: modifying global verbosity level won't work if we have - // multiple top-level contexts running in parallel. - // - auto verbg = make_guard ( - [z = !silent && verb == 0 ? (verb = 1, true) : false] () - { - if (z) - verb = 0; - }); - - // Note that for now we suppress progress since it would clash with - // the progress of what we are already doing (maybe in the future we - // can do save/restore but then we would need some sort of - // diagnostics that we have switched to another task). - // - mo_perform.search ({}, /* parameters */ - rs, /* root scope */ - rs, /* base scope */ - bf, /* buildfile */ - rs.find_target_key (tgt, loc), - loc, - tgs); - - mo_perform.match ({}, /* parameters */ - a, - tgs, - 1, /* diag (failures only) */ - false /* progress */); - - mo_perform.execute ({}, /* parameters */ - a, - tgs, - 1, /* diag (failures only) */ - false /* progress */); - } + mo_perform.search ({}, /* parameters */ + rs, /* root scope */ + rs, /* base scope */ + bf, /* buildfile */ + rs.find_target_key (tgt, loc), + loc, + tgs); + + mo_perform.match ({}, /* parameters */ + a, + tgs, + 1, /* diag (failures only) */ + false /* progress */); + + mo_perform.execute ({}, /* parameters */ + a, + tgs, + 1, /* diag (failures only) */ + false /* progress */); assert (tgs.size () == 1); return tgs[0].as (); @@ -348,7 +333,13 @@ namespace build2 // Create the build context if necessary. // if (ctx.module_context == nullptr) - create_module_context (ctx, loc, "build system module"); + { + if (!ctx.module_context_storage) + fail (loc) << "unable to update build system module " << mod << + info << "building of build system modules is disabled"; + + create_module_context (ctx, loc); + } // Inherit loaded_modules lock from the outer context. // @@ -378,17 +369,27 @@ namespace build2 } else { - const scope& rs (lr.second); + const target* l; + { + // Cutoff the existing diagnostics stack and push our own entry. + // + diag_frame::stack_guard diag_cutoff (nullptr); - const target& l ( - update_in_module_context ( - ctx, rs, move (lr.first), - loc, path (), "loading build system module", mod.c_str ())); + auto df = make_diag_frame ( + [&loc, &mod] (const diag_record& dr) + { + dr << info (loc) << "while loading build system module " << mod; + }); + + l = &update_in_module_context ( + ctx, lr.second, move (lr.first), + loc, path ()); + } - if (!l.is_a ("libs")) + if (!l->is_a ("libs")) fail (loc) << "wrong export from build system module " << mod; - lib = l.as ().path (); + lib = l->as ().path (); l5 ([&]{trace << "updated " << lib;}); } -- cgit v1.1