aboutsummaryrefslogtreecommitdiff
path: root/build2/algorithm.hxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-02-16 16:24:07 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-02-16 16:40:45 +0200
commit6293ede7a742866a713050737cc2b43d51161b6f (patch)
treef259845f47c2e03432bdd98b4b665e1a3de85d55 /build2/algorithm.hxx
parentad9cb7fec5cc74697322620909e0ff1ba9ecb61b (diff)
Add support for detecting dependency cycles
Diffstat (limited to 'build2/algorithm.hxx')
-rw-r--r--build2/algorithm.hxx45
1 files changed, 39 insertions, 6 deletions
diff --git a/build2/algorithm.hxx b/build2/algorithm.hxx
index 1f8736f..62c312f 100644
--- a/build2/algorithm.hxx
+++ b/build2/algorithm.hxx
@@ -104,7 +104,9 @@ namespace build2
// Target match lock: a non-const target reference and the target::offset_*
// state that has already been "achieved". Note that target::task_count
- // itself is set to busy for the duration or the lock.
+ // itself is set to busy for the duration or the lock. While at it we also
+ // maintain a stack of active locks in the current dependency chain (used to
+ // detect dependency cycles).
//
struct target_lock
{
@@ -120,6 +122,8 @@ namespace build2
void
unlock ();
+ // Movable-only type with move-assignment only to NULL lock.
+ //
target_lock () = default;
target_lock (target_lock&&);
target_lock& operator= (target_lock&&);
@@ -130,13 +134,42 @@ namespace build2
// Implementation details.
//
~target_lock ();
- target_lock (action_type a, target_type* t, size_t o)
- : action (a), target (t), offset (o) {}
-
- target_type*
- release () {auto r (target); target = nullptr; return r;}
+ target_lock (action_type, target_type*, size_t);
+
+ struct data
+ {
+ action_type action;
+ target_type* target;
+ size_t offset;
+ };
+
+ data
+ release ();
+
+ static
+#ifdef __cpp_thread_local
+ thread_local
+#else
+ __thread
+#endif
+ const target_lock* stack; // Tip of the stack.
+ const target_lock* prev;
+
+ struct stack_guard
+ {
+ explicit stack_guard (const target_lock* s): s_ (stack) {stack = s;}
+ ~stack_guard () {stack = s_;}
+ const target_lock* s_;
+ };
};
+ // If this target is already locked in this dependency chain, then return
+ // the corresponding lock. Return NULL otherwise (so can be used a boolean
+ // predicate).
+ //
+ const target_lock*
+ dependency_cycle (action, const target&);
+
// If the target is already applied (for this action ) or executed, then no
// lock is acquired. Otherwise, the target must not yet be matched for this
// action.