aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-10-26 12:06:33 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-10-26 12:37:28 +0200
commita3d5de5dfecc694d15f23ed03d13cb108dda3e1b (patch)
tree1fb9bf176ce3aa9ecacdfbb8687ae87198e8af66
parent0fc035dd4d51c70231a46d6b8a6ba18a91e8ae43 (diff)
Add clean_during_match*() functions
-rw-r--r--libbuild2/algorithm.cxx180
-rw-r--r--libbuild2/algorithm.hxx17
2 files changed, 196 insertions, 1 deletions
diff --git a/libbuild2/algorithm.cxx b/libbuild2/algorithm.cxx
index 3d04173..bd64cb1 100644
--- a/libbuild2/algorithm.cxx
+++ b/libbuild2/algorithm.cxx
@@ -2727,6 +2727,8 @@ namespace build2
bool
update_during_match (tracer& trace, action a, const target& t, timestamp ts)
{
+ // NOTE: see also clean_during_match() if changing anything here.
+
assert (a == perform_update_id);
// Note: this function is used to make sure header dependencies are up to
@@ -2798,6 +2800,11 @@ namespace build2
action a, target& t,
uintptr_t mask)
{
+ // NOTE: see also clean_during_match_prerequisites() if changing anything
+ // here.
+
+ assert (a == perform_update_id);
+
prerequisite_targets& pts (t.prerequisite_targets[a]);
// On the first pass detect and handle unchanged tragets. Note that we
@@ -2924,6 +2931,179 @@ namespace build2
return r;
}
+ bool
+ clean_during_match (tracer& trace, action a, const target& t)
+ {
+ // Let's keep this as close to update_during_match() semantically as
+ // possible until we see a clear reason to deviate.
+
+ assert (a == perform_clean_id);
+
+ target_state os (t.matched_state (a));
+
+ if (os == target_state::unchanged)
+ return false;
+ else
+ {
+ target_state ns;
+ if (os != target_state::changed)
+ {
+ phase_switch ps (t.ctx, run_phase::execute);
+ ns = execute_direct_sync (a, t);
+ }
+ else
+ ns = os;
+
+ if (ns != os && ns != target_state::unchanged)
+ {
+ l6 ([&]{trace << "cleaned " << t
+ << "; old state " << os
+ << "; new state " << ns;});
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+
+ bool
+ clean_during_match_prerequisites (tracer& trace,
+ action a, target& t,
+ uintptr_t mask)
+ {
+ // Let's keep this as close to update_during_match_prerequisites()
+ // semantically as possible until we see a clear reason to deviate.
+ //
+ // Currently the only substantial change is the reverse iteration order.
+
+ assert (a == perform_clean_id);
+
+ prerequisite_targets& pts (t.prerequisite_targets[a]);
+
+ // On the first pass detect and handle unchanged tragets. Note that we
+ // have to do it in a separate pass since we cannot call matched_state()
+ // once we've switched the phase.
+ //
+ size_t n (0);
+
+ for (prerequisite_target& p: pts)
+ {
+ if (mask == 0 || (p.include & mask) != 0)
+ {
+ if (p.target != nullptr)
+ {
+ const target& pt (*p.target);
+
+ target_state os (pt.matched_state (a));
+
+ if (os != target_state::unchanged)
+ {
+ ++n;
+ p.data = static_cast<uintptr_t> (os);
+ continue;
+ }
+ }
+
+ p.data = 0;
+ }
+ }
+
+ // If all unchanged, we are done.
+ //
+ if (n == 0)
+ return false;
+
+ // Provide additional information on what's going on.
+ //
+ auto df = make_diag_frame (
+ [&t](const diag_record& dr)
+ {
+ if (verb != 0)
+ dr << info << "while cleaning during match prerequisites of "
+ << "target " << t;
+ });
+
+ context& ctx (t.ctx);
+
+ phase_switch ps (ctx, run_phase::execute);
+
+ bool r (false);
+
+ // @@ Maybe we should optimize for n == 1? Maybe we should just call
+ // smarter clean_during_match() in this case?
+ //
+#if 0
+ for (prerequisite_target& p: reverse_iterate (pts))
+ {
+ if ((mask == 0 || (p.include & mask) != 0) && p.data != 0)
+ {
+ const target& pt (*p.target);
+
+ target_state os (static_cast<target_state> (p.data));
+ target_state ns (execute_direct_sync (a, pt));
+
+ if (ns != os && ns != target_state::unchanged)
+ {
+ l6 ([&]{trace << "cleaned " << pt
+ << "; old state " << os
+ << "; new state " << ns;});
+ r = true;
+ }
+
+ p.data = 0;
+ }
+ }
+#else
+
+ // Start asynchronous execution of prerequisites. Similar logic to
+ // straight_execute_members().
+ //
+ // Note that the target's task count is expected to be busy (since this
+ // function is called during match). And there don't seem to be any
+ // problems in using it for execute.
+ //
+ atomic_count& tc (t[a].task_count);
+
+ size_t busy (ctx.count_busy ());
+
+ wait_guard wg (ctx, busy, tc);
+
+ for (prerequisite_target& p: reverse_iterate (pts))
+ {
+ if ((mask == 0 || (p.include & mask) != 0) && p.data != 0)
+ {
+ execute_direct_async (a, *p.target, busy, tc);
+ }
+ }
+
+ wg.wait ();
+
+ // Finish execution and process the result.
+ //
+ for (prerequisite_target& p: reverse_iterate (pts))
+ {
+ if ((mask == 0 || (p.include & mask) != 0) && p.data != 0)
+ {
+ const target& pt (*p.target);
+ target_state ns (execute_complete (a, pt));
+ target_state os (static_cast<target_state> (p.data));
+
+ if (ns != os && ns != target_state::unchanged)
+ {
+ l6 ([&]{trace << "cleaned " << pt
+ << "; old state " << os
+ << "; new state " << ns;});
+ r = true;
+ }
+
+ p.data = 0;
+ }
+ }
+#endif
+
+ return r;
+ }
+
static inline void
blank_adhoc_member (const target*&)
{
diff --git a/libbuild2/algorithm.hxx b/libbuild2/algorithm.hxx
index 779578e..5ebbee2 100644
--- a/libbuild2/algorithm.hxx
+++ b/libbuild2/algorithm.hxx
@@ -649,7 +649,8 @@ namespace build2
//
// Note that such a target must still be updated normally during the execute
// phase in order to keep the dependency counts straight (at which point the
- // target state/timestamp will be re-incorporated into the result).
+ // target state/timestamp will be re-incorporated into the result). Unless
+ // it was matched direct.
//
LIBBUILD2_SYMEXPORT bool
update_during_match (tracer&,
@@ -669,6 +670,20 @@ namespace build2
action, target&,
uintptr_t mask = prerequisite_target::include_udm);
+ // Equivalent functions for clean. Note that if possible you should leave
+ // cleaning to normal execute and these functions should only be used in
+ // special cases where this is not possible.
+ //
+ LIBBUILD2_SYMEXPORT bool
+ clean_during_match (tracer&,
+ action, const target&);
+
+ LIBBUILD2_SYMEXPORT bool
+ clean_during_match_prerequisites (
+ tracer&,
+ action, target&,
+ uintptr_t mask = prerequisite_target::include_udm);
+
// The default prerequisite execute implementation. Call execute_async() on
// each non-ignored (non-NULL) prerequisite target in a loop and then wait
// for their completion. Return target_state::changed if any of them were