diff options
Diffstat (limited to 'libbuild2/algorithm.ixx')
-rw-r--r-- | libbuild2/algorithm.ixx | 311 |
1 files changed, 262 insertions, 49 deletions
diff --git a/libbuild2/algorithm.ixx b/libbuild2/algorithm.ixx index 24d9e5b..9f1b70f 100644 --- a/libbuild2/algorithm.ixx +++ b/libbuild2/algorithm.ixx @@ -45,6 +45,39 @@ namespace build2 k.proj, {&tt, k.tk.dir, k.tk.out, k.tk.name, k.tk.ext}, k.scope}); } + inline const target* + search_exsiting (context& ctx, + const target_type& tt, + const prerequisite_key& k) + { + return search_existing ( + ctx, + prerequisite_key { + k.proj, {&tt, k.tk.dir, k.tk.out, k.tk.name, k.tk.ext}, k.scope}); + } + + inline const target& + search_new (context& ctx, + const target_type& tt, + const prerequisite_key& k) + { + return search_new ( + ctx, + prerequisite_key { + k.proj, {&tt, k.tk.dir, k.tk.out, k.tk.name, k.tk.ext}, k.scope}); + } + + inline pair<target&, ulock> + search_new_locked (context& ctx, + const target_type& tt, + const prerequisite_key& k) + { + return search_new_locked ( + ctx, + prerequisite_key { + k.proj, {&tt, k.tk.dir, k.tk.out, k.tk.name, k.tk.ext}, k.scope}); + } + inline const target& search (const target& t, const target_type& type, @@ -110,6 +143,48 @@ namespace build2 scope}); } + inline const target& + search_new (context& ctx, + const target_type& type, + const dir_path& dir, + const dir_path& out, + const string& name, + const string* ext, + const scope* scope) + { + return search_new ( + ctx, + prerequisite_key { + nullopt, + { + &type, + &dir, &out, &name, + ext != nullptr ? optional<string> (*ext) : nullopt + }, + scope}); + } + + inline pair<target&, ulock> + search_new_locked (context& ctx, + const target_type& type, + const dir_path& dir, + const dir_path& out, + const string& name, + const string* ext, + const scope* scope) + { + return search_new_locked ( + ctx, + prerequisite_key { + nullopt, + { + &type, + &dir, &out, &name, + ext != nullptr ? optional<string> (*ext) : nullopt + }, + scope}); + } + template <typename T> inline const T& search (const target& t, @@ -123,6 +198,21 @@ namespace build2 t, T::static_type, dir, out, name, ext, scope).template as<T> (); } + template <typename T> + inline const T* + search_existing (context& ctx, + const dir_path& dir, + const dir_path& out, + const string& name, + const string* ext, + const scope* scope) + { + const target* r ( + search_existing ( + ctx, T::static_type, dir, out, name, ext, scope)); + return r != nullptr ? &r->template as<T> () : nullptr; + } + LIBBUILD2_SYMEXPORT target_lock lock_impl (action, const target&, optional<scheduler::work_queue>); @@ -130,8 +220,8 @@ namespace build2 unlock_impl (action, target&, size_t); inline target_lock:: - target_lock (action_type a, target_type* t, size_t o) - : action (a), target (t), offset (o) + target_lock (action_type a, target_type* t, size_t o, bool f) + : action (a), target (t), offset (o), first (f) { if (target != nullptr) prev = stack (this); @@ -143,7 +233,8 @@ namespace build2 if (target != nullptr && prev != this) { const target_lock* cur (stack (prev)); - assert (cur == this); + if (cur != this) // NDEBUG + assert (cur == this); prev = this; } } @@ -158,7 +249,8 @@ namespace build2 if (prev != this) { const target_lock* cur (stack (prev)); - assert (cur == this); + if (cur != this) // NDEBUG + assert (cur == this); } target = nullptr; @@ -168,14 +260,15 @@ namespace build2 inline auto target_lock:: release () -> data { - data r {action, target, offset}; + data r {action, target, offset, first}; if (target != nullptr) { if (prev != this) { const target_lock* cur (stack (prev)); - assert (cur == this); + if (cur != this) // NDEBUG + assert (cur == this); } target = nullptr; @@ -191,7 +284,7 @@ namespace build2 } inline target_lock:: - target_lock (target_lock&& x) + target_lock (target_lock&& x) noexcept : action (x.action), target (x.target), offset (x.offset) { if (target != nullptr) @@ -199,7 +292,8 @@ namespace build2 if (x.prev != &x) { const target_lock* cur (stack (this)); - assert (cur == &x); + if (cur != &x) // NDEBUG + assert (cur == &x); prev = x.prev; } else @@ -210,7 +304,7 @@ namespace build2 } inline target_lock& target_lock:: - operator= (target_lock&& x) + operator= (target_lock&& x) noexcept { if (this != &x) { @@ -225,7 +319,8 @@ namespace build2 if (x.prev != &x) { const target_lock* cur (stack (this)); - assert (cur == &x); + if (cur != &x) // NDEBUG + assert (cur == &x); prev = x.prev; } else @@ -297,48 +392,66 @@ namespace build2 } LIBBUILD2_SYMEXPORT const rule_match* - match_rule (action, target&, const rule* skip, bool try_match = false); + match_rule (action, target&, + const rule* skip, + bool try_match = false, + match_extra* = nullptr); LIBBUILD2_SYMEXPORT recipe apply_impl (action, target&, const rule_match&); LIBBUILD2_SYMEXPORT pair<bool, target_state> - match (action, const target&, size_t, atomic_count*, bool try_match = false); + match_impl (action, const target&, + size_t, atomic_count*, + bool try_match = false); inline void - match_inc_dependens (action a, const target& t) + match_inc_dependents (action a, const target& t) { t.ctx.dependency_count.fetch_add (1, memory_order_relaxed); t[a].dependents.fetch_add (1, memory_order_release); } inline target_state - match (action a, const target& t, bool fail) + match_sync (action a, const target& t, bool fail) { assert (t.ctx.phase == run_phase::match); - target_state r (match (a, t, 0, nullptr).second); + target_state r (match_impl (a, t, 0, nullptr).second); if (r != target_state::failed) - match_inc_dependens (a, t); + match_inc_dependents (a, t); else if (fail) throw failed (); return r; } + inline target_state + match_direct_sync (action a, const target& t, bool fail) + { + assert (t.ctx.phase == run_phase::match); + + target_state r (match_impl (a, t, 0, nullptr).second); + + if (r == target_state::failed && fail) + throw failed (); + + return r; + } + inline pair<bool, target_state> - try_match (action a, const target& t, bool fail) + try_match_sync (action a, const target& t, bool fail) { assert (t.ctx.phase == run_phase::match); pair<bool, target_state> r ( - match (a, t, 0, nullptr, true /* try_match */)); + match_impl (a, t, 0, nullptr, true /* try_match */)); if (r.first) { if (r.second != target_state::failed) - match_inc_dependens (a, t); + match_inc_dependents (a, t); else if (fail) throw failed (); } @@ -347,11 +460,11 @@ namespace build2 } inline pair<bool, target_state> - match (action a, const target& t, unmatch um) + match_sync (action a, const target& t, unmatch um) { assert (t.ctx.phase == run_phase::match); - target_state s (match (a, t, 0, nullptr).second); + target_state s (match_impl (a, t, 0, nullptr).second); if (s == target_state::failed) throw failed (); @@ -385,7 +498,7 @@ namespace build2 } } - match_inc_dependens (a, t); + match_inc_dependents (a, t); return make_pair (false, s);; } @@ -397,23 +510,49 @@ namespace build2 context& ctx (t.ctx); assert (ctx.phase == run_phase::match); - target_state r (match (a, t, sc, &tc).second); + target_state r (match_impl (a, t, sc, &tc).second); - if (fail && !ctx.keep_going && r == target_state::failed) + if (r == target_state::failed && fail && !ctx.keep_going) throw failed (); return r; } + inline target_state + match_complete (action a, const target& t, bool fail) + { + return match_sync (a, t, fail); + } + + inline pair<bool, target_state> + match_complete (action a, const target& t, unmatch um) + { + return match_sync (a, t, um); + } + // Clear rule match-specific target data. // inline void clear_target (action a, target& t) { - t[a].vars.clear (); + target::opstate& s (t.state[a]); + s.recipe = nullptr; + s.recipe_keep = false; + s.resolve_counted = false; + s.vars.clear (); t.prerequisite_targets[a].clear (); - if (a.inner ()) - t.clear_data (); + } + + LIBBUILD2_SYMEXPORT void + set_rule_trace (target_lock&, const rule_match*); + + inline void + set_rule (target_lock& l, const rule_match* r) + { + if (l.target->ctx.trace_match == nullptr) + (*l.target)[l.action].rule = r; + else + set_rule_trace (l, r); } inline void @@ -423,6 +562,7 @@ namespace build2 target::opstate& s (t[l.action]); s.recipe = move (r); + s.recipe_group_action = false; // If this is a noop recipe, then mark the target unchanged to allow for // some optimizations. @@ -448,9 +588,11 @@ namespace build2 // likely. The alternative (trying to "merge" the count keeping track of // whether inner and/or outer is noop) gets hairy rather quickly. // - if (l.action.inner ()) + if (f != nullptr && *f == &group_action) + s.recipe_group_action = true; + else { - if (f == nullptr || *f != &group_action) + if (l.action.inner ()) t.ctx.target_count.fetch_add (1, memory_order_relaxed); } } @@ -464,7 +606,7 @@ namespace build2 l.target->ctx.phase == run_phase::match); clear_target (l.action, *l.target); - (*l.target)[l.action].rule = nullptr; // No rule. + set_rule (l, nullptr); // No rule. set_recipe (l, move (r)); l.offset = target::offset_applied; } @@ -477,7 +619,7 @@ namespace build2 l.target->ctx.phase == run_phase::match); clear_target (l.action, *l.target); - (*l.target)[l.action].rule = &r; + set_rule (l, &r); l.offset = target::offset_matched; } @@ -499,18 +641,18 @@ namespace build2 // In a sense this is like any other dependency. // assert (a.outer ()); - return match (a.inner_action (), t); + return match_sync (a.inner_action (), t); } inline pair<bool, target_state> match_inner (action a, const target& t, unmatch um) { assert (a.outer ()); - return match (a.inner_action (), t, um); + return match_sync (a.inner_action (), t, um); } LIBBUILD2_SYMEXPORT void - resolve_group_impl (action, const target&, target_lock); + resolve_group_impl (action, const target&, target_lock&&); inline const target* resolve_group (action a, const target& t) @@ -544,7 +686,7 @@ namespace build2 inline void inject (action a, target& t, const target& p) { - match (a, p); + match_sync (a, p); t.prerequisite_targets[a].emplace_back (&p); } @@ -608,23 +750,26 @@ namespace build2 } LIBBUILD2_SYMEXPORT target_state - execute (action, const target&, size_t, atomic_count*); + execute_impl (action, const target&, size_t, atomic_count*); inline target_state - execute (action a, const target& t) + execute_sync (action a, const target& t, bool fail) { - return execute (a, t, 0, nullptr); - } + target_state r (execute_impl (a, t, 0, nullptr)); - inline target_state - execute_wait (action a, const target& t) - { - if (execute (a, t) == target_state::busy) - t.ctx.sched.wait (t.ctx.count_executed (), + if (r == target_state::busy) + { + t.ctx.sched->wait (t.ctx.count_executed (), t[a].task_count, scheduler::work_none); - return t.executed_state (a); + r = t.executed_state (a, false); + } + + if (r == target_state::failed && fail) + throw failed (); + + return r; } inline target_state @@ -632,9 +777,62 @@ namespace build2 size_t sc, atomic_count& tc, bool fail) { - target_state r (execute (a, t, sc, &tc)); + target_state r (execute_impl (a, t, sc, &tc)); + + if (r == target_state::failed && fail && !t.ctx.keep_going) + throw failed (); + + return r; + } + + inline target_state + execute_complete (action a, const target& t) + { + // Note: standard operation execute() sidesteps this and calls + // executed_state() directly. + + context& ctx (t.ctx); + + // If the target is still busy, wait for its completion. + // + ctx.sched->wait (ctx.count_executed (), + t[a].task_count, + scheduler::work_none); + + return t.executed_state (a); + } + + LIBBUILD2_SYMEXPORT target_state + execute_direct_impl (action, const target&, size_t, atomic_count*); + + inline target_state + execute_direct_sync (action a, const target& t, bool fail) + { + target_state r (execute_direct_impl (a, t, 0, nullptr)); + + if (r == target_state::busy) + { + t.ctx.sched->wait (t.ctx.count_executed (), + t[a].task_count, + scheduler::work_none); + + r = t.executed_state (a, false); + } + + if (r == target_state::failed && fail) + throw failed (); + + return r; + } + + inline target_state + execute_direct_async (action a, const target& t, + size_t sc, atomic_count& tc, + bool fail) + { + target_state r (execute_direct_impl (a, t, sc, &tc)); - if (fail && !t.ctx.keep_going && r == target_state::failed) + if (r == target_state::failed && fail && !t.ctx.keep_going) throw failed (); return r; @@ -650,7 +848,7 @@ namespace build2 execute_inner (action a, const target& t) { assert (a.outer ()); - return execute_wait (a.inner_action (), t); + return execute_sync (a.inner_action (), t); } inline target_state @@ -726,6 +924,12 @@ namespace build2 const timestamp&, const execute_filter&, size_t); + LIBBUILD2_SYMEXPORT pair<optional<target_state>, const target*> + reverse_execute_prerequisites (const target_type*, + action, const target&, + const timestamp&, const execute_filter&, + size_t); + inline optional<target_state> execute_prerequisites (action a, const target& t, const timestamp& mt, const execute_filter& ef, @@ -734,6 +938,14 @@ namespace build2 return execute_prerequisites (nullptr, a, t, mt, ef, n).first; } + inline optional<target_state> + reverse_execute_prerequisites (action a, const target& t, + const timestamp& mt, const execute_filter& ef, + size_t n) + { + return reverse_execute_prerequisites (nullptr, a, t, mt, ef, n).first; + } + template <typename T> inline pair<optional<target_state>, const T&> execute_prerequisites (action a, const target& t, @@ -767,8 +979,9 @@ namespace build2 p.first, static_cast<const T&> (p.second)); } + template <typename T> inline target_state - execute_members (action a, const target& t, const target* ts[], size_t n) + execute_members (action a, const target& t, T ts[], size_t n) { return t.ctx.current_mode == execution_mode::first ? straight_execute_members (a, t, ts, n, 0) |