xref: /aosp_15_r20/external/abseil-cpp/absl/synchronization/internal/graphcycles.cc (revision 9356374a3709195abf420251b3e825997ff56c0f)
1*9356374aSAndroid Build Coastguard Worker // Copyright 2017 The Abseil Authors.
2*9356374aSAndroid Build Coastguard Worker //
3*9356374aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*9356374aSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*9356374aSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*9356374aSAndroid Build Coastguard Worker //
7*9356374aSAndroid Build Coastguard Worker //      https://www.apache.org/licenses/LICENSE-2.0
8*9356374aSAndroid Build Coastguard Worker //
9*9356374aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*9356374aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*9356374aSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9356374aSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*9356374aSAndroid Build Coastguard Worker // limitations under the License.
14*9356374aSAndroid Build Coastguard Worker 
15*9356374aSAndroid Build Coastguard Worker // GraphCycles provides incremental cycle detection on a dynamic
16*9356374aSAndroid Build Coastguard Worker // graph using the following algorithm:
17*9356374aSAndroid Build Coastguard Worker //
18*9356374aSAndroid Build Coastguard Worker // A dynamic topological sort algorithm for directed acyclic graphs
19*9356374aSAndroid Build Coastguard Worker // David J. Pearce, Paul H. J. Kelly
20*9356374aSAndroid Build Coastguard Worker // Journal of Experimental Algorithmics (JEA) JEA Homepage archive
21*9356374aSAndroid Build Coastguard Worker // Volume 11, 2006, Article No. 1.7
22*9356374aSAndroid Build Coastguard Worker //
23*9356374aSAndroid Build Coastguard Worker // Brief summary of the algorithm:
24*9356374aSAndroid Build Coastguard Worker //
25*9356374aSAndroid Build Coastguard Worker // (1) Maintain a rank for each node that is consistent
26*9356374aSAndroid Build Coastguard Worker //     with the topological sort of the graph. I.e., path from x to y
27*9356374aSAndroid Build Coastguard Worker //     implies rank[x] < rank[y].
28*9356374aSAndroid Build Coastguard Worker // (2) When a new edge (x->y) is inserted, do nothing if rank[x] < rank[y].
29*9356374aSAndroid Build Coastguard Worker // (3) Otherwise: adjust ranks in the neighborhood of x and y.
30*9356374aSAndroid Build Coastguard Worker 
31*9356374aSAndroid Build Coastguard Worker #include "absl/base/attributes.h"
32*9356374aSAndroid Build Coastguard Worker // This file is a no-op if the required LowLevelAlloc support is missing.
33*9356374aSAndroid Build Coastguard Worker #include "absl/base/internal/low_level_alloc.h"
34*9356374aSAndroid Build Coastguard Worker #ifndef ABSL_LOW_LEVEL_ALLOC_MISSING
35*9356374aSAndroid Build Coastguard Worker 
36*9356374aSAndroid Build Coastguard Worker #include "absl/synchronization/internal/graphcycles.h"
37*9356374aSAndroid Build Coastguard Worker 
38*9356374aSAndroid Build Coastguard Worker #include <algorithm>
39*9356374aSAndroid Build Coastguard Worker #include <array>
40*9356374aSAndroid Build Coastguard Worker #include <cinttypes>
41*9356374aSAndroid Build Coastguard Worker #include <limits>
42*9356374aSAndroid Build Coastguard Worker #include "absl/base/internal/hide_ptr.h"
43*9356374aSAndroid Build Coastguard Worker #include "absl/base/internal/raw_logging.h"
44*9356374aSAndroid Build Coastguard Worker #include "absl/base/internal/spinlock.h"
45*9356374aSAndroid Build Coastguard Worker 
46*9356374aSAndroid Build Coastguard Worker // Do not use STL.   This module does not use standard memory allocation.
47*9356374aSAndroid Build Coastguard Worker 
48*9356374aSAndroid Build Coastguard Worker namespace absl {
49*9356374aSAndroid Build Coastguard Worker ABSL_NAMESPACE_BEGIN
50*9356374aSAndroid Build Coastguard Worker namespace synchronization_internal {
51*9356374aSAndroid Build Coastguard Worker 
52*9356374aSAndroid Build Coastguard Worker namespace {
53*9356374aSAndroid Build Coastguard Worker 
54*9356374aSAndroid Build Coastguard Worker // Avoid LowLevelAlloc's default arena since it calls malloc hooks in
55*9356374aSAndroid Build Coastguard Worker // which people are doing things like acquiring Mutexes.
56*9356374aSAndroid Build Coastguard Worker ABSL_CONST_INIT static absl::base_internal::SpinLock arena_mu(
57*9356374aSAndroid Build Coastguard Worker     absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
58*9356374aSAndroid Build Coastguard Worker ABSL_CONST_INIT static base_internal::LowLevelAlloc::Arena* arena;
59*9356374aSAndroid Build Coastguard Worker 
InitArenaIfNecessary()60*9356374aSAndroid Build Coastguard Worker static void InitArenaIfNecessary() {
61*9356374aSAndroid Build Coastguard Worker   arena_mu.Lock();
62*9356374aSAndroid Build Coastguard Worker   if (arena == nullptr) {
63*9356374aSAndroid Build Coastguard Worker     arena = base_internal::LowLevelAlloc::NewArena(0);
64*9356374aSAndroid Build Coastguard Worker   }
65*9356374aSAndroid Build Coastguard Worker   arena_mu.Unlock();
66*9356374aSAndroid Build Coastguard Worker }
67*9356374aSAndroid Build Coastguard Worker 
68*9356374aSAndroid Build Coastguard Worker // Number of inlined elements in Vec.  Hash table implementation
69*9356374aSAndroid Build Coastguard Worker // relies on this being a power of two.
70*9356374aSAndroid Build Coastguard Worker static const uint32_t kInline = 8;
71*9356374aSAndroid Build Coastguard Worker 
72*9356374aSAndroid Build Coastguard Worker // A simple LowLevelAlloc based resizable vector with inlined storage
73*9356374aSAndroid Build Coastguard Worker // for a few elements.  T must be a plain type since constructor
74*9356374aSAndroid Build Coastguard Worker // and destructor are not run on elements of type T managed by Vec.
75*9356374aSAndroid Build Coastguard Worker template <typename T>
76*9356374aSAndroid Build Coastguard Worker class Vec {
77*9356374aSAndroid Build Coastguard Worker  public:
Vec()78*9356374aSAndroid Build Coastguard Worker   Vec() { Init(); }
~Vec()79*9356374aSAndroid Build Coastguard Worker   ~Vec() { Discard(); }
80*9356374aSAndroid Build Coastguard Worker 
clear()81*9356374aSAndroid Build Coastguard Worker   void clear() {
82*9356374aSAndroid Build Coastguard Worker     Discard();
83*9356374aSAndroid Build Coastguard Worker     Init();
84*9356374aSAndroid Build Coastguard Worker   }
85*9356374aSAndroid Build Coastguard Worker 
empty() const86*9356374aSAndroid Build Coastguard Worker   bool empty() const { return size_ == 0; }
size() const87*9356374aSAndroid Build Coastguard Worker   uint32_t size() const { return size_; }
begin()88*9356374aSAndroid Build Coastguard Worker   T* begin() { return ptr_; }
end()89*9356374aSAndroid Build Coastguard Worker   T* end() { return ptr_ + size_; }
operator [](uint32_t i) const90*9356374aSAndroid Build Coastguard Worker   const T& operator[](uint32_t i) const { return ptr_[i]; }
operator [](uint32_t i)91*9356374aSAndroid Build Coastguard Worker   T& operator[](uint32_t i) { return ptr_[i]; }
back() const92*9356374aSAndroid Build Coastguard Worker   const T& back() const { return ptr_[size_-1]; }
pop_back()93*9356374aSAndroid Build Coastguard Worker   void pop_back() { size_--; }
94*9356374aSAndroid Build Coastguard Worker 
push_back(const T & v)95*9356374aSAndroid Build Coastguard Worker   void push_back(const T& v) {
96*9356374aSAndroid Build Coastguard Worker     if (size_ == capacity_) Grow(size_ + 1);
97*9356374aSAndroid Build Coastguard Worker     ptr_[size_] = v;
98*9356374aSAndroid Build Coastguard Worker     size_++;
99*9356374aSAndroid Build Coastguard Worker   }
100*9356374aSAndroid Build Coastguard Worker 
resize(uint32_t n)101*9356374aSAndroid Build Coastguard Worker   void resize(uint32_t n) {
102*9356374aSAndroid Build Coastguard Worker     if (n > capacity_) Grow(n);
103*9356374aSAndroid Build Coastguard Worker     size_ = n;
104*9356374aSAndroid Build Coastguard Worker   }
105*9356374aSAndroid Build Coastguard Worker 
fill(const T & val)106*9356374aSAndroid Build Coastguard Worker   void fill(const T& val) {
107*9356374aSAndroid Build Coastguard Worker     for (uint32_t i = 0; i < size(); i++) {
108*9356374aSAndroid Build Coastguard Worker       ptr_[i] = val;
109*9356374aSAndroid Build Coastguard Worker     }
110*9356374aSAndroid Build Coastguard Worker   }
111*9356374aSAndroid Build Coastguard Worker 
112*9356374aSAndroid Build Coastguard Worker   // Guarantees src is empty at end.
113*9356374aSAndroid Build Coastguard Worker   // Provided for the hash table resizing code below.
MoveFrom(Vec<T> * src)114*9356374aSAndroid Build Coastguard Worker   void MoveFrom(Vec<T>* src) {
115*9356374aSAndroid Build Coastguard Worker     if (src->ptr_ == src->space_) {
116*9356374aSAndroid Build Coastguard Worker       // Need to actually copy
117*9356374aSAndroid Build Coastguard Worker       resize(src->size_);
118*9356374aSAndroid Build Coastguard Worker       std::copy_n(src->ptr_, src->size_, ptr_);
119*9356374aSAndroid Build Coastguard Worker       src->size_ = 0;
120*9356374aSAndroid Build Coastguard Worker     } else {
121*9356374aSAndroid Build Coastguard Worker       Discard();
122*9356374aSAndroid Build Coastguard Worker       ptr_ = src->ptr_;
123*9356374aSAndroid Build Coastguard Worker       size_ = src->size_;
124*9356374aSAndroid Build Coastguard Worker       capacity_ = src->capacity_;
125*9356374aSAndroid Build Coastguard Worker       src->Init();
126*9356374aSAndroid Build Coastguard Worker     }
127*9356374aSAndroid Build Coastguard Worker   }
128*9356374aSAndroid Build Coastguard Worker 
129*9356374aSAndroid Build Coastguard Worker  private:
130*9356374aSAndroid Build Coastguard Worker   T* ptr_;
131*9356374aSAndroid Build Coastguard Worker   T space_[kInline];
132*9356374aSAndroid Build Coastguard Worker   uint32_t size_;
133*9356374aSAndroid Build Coastguard Worker   uint32_t capacity_;
134*9356374aSAndroid Build Coastguard Worker 
Init()135*9356374aSAndroid Build Coastguard Worker   void Init() {
136*9356374aSAndroid Build Coastguard Worker     ptr_ = space_;
137*9356374aSAndroid Build Coastguard Worker     size_ = 0;
138*9356374aSAndroid Build Coastguard Worker     capacity_ = kInline;
139*9356374aSAndroid Build Coastguard Worker   }
140*9356374aSAndroid Build Coastguard Worker 
Discard()141*9356374aSAndroid Build Coastguard Worker   void Discard() {
142*9356374aSAndroid Build Coastguard Worker     if (ptr_ != space_) base_internal::LowLevelAlloc::Free(ptr_);
143*9356374aSAndroid Build Coastguard Worker   }
144*9356374aSAndroid Build Coastguard Worker 
Grow(uint32_t n)145*9356374aSAndroid Build Coastguard Worker   void Grow(uint32_t n) {
146*9356374aSAndroid Build Coastguard Worker     while (capacity_ < n) {
147*9356374aSAndroid Build Coastguard Worker       capacity_ *= 2;
148*9356374aSAndroid Build Coastguard Worker     }
149*9356374aSAndroid Build Coastguard Worker     size_t request = static_cast<size_t>(capacity_) * sizeof(T);
150*9356374aSAndroid Build Coastguard Worker     T* copy = static_cast<T*>(
151*9356374aSAndroid Build Coastguard Worker         base_internal::LowLevelAlloc::AllocWithArena(request, arena));
152*9356374aSAndroid Build Coastguard Worker     std::copy_n(ptr_, size_, copy);
153*9356374aSAndroid Build Coastguard Worker     Discard();
154*9356374aSAndroid Build Coastguard Worker     ptr_ = copy;
155*9356374aSAndroid Build Coastguard Worker   }
156*9356374aSAndroid Build Coastguard Worker 
157*9356374aSAndroid Build Coastguard Worker   Vec(const Vec&) = delete;
158*9356374aSAndroid Build Coastguard Worker   Vec& operator=(const Vec&) = delete;
159*9356374aSAndroid Build Coastguard Worker };
160*9356374aSAndroid Build Coastguard Worker 
161*9356374aSAndroid Build Coastguard Worker // A hash set of non-negative int32_t that uses Vec for its underlying storage.
162*9356374aSAndroid Build Coastguard Worker class NodeSet {
163*9356374aSAndroid Build Coastguard Worker  public:
NodeSet()164*9356374aSAndroid Build Coastguard Worker   NodeSet() { Init(); }
165*9356374aSAndroid Build Coastguard Worker 
clear()166*9356374aSAndroid Build Coastguard Worker   void clear() { Init(); }
contains(int32_t v) const167*9356374aSAndroid Build Coastguard Worker   bool contains(int32_t v) const { return table_[FindIndex(v)] == v; }
168*9356374aSAndroid Build Coastguard Worker 
insert(int32_t v)169*9356374aSAndroid Build Coastguard Worker   bool insert(int32_t v) {
170*9356374aSAndroid Build Coastguard Worker     uint32_t i = FindIndex(v);
171*9356374aSAndroid Build Coastguard Worker     if (table_[i] == v) {
172*9356374aSAndroid Build Coastguard Worker       return false;
173*9356374aSAndroid Build Coastguard Worker     }
174*9356374aSAndroid Build Coastguard Worker     if (table_[i] == kEmpty) {
175*9356374aSAndroid Build Coastguard Worker       // Only inserting over an empty cell increases the number of occupied
176*9356374aSAndroid Build Coastguard Worker       // slots.
177*9356374aSAndroid Build Coastguard Worker       occupied_++;
178*9356374aSAndroid Build Coastguard Worker     }
179*9356374aSAndroid Build Coastguard Worker     table_[i] = v;
180*9356374aSAndroid Build Coastguard Worker     // Double when 75% full.
181*9356374aSAndroid Build Coastguard Worker     if (occupied_ >= table_.size() - table_.size()/4) Grow();
182*9356374aSAndroid Build Coastguard Worker     return true;
183*9356374aSAndroid Build Coastguard Worker   }
184*9356374aSAndroid Build Coastguard Worker 
erase(int32_t v)185*9356374aSAndroid Build Coastguard Worker   void erase(int32_t v) {
186*9356374aSAndroid Build Coastguard Worker     uint32_t i = FindIndex(v);
187*9356374aSAndroid Build Coastguard Worker     if (table_[i] == v) {
188*9356374aSAndroid Build Coastguard Worker       table_[i] = kDel;
189*9356374aSAndroid Build Coastguard Worker     }
190*9356374aSAndroid Build Coastguard Worker   }
191*9356374aSAndroid Build Coastguard Worker 
192*9356374aSAndroid Build Coastguard Worker   // Iteration: is done via HASH_FOR_EACH
193*9356374aSAndroid Build Coastguard Worker   // Example:
194*9356374aSAndroid Build Coastguard Worker   //    HASH_FOR_EACH(elem, node->out) { ... }
195*9356374aSAndroid Build Coastguard Worker #define HASH_FOR_EACH(elem, eset) \
196*9356374aSAndroid Build Coastguard Worker   for (int32_t elem, _cursor = 0; (eset).Next(&_cursor, &elem); )
Next(int32_t * cursor,int32_t * elem)197*9356374aSAndroid Build Coastguard Worker   bool Next(int32_t* cursor, int32_t* elem) {
198*9356374aSAndroid Build Coastguard Worker     while (static_cast<uint32_t>(*cursor) < table_.size()) {
199*9356374aSAndroid Build Coastguard Worker       int32_t v = table_[static_cast<uint32_t>(*cursor)];
200*9356374aSAndroid Build Coastguard Worker       (*cursor)++;
201*9356374aSAndroid Build Coastguard Worker       if (v >= 0) {
202*9356374aSAndroid Build Coastguard Worker         *elem = v;
203*9356374aSAndroid Build Coastguard Worker         return true;
204*9356374aSAndroid Build Coastguard Worker       }
205*9356374aSAndroid Build Coastguard Worker     }
206*9356374aSAndroid Build Coastguard Worker     return false;
207*9356374aSAndroid Build Coastguard Worker   }
208*9356374aSAndroid Build Coastguard Worker 
209*9356374aSAndroid Build Coastguard Worker  private:
210*9356374aSAndroid Build Coastguard Worker   enum : int32_t { kEmpty = -1, kDel = -2 };
211*9356374aSAndroid Build Coastguard Worker   Vec<int32_t> table_;
212*9356374aSAndroid Build Coastguard Worker   uint32_t occupied_;     // Count of non-empty slots (includes deleted slots)
213*9356374aSAndroid Build Coastguard Worker 
Hash(int32_t a)214*9356374aSAndroid Build Coastguard Worker   static uint32_t Hash(int32_t a) { return static_cast<uint32_t>(a) * 41; }
215*9356374aSAndroid Build Coastguard Worker 
216*9356374aSAndroid Build Coastguard Worker   // Return index for storing v.  May return an empty index or deleted index
FindIndex(int32_t v) const217*9356374aSAndroid Build Coastguard Worker   uint32_t FindIndex(int32_t v) const {
218*9356374aSAndroid Build Coastguard Worker     // Search starting at hash index.
219*9356374aSAndroid Build Coastguard Worker     const uint32_t mask = table_.size() - 1;
220*9356374aSAndroid Build Coastguard Worker     uint32_t i = Hash(v) & mask;
221*9356374aSAndroid Build Coastguard Worker     uint32_t deleted_index = 0;  // index of first deleted element we see
222*9356374aSAndroid Build Coastguard Worker     bool seen_deleted_element = false;
223*9356374aSAndroid Build Coastguard Worker     while (true) {
224*9356374aSAndroid Build Coastguard Worker       int32_t e = table_[i];
225*9356374aSAndroid Build Coastguard Worker       if (v == e) {
226*9356374aSAndroid Build Coastguard Worker         return i;
227*9356374aSAndroid Build Coastguard Worker       } else if (e == kEmpty) {
228*9356374aSAndroid Build Coastguard Worker         // Return any previously encountered deleted slot.
229*9356374aSAndroid Build Coastguard Worker         return seen_deleted_element ? deleted_index : i;
230*9356374aSAndroid Build Coastguard Worker       } else if (e == kDel && !seen_deleted_element) {
231*9356374aSAndroid Build Coastguard Worker         // Keep searching since v might be present later.
232*9356374aSAndroid Build Coastguard Worker         deleted_index = i;
233*9356374aSAndroid Build Coastguard Worker         seen_deleted_element = true;
234*9356374aSAndroid Build Coastguard Worker       }
235*9356374aSAndroid Build Coastguard Worker       i = (i + 1) & mask;  // Linear probing; quadratic is slightly slower.
236*9356374aSAndroid Build Coastguard Worker     }
237*9356374aSAndroid Build Coastguard Worker   }
238*9356374aSAndroid Build Coastguard Worker 
Init()239*9356374aSAndroid Build Coastguard Worker   void Init() {
240*9356374aSAndroid Build Coastguard Worker     table_.clear();
241*9356374aSAndroid Build Coastguard Worker     table_.resize(kInline);
242*9356374aSAndroid Build Coastguard Worker     table_.fill(kEmpty);
243*9356374aSAndroid Build Coastguard Worker     occupied_ = 0;
244*9356374aSAndroid Build Coastguard Worker   }
245*9356374aSAndroid Build Coastguard Worker 
Grow()246*9356374aSAndroid Build Coastguard Worker   void Grow() {
247*9356374aSAndroid Build Coastguard Worker     Vec<int32_t> copy;
248*9356374aSAndroid Build Coastguard Worker     copy.MoveFrom(&table_);
249*9356374aSAndroid Build Coastguard Worker     occupied_ = 0;
250*9356374aSAndroid Build Coastguard Worker     table_.resize(copy.size() * 2);
251*9356374aSAndroid Build Coastguard Worker     table_.fill(kEmpty);
252*9356374aSAndroid Build Coastguard Worker 
253*9356374aSAndroid Build Coastguard Worker     for (const auto& e : copy) {
254*9356374aSAndroid Build Coastguard Worker       if (e >= 0) insert(e);
255*9356374aSAndroid Build Coastguard Worker     }
256*9356374aSAndroid Build Coastguard Worker   }
257*9356374aSAndroid Build Coastguard Worker 
258*9356374aSAndroid Build Coastguard Worker   NodeSet(const NodeSet&) = delete;
259*9356374aSAndroid Build Coastguard Worker   NodeSet& operator=(const NodeSet&) = delete;
260*9356374aSAndroid Build Coastguard Worker };
261*9356374aSAndroid Build Coastguard Worker 
262*9356374aSAndroid Build Coastguard Worker // We encode a node index and a node version in GraphId.  The version
263*9356374aSAndroid Build Coastguard Worker // number is incremented when the GraphId is freed which automatically
264*9356374aSAndroid Build Coastguard Worker // invalidates all copies of the GraphId.
265*9356374aSAndroid Build Coastguard Worker 
MakeId(int32_t index,uint32_t version)266*9356374aSAndroid Build Coastguard Worker inline GraphId MakeId(int32_t index, uint32_t version) {
267*9356374aSAndroid Build Coastguard Worker   GraphId g;
268*9356374aSAndroid Build Coastguard Worker   g.handle =
269*9356374aSAndroid Build Coastguard Worker       (static_cast<uint64_t>(version) << 32) | static_cast<uint32_t>(index);
270*9356374aSAndroid Build Coastguard Worker   return g;
271*9356374aSAndroid Build Coastguard Worker }
272*9356374aSAndroid Build Coastguard Worker 
NodeIndex(GraphId id)273*9356374aSAndroid Build Coastguard Worker inline int32_t NodeIndex(GraphId id) {
274*9356374aSAndroid Build Coastguard Worker   return static_cast<int32_t>(id.handle);
275*9356374aSAndroid Build Coastguard Worker }
276*9356374aSAndroid Build Coastguard Worker 
NodeVersion(GraphId id)277*9356374aSAndroid Build Coastguard Worker inline uint32_t NodeVersion(GraphId id) {
278*9356374aSAndroid Build Coastguard Worker   return static_cast<uint32_t>(id.handle >> 32);
279*9356374aSAndroid Build Coastguard Worker }
280*9356374aSAndroid Build Coastguard Worker 
281*9356374aSAndroid Build Coastguard Worker struct Node {
282*9356374aSAndroid Build Coastguard Worker   int32_t rank;               // rank number assigned by Pearce-Kelly algorithm
283*9356374aSAndroid Build Coastguard Worker   uint32_t version;           // Current version number
284*9356374aSAndroid Build Coastguard Worker   int32_t next_hash;          // Next entry in hash table
285*9356374aSAndroid Build Coastguard Worker   bool visited;               // Temporary marker used by depth-first-search
286*9356374aSAndroid Build Coastguard Worker   uintptr_t masked_ptr;       // User-supplied pointer
287*9356374aSAndroid Build Coastguard Worker   NodeSet in;                 // List of immediate predecessor nodes in graph
288*9356374aSAndroid Build Coastguard Worker   NodeSet out;                // List of immediate successor nodes in graph
289*9356374aSAndroid Build Coastguard Worker   int priority;               // Priority of recorded stack trace.
290*9356374aSAndroid Build Coastguard Worker   int nstack;                 // Depth of recorded stack trace.
291*9356374aSAndroid Build Coastguard Worker   void* stack[40];            // stack[0,nstack-1] holds stack trace for node.
292*9356374aSAndroid Build Coastguard Worker };
293*9356374aSAndroid Build Coastguard Worker 
294*9356374aSAndroid Build Coastguard Worker // Hash table for pointer to node index lookups.
295*9356374aSAndroid Build Coastguard Worker class PointerMap {
296*9356374aSAndroid Build Coastguard Worker  public:
PointerMap(const Vec<Node * > * nodes)297*9356374aSAndroid Build Coastguard Worker   explicit PointerMap(const Vec<Node*>* nodes) : nodes_(nodes) {
298*9356374aSAndroid Build Coastguard Worker     table_.fill(-1);
299*9356374aSAndroid Build Coastguard Worker   }
300*9356374aSAndroid Build Coastguard Worker 
Find(void * ptr)301*9356374aSAndroid Build Coastguard Worker   int32_t Find(void* ptr) {
302*9356374aSAndroid Build Coastguard Worker     auto masked = base_internal::HidePtr(ptr);
303*9356374aSAndroid Build Coastguard Worker     for (int32_t i = table_[Hash(ptr)]; i != -1;) {
304*9356374aSAndroid Build Coastguard Worker       Node* n = (*nodes_)[static_cast<uint32_t>(i)];
305*9356374aSAndroid Build Coastguard Worker       if (n->masked_ptr == masked) return i;
306*9356374aSAndroid Build Coastguard Worker       i = n->next_hash;
307*9356374aSAndroid Build Coastguard Worker     }
308*9356374aSAndroid Build Coastguard Worker     return -1;
309*9356374aSAndroid Build Coastguard Worker   }
310*9356374aSAndroid Build Coastguard Worker 
Add(void * ptr,int32_t i)311*9356374aSAndroid Build Coastguard Worker   void Add(void* ptr, int32_t i) {
312*9356374aSAndroid Build Coastguard Worker     int32_t* head = &table_[Hash(ptr)];
313*9356374aSAndroid Build Coastguard Worker     (*nodes_)[static_cast<uint32_t>(i)]->next_hash = *head;
314*9356374aSAndroid Build Coastguard Worker     *head = i;
315*9356374aSAndroid Build Coastguard Worker   }
316*9356374aSAndroid Build Coastguard Worker 
Remove(void * ptr)317*9356374aSAndroid Build Coastguard Worker   int32_t Remove(void* ptr) {
318*9356374aSAndroid Build Coastguard Worker     // Advance through linked list while keeping track of the
319*9356374aSAndroid Build Coastguard Worker     // predecessor slot that points to the current entry.
320*9356374aSAndroid Build Coastguard Worker     auto masked = base_internal::HidePtr(ptr);
321*9356374aSAndroid Build Coastguard Worker     for (int32_t* slot = &table_[Hash(ptr)]; *slot != -1; ) {
322*9356374aSAndroid Build Coastguard Worker       int32_t index = *slot;
323*9356374aSAndroid Build Coastguard Worker       Node* n = (*nodes_)[static_cast<uint32_t>(index)];
324*9356374aSAndroid Build Coastguard Worker       if (n->masked_ptr == masked) {
325*9356374aSAndroid Build Coastguard Worker         *slot = n->next_hash;  // Remove n from linked list
326*9356374aSAndroid Build Coastguard Worker         n->next_hash = -1;
327*9356374aSAndroid Build Coastguard Worker         return index;
328*9356374aSAndroid Build Coastguard Worker       }
329*9356374aSAndroid Build Coastguard Worker       slot = &n->next_hash;
330*9356374aSAndroid Build Coastguard Worker     }
331*9356374aSAndroid Build Coastguard Worker     return -1;
332*9356374aSAndroid Build Coastguard Worker   }
333*9356374aSAndroid Build Coastguard Worker 
334*9356374aSAndroid Build Coastguard Worker  private:
335*9356374aSAndroid Build Coastguard Worker   // Number of buckets in hash table for pointer lookups.
336*9356374aSAndroid Build Coastguard Worker   static constexpr uint32_t kHashTableSize = 262139;  // should be prime
337*9356374aSAndroid Build Coastguard Worker 
338*9356374aSAndroid Build Coastguard Worker   const Vec<Node*>* nodes_;
339*9356374aSAndroid Build Coastguard Worker   std::array<int32_t, kHashTableSize> table_;
340*9356374aSAndroid Build Coastguard Worker 
Hash(void * ptr)341*9356374aSAndroid Build Coastguard Worker   static uint32_t Hash(void* ptr) {
342*9356374aSAndroid Build Coastguard Worker     return reinterpret_cast<uintptr_t>(ptr) % kHashTableSize;
343*9356374aSAndroid Build Coastguard Worker   }
344*9356374aSAndroid Build Coastguard Worker };
345*9356374aSAndroid Build Coastguard Worker 
346*9356374aSAndroid Build Coastguard Worker }  // namespace
347*9356374aSAndroid Build Coastguard Worker 
348*9356374aSAndroid Build Coastguard Worker struct GraphCycles::Rep {
349*9356374aSAndroid Build Coastguard Worker   Vec<Node*> nodes_;
350*9356374aSAndroid Build Coastguard Worker   Vec<int32_t> free_nodes_;  // Indices for unused entries in nodes_
351*9356374aSAndroid Build Coastguard Worker   PointerMap ptrmap_;
352*9356374aSAndroid Build Coastguard Worker 
353*9356374aSAndroid Build Coastguard Worker   // Temporary state.
354*9356374aSAndroid Build Coastguard Worker   Vec<int32_t> deltaf_;  // Results of forward DFS
355*9356374aSAndroid Build Coastguard Worker   Vec<int32_t> deltab_;  // Results of backward DFS
356*9356374aSAndroid Build Coastguard Worker   Vec<int32_t> list_;    // All nodes to reprocess
357*9356374aSAndroid Build Coastguard Worker   Vec<int32_t> merged_;  // Rank values to assign to list_ entries
358*9356374aSAndroid Build Coastguard Worker   Vec<int32_t> stack_;   // Emulates recursion stack for depth-first searches
359*9356374aSAndroid Build Coastguard Worker 
Repabsl::synchronization_internal::GraphCycles::Rep360*9356374aSAndroid Build Coastguard Worker   Rep() : ptrmap_(&nodes_) {}
361*9356374aSAndroid Build Coastguard Worker };
362*9356374aSAndroid Build Coastguard Worker 
FindNode(GraphCycles::Rep * rep,GraphId id)363*9356374aSAndroid Build Coastguard Worker static Node* FindNode(GraphCycles::Rep* rep, GraphId id) {
364*9356374aSAndroid Build Coastguard Worker   Node* n = rep->nodes_[static_cast<uint32_t>(NodeIndex(id))];
365*9356374aSAndroid Build Coastguard Worker   return (n->version == NodeVersion(id)) ? n : nullptr;
366*9356374aSAndroid Build Coastguard Worker }
367*9356374aSAndroid Build Coastguard Worker 
TestOnlyAddNodes(uint32_t n)368*9356374aSAndroid Build Coastguard Worker void GraphCycles::TestOnlyAddNodes(uint32_t n) {
369*9356374aSAndroid Build Coastguard Worker   uint32_t old_size = rep_->nodes_.size();
370*9356374aSAndroid Build Coastguard Worker   rep_->nodes_.resize(n);
371*9356374aSAndroid Build Coastguard Worker   for (auto i = old_size; i < n; ++i) {
372*9356374aSAndroid Build Coastguard Worker     rep_->nodes_[i] = nullptr;
373*9356374aSAndroid Build Coastguard Worker   }
374*9356374aSAndroid Build Coastguard Worker }
375*9356374aSAndroid Build Coastguard Worker 
GraphCycles()376*9356374aSAndroid Build Coastguard Worker GraphCycles::GraphCycles() {
377*9356374aSAndroid Build Coastguard Worker   InitArenaIfNecessary();
378*9356374aSAndroid Build Coastguard Worker   rep_ = new (base_internal::LowLevelAlloc::AllocWithArena(sizeof(Rep), arena))
379*9356374aSAndroid Build Coastguard Worker       Rep;
380*9356374aSAndroid Build Coastguard Worker }
381*9356374aSAndroid Build Coastguard Worker 
~GraphCycles()382*9356374aSAndroid Build Coastguard Worker GraphCycles::~GraphCycles() {
383*9356374aSAndroid Build Coastguard Worker   for (auto* node : rep_->nodes_) {
384*9356374aSAndroid Build Coastguard Worker     if (node == nullptr) { continue; }
385*9356374aSAndroid Build Coastguard Worker     node->Node::~Node();
386*9356374aSAndroid Build Coastguard Worker     base_internal::LowLevelAlloc::Free(node);
387*9356374aSAndroid Build Coastguard Worker   }
388*9356374aSAndroid Build Coastguard Worker   rep_->Rep::~Rep();
389*9356374aSAndroid Build Coastguard Worker   base_internal::LowLevelAlloc::Free(rep_);
390*9356374aSAndroid Build Coastguard Worker }
391*9356374aSAndroid Build Coastguard Worker 
CheckInvariants() const392*9356374aSAndroid Build Coastguard Worker bool GraphCycles::CheckInvariants() const {
393*9356374aSAndroid Build Coastguard Worker   Rep* r = rep_;
394*9356374aSAndroid Build Coastguard Worker   NodeSet ranks;  // Set of ranks seen so far.
395*9356374aSAndroid Build Coastguard Worker   for (uint32_t x = 0; x < r->nodes_.size(); x++) {
396*9356374aSAndroid Build Coastguard Worker     Node* nx = r->nodes_[x];
397*9356374aSAndroid Build Coastguard Worker     void* ptr = base_internal::UnhidePtr<void>(nx->masked_ptr);
398*9356374aSAndroid Build Coastguard Worker     if (ptr != nullptr && static_cast<uint32_t>(r->ptrmap_.Find(ptr)) != x) {
399*9356374aSAndroid Build Coastguard Worker       ABSL_RAW_LOG(FATAL, "Did not find live node in hash table %" PRIu32 " %p",
400*9356374aSAndroid Build Coastguard Worker                    x, ptr);
401*9356374aSAndroid Build Coastguard Worker     }
402*9356374aSAndroid Build Coastguard Worker     if (nx->visited) {
403*9356374aSAndroid Build Coastguard Worker       ABSL_RAW_LOG(FATAL, "Did not clear visited marker on node %" PRIu32, x);
404*9356374aSAndroid Build Coastguard Worker     }
405*9356374aSAndroid Build Coastguard Worker     if (!ranks.insert(nx->rank)) {
406*9356374aSAndroid Build Coastguard Worker       ABSL_RAW_LOG(FATAL, "Duplicate occurrence of rank %" PRId32, nx->rank);
407*9356374aSAndroid Build Coastguard Worker     }
408*9356374aSAndroid Build Coastguard Worker     HASH_FOR_EACH(y, nx->out) {
409*9356374aSAndroid Build Coastguard Worker       Node* ny = r->nodes_[static_cast<uint32_t>(y)];
410*9356374aSAndroid Build Coastguard Worker       if (nx->rank >= ny->rank) {
411*9356374aSAndroid Build Coastguard Worker         ABSL_RAW_LOG(FATAL,
412*9356374aSAndroid Build Coastguard Worker                      "Edge %" PRIu32 " ->%" PRId32
413*9356374aSAndroid Build Coastguard Worker                      " has bad rank assignment %" PRId32 "->%" PRId32,
414*9356374aSAndroid Build Coastguard Worker                      x, y, nx->rank, ny->rank);
415*9356374aSAndroid Build Coastguard Worker       }
416*9356374aSAndroid Build Coastguard Worker     }
417*9356374aSAndroid Build Coastguard Worker   }
418*9356374aSAndroid Build Coastguard Worker   return true;
419*9356374aSAndroid Build Coastguard Worker }
420*9356374aSAndroid Build Coastguard Worker 
GetId(void * ptr)421*9356374aSAndroid Build Coastguard Worker GraphId GraphCycles::GetId(void* ptr) {
422*9356374aSAndroid Build Coastguard Worker   int32_t i = rep_->ptrmap_.Find(ptr);
423*9356374aSAndroid Build Coastguard Worker   if (i != -1) {
424*9356374aSAndroid Build Coastguard Worker     return MakeId(i, rep_->nodes_[static_cast<uint32_t>(i)]->version);
425*9356374aSAndroid Build Coastguard Worker   } else if (rep_->free_nodes_.empty()) {
426*9356374aSAndroid Build Coastguard Worker     Node* n =
427*9356374aSAndroid Build Coastguard Worker         new (base_internal::LowLevelAlloc::AllocWithArena(sizeof(Node), arena))
428*9356374aSAndroid Build Coastguard Worker             Node;
429*9356374aSAndroid Build Coastguard Worker     n->version = 1;  // Avoid 0 since it is used by InvalidGraphId()
430*9356374aSAndroid Build Coastguard Worker     n->visited = false;
431*9356374aSAndroid Build Coastguard Worker     n->rank = static_cast<int32_t>(rep_->nodes_.size());
432*9356374aSAndroid Build Coastguard Worker     n->masked_ptr = base_internal::HidePtr(ptr);
433*9356374aSAndroid Build Coastguard Worker     n->nstack = 0;
434*9356374aSAndroid Build Coastguard Worker     n->priority = 0;
435*9356374aSAndroid Build Coastguard Worker     rep_->nodes_.push_back(n);
436*9356374aSAndroid Build Coastguard Worker     rep_->ptrmap_.Add(ptr, n->rank);
437*9356374aSAndroid Build Coastguard Worker     return MakeId(n->rank, n->version);
438*9356374aSAndroid Build Coastguard Worker   } else {
439*9356374aSAndroid Build Coastguard Worker     // Preserve preceding rank since the set of ranks in use must be
440*9356374aSAndroid Build Coastguard Worker     // a permutation of [0,rep_->nodes_.size()-1].
441*9356374aSAndroid Build Coastguard Worker     int32_t r = rep_->free_nodes_.back();
442*9356374aSAndroid Build Coastguard Worker     rep_->free_nodes_.pop_back();
443*9356374aSAndroid Build Coastguard Worker     Node* n = rep_->nodes_[static_cast<uint32_t>(r)];
444*9356374aSAndroid Build Coastguard Worker     n->masked_ptr = base_internal::HidePtr(ptr);
445*9356374aSAndroid Build Coastguard Worker     n->nstack = 0;
446*9356374aSAndroid Build Coastguard Worker     n->priority = 0;
447*9356374aSAndroid Build Coastguard Worker     rep_->ptrmap_.Add(ptr, r);
448*9356374aSAndroid Build Coastguard Worker     return MakeId(r, n->version);
449*9356374aSAndroid Build Coastguard Worker   }
450*9356374aSAndroid Build Coastguard Worker }
451*9356374aSAndroid Build Coastguard Worker 
RemoveNode(void * ptr)452*9356374aSAndroid Build Coastguard Worker void GraphCycles::RemoveNode(void* ptr) {
453*9356374aSAndroid Build Coastguard Worker   int32_t i = rep_->ptrmap_.Remove(ptr);
454*9356374aSAndroid Build Coastguard Worker   if (i == -1) {
455*9356374aSAndroid Build Coastguard Worker     return;
456*9356374aSAndroid Build Coastguard Worker   }
457*9356374aSAndroid Build Coastguard Worker   Node* x = rep_->nodes_[static_cast<uint32_t>(i)];
458*9356374aSAndroid Build Coastguard Worker   HASH_FOR_EACH(y, x->out) {
459*9356374aSAndroid Build Coastguard Worker     rep_->nodes_[static_cast<uint32_t>(y)]->in.erase(i);
460*9356374aSAndroid Build Coastguard Worker   }
461*9356374aSAndroid Build Coastguard Worker   HASH_FOR_EACH(y, x->in) {
462*9356374aSAndroid Build Coastguard Worker     rep_->nodes_[static_cast<uint32_t>(y)]->out.erase(i);
463*9356374aSAndroid Build Coastguard Worker   }
464*9356374aSAndroid Build Coastguard Worker   x->in.clear();
465*9356374aSAndroid Build Coastguard Worker   x->out.clear();
466*9356374aSAndroid Build Coastguard Worker   x->masked_ptr = base_internal::HidePtr<void>(nullptr);
467*9356374aSAndroid Build Coastguard Worker   if (x->version == std::numeric_limits<uint32_t>::max()) {
468*9356374aSAndroid Build Coastguard Worker     // Cannot use x any more
469*9356374aSAndroid Build Coastguard Worker   } else {
470*9356374aSAndroid Build Coastguard Worker     x->version++;  // Invalidates all copies of node.
471*9356374aSAndroid Build Coastguard Worker     rep_->free_nodes_.push_back(i);
472*9356374aSAndroid Build Coastguard Worker   }
473*9356374aSAndroid Build Coastguard Worker }
474*9356374aSAndroid Build Coastguard Worker 
Ptr(GraphId id)475*9356374aSAndroid Build Coastguard Worker void* GraphCycles::Ptr(GraphId id) {
476*9356374aSAndroid Build Coastguard Worker   Node* n = FindNode(rep_, id);
477*9356374aSAndroid Build Coastguard Worker   return n == nullptr ? nullptr
478*9356374aSAndroid Build Coastguard Worker                       : base_internal::UnhidePtr<void>(n->masked_ptr);
479*9356374aSAndroid Build Coastguard Worker }
480*9356374aSAndroid Build Coastguard Worker 
HasNode(GraphId node)481*9356374aSAndroid Build Coastguard Worker bool GraphCycles::HasNode(GraphId node) {
482*9356374aSAndroid Build Coastguard Worker   return FindNode(rep_, node) != nullptr;
483*9356374aSAndroid Build Coastguard Worker }
484*9356374aSAndroid Build Coastguard Worker 
HasEdge(GraphId x,GraphId y) const485*9356374aSAndroid Build Coastguard Worker bool GraphCycles::HasEdge(GraphId x, GraphId y) const {
486*9356374aSAndroid Build Coastguard Worker   Node* xn = FindNode(rep_, x);
487*9356374aSAndroid Build Coastguard Worker   return xn && FindNode(rep_, y) && xn->out.contains(NodeIndex(y));
488*9356374aSAndroid Build Coastguard Worker }
489*9356374aSAndroid Build Coastguard Worker 
RemoveEdge(GraphId x,GraphId y)490*9356374aSAndroid Build Coastguard Worker void GraphCycles::RemoveEdge(GraphId x, GraphId y) {
491*9356374aSAndroid Build Coastguard Worker   Node* xn = FindNode(rep_, x);
492*9356374aSAndroid Build Coastguard Worker   Node* yn = FindNode(rep_, y);
493*9356374aSAndroid Build Coastguard Worker   if (xn && yn) {
494*9356374aSAndroid Build Coastguard Worker     xn->out.erase(NodeIndex(y));
495*9356374aSAndroid Build Coastguard Worker     yn->in.erase(NodeIndex(x));
496*9356374aSAndroid Build Coastguard Worker     // No need to update the rank assignment since a previous valid
497*9356374aSAndroid Build Coastguard Worker     // rank assignment remains valid after an edge deletion.
498*9356374aSAndroid Build Coastguard Worker   }
499*9356374aSAndroid Build Coastguard Worker }
500*9356374aSAndroid Build Coastguard Worker 
501*9356374aSAndroid Build Coastguard Worker static bool ForwardDFS(GraphCycles::Rep* r, int32_t n, int32_t upper_bound);
502*9356374aSAndroid Build Coastguard Worker static void BackwardDFS(GraphCycles::Rep* r, int32_t n, int32_t lower_bound);
503*9356374aSAndroid Build Coastguard Worker static void Reorder(GraphCycles::Rep* r);
504*9356374aSAndroid Build Coastguard Worker static void Sort(const Vec<Node*>&, Vec<int32_t>* delta);
505*9356374aSAndroid Build Coastguard Worker static void MoveToList(
506*9356374aSAndroid Build Coastguard Worker     GraphCycles::Rep* r, Vec<int32_t>* src, Vec<int32_t>* dst);
507*9356374aSAndroid Build Coastguard Worker 
InsertEdge(GraphId idx,GraphId idy)508*9356374aSAndroid Build Coastguard Worker bool GraphCycles::InsertEdge(GraphId idx, GraphId idy) {
509*9356374aSAndroid Build Coastguard Worker   Rep* r = rep_;
510*9356374aSAndroid Build Coastguard Worker   const int32_t x = NodeIndex(idx);
511*9356374aSAndroid Build Coastguard Worker   const int32_t y = NodeIndex(idy);
512*9356374aSAndroid Build Coastguard Worker   Node* nx = FindNode(r, idx);
513*9356374aSAndroid Build Coastguard Worker   Node* ny = FindNode(r, idy);
514*9356374aSAndroid Build Coastguard Worker   if (nx == nullptr || ny == nullptr) return true;  // Expired ids
515*9356374aSAndroid Build Coastguard Worker 
516*9356374aSAndroid Build Coastguard Worker   if (nx == ny) return false;  // Self edge
517*9356374aSAndroid Build Coastguard Worker   if (!nx->out.insert(y)) {
518*9356374aSAndroid Build Coastguard Worker     // Edge already exists.
519*9356374aSAndroid Build Coastguard Worker     return true;
520*9356374aSAndroid Build Coastguard Worker   }
521*9356374aSAndroid Build Coastguard Worker 
522*9356374aSAndroid Build Coastguard Worker   ny->in.insert(x);
523*9356374aSAndroid Build Coastguard Worker 
524*9356374aSAndroid Build Coastguard Worker   if (nx->rank <= ny->rank) {
525*9356374aSAndroid Build Coastguard Worker     // New edge is consistent with existing rank assignment.
526*9356374aSAndroid Build Coastguard Worker     return true;
527*9356374aSAndroid Build Coastguard Worker   }
528*9356374aSAndroid Build Coastguard Worker 
529*9356374aSAndroid Build Coastguard Worker   // Current rank assignments are incompatible with the new edge.  Recompute.
530*9356374aSAndroid Build Coastguard Worker   // We only need to consider nodes that fall in the range [ny->rank,nx->rank].
531*9356374aSAndroid Build Coastguard Worker   if (!ForwardDFS(r, y, nx->rank)) {
532*9356374aSAndroid Build Coastguard Worker     // Found a cycle.  Undo the insertion and tell caller.
533*9356374aSAndroid Build Coastguard Worker     nx->out.erase(y);
534*9356374aSAndroid Build Coastguard Worker     ny->in.erase(x);
535*9356374aSAndroid Build Coastguard Worker     // Since we do not call Reorder() on this path, clear any visited
536*9356374aSAndroid Build Coastguard Worker     // markers left by ForwardDFS.
537*9356374aSAndroid Build Coastguard Worker     for (const auto& d : r->deltaf_) {
538*9356374aSAndroid Build Coastguard Worker       r->nodes_[static_cast<uint32_t>(d)]->visited = false;
539*9356374aSAndroid Build Coastguard Worker     }
540*9356374aSAndroid Build Coastguard Worker     return false;
541*9356374aSAndroid Build Coastguard Worker   }
542*9356374aSAndroid Build Coastguard Worker   BackwardDFS(r, x, ny->rank);
543*9356374aSAndroid Build Coastguard Worker   Reorder(r);
544*9356374aSAndroid Build Coastguard Worker   return true;
545*9356374aSAndroid Build Coastguard Worker }
546*9356374aSAndroid Build Coastguard Worker 
ForwardDFS(GraphCycles::Rep * r,int32_t n,int32_t upper_bound)547*9356374aSAndroid Build Coastguard Worker static bool ForwardDFS(GraphCycles::Rep* r, int32_t n, int32_t upper_bound) {
548*9356374aSAndroid Build Coastguard Worker   // Avoid recursion since stack space might be limited.
549*9356374aSAndroid Build Coastguard Worker   // We instead keep a stack of nodes to visit.
550*9356374aSAndroid Build Coastguard Worker   r->deltaf_.clear();
551*9356374aSAndroid Build Coastguard Worker   r->stack_.clear();
552*9356374aSAndroid Build Coastguard Worker   r->stack_.push_back(n);
553*9356374aSAndroid Build Coastguard Worker   while (!r->stack_.empty()) {
554*9356374aSAndroid Build Coastguard Worker     n = r->stack_.back();
555*9356374aSAndroid Build Coastguard Worker     r->stack_.pop_back();
556*9356374aSAndroid Build Coastguard Worker     Node* nn = r->nodes_[static_cast<uint32_t>(n)];
557*9356374aSAndroid Build Coastguard Worker     if (nn->visited) continue;
558*9356374aSAndroid Build Coastguard Worker 
559*9356374aSAndroid Build Coastguard Worker     nn->visited = true;
560*9356374aSAndroid Build Coastguard Worker     r->deltaf_.push_back(n);
561*9356374aSAndroid Build Coastguard Worker 
562*9356374aSAndroid Build Coastguard Worker     HASH_FOR_EACH(w, nn->out) {
563*9356374aSAndroid Build Coastguard Worker       Node* nw = r->nodes_[static_cast<uint32_t>(w)];
564*9356374aSAndroid Build Coastguard Worker       if (nw->rank == upper_bound) {
565*9356374aSAndroid Build Coastguard Worker         return false;  // Cycle
566*9356374aSAndroid Build Coastguard Worker       }
567*9356374aSAndroid Build Coastguard Worker       if (!nw->visited && nw->rank < upper_bound) {
568*9356374aSAndroid Build Coastguard Worker         r->stack_.push_back(w);
569*9356374aSAndroid Build Coastguard Worker       }
570*9356374aSAndroid Build Coastguard Worker     }
571*9356374aSAndroid Build Coastguard Worker   }
572*9356374aSAndroid Build Coastguard Worker   return true;
573*9356374aSAndroid Build Coastguard Worker }
574*9356374aSAndroid Build Coastguard Worker 
BackwardDFS(GraphCycles::Rep * r,int32_t n,int32_t lower_bound)575*9356374aSAndroid Build Coastguard Worker static void BackwardDFS(GraphCycles::Rep* r, int32_t n, int32_t lower_bound) {
576*9356374aSAndroid Build Coastguard Worker   r->deltab_.clear();
577*9356374aSAndroid Build Coastguard Worker   r->stack_.clear();
578*9356374aSAndroid Build Coastguard Worker   r->stack_.push_back(n);
579*9356374aSAndroid Build Coastguard Worker   while (!r->stack_.empty()) {
580*9356374aSAndroid Build Coastguard Worker     n = r->stack_.back();
581*9356374aSAndroid Build Coastguard Worker     r->stack_.pop_back();
582*9356374aSAndroid Build Coastguard Worker     Node* nn = r->nodes_[static_cast<uint32_t>(n)];
583*9356374aSAndroid Build Coastguard Worker     if (nn->visited) continue;
584*9356374aSAndroid Build Coastguard Worker 
585*9356374aSAndroid Build Coastguard Worker     nn->visited = true;
586*9356374aSAndroid Build Coastguard Worker     r->deltab_.push_back(n);
587*9356374aSAndroid Build Coastguard Worker 
588*9356374aSAndroid Build Coastguard Worker     HASH_FOR_EACH(w, nn->in) {
589*9356374aSAndroid Build Coastguard Worker       Node* nw = r->nodes_[static_cast<uint32_t>(w)];
590*9356374aSAndroid Build Coastguard Worker       if (!nw->visited && lower_bound < nw->rank) {
591*9356374aSAndroid Build Coastguard Worker         r->stack_.push_back(w);
592*9356374aSAndroid Build Coastguard Worker       }
593*9356374aSAndroid Build Coastguard Worker     }
594*9356374aSAndroid Build Coastguard Worker   }
595*9356374aSAndroid Build Coastguard Worker }
596*9356374aSAndroid Build Coastguard Worker 
Reorder(GraphCycles::Rep * r)597*9356374aSAndroid Build Coastguard Worker static void Reorder(GraphCycles::Rep* r) {
598*9356374aSAndroid Build Coastguard Worker   Sort(r->nodes_, &r->deltab_);
599*9356374aSAndroid Build Coastguard Worker   Sort(r->nodes_, &r->deltaf_);
600*9356374aSAndroid Build Coastguard Worker 
601*9356374aSAndroid Build Coastguard Worker   // Adds contents of delta lists to list_ (backwards deltas first).
602*9356374aSAndroid Build Coastguard Worker   r->list_.clear();
603*9356374aSAndroid Build Coastguard Worker   MoveToList(r, &r->deltab_, &r->list_);
604*9356374aSAndroid Build Coastguard Worker   MoveToList(r, &r->deltaf_, &r->list_);
605*9356374aSAndroid Build Coastguard Worker 
606*9356374aSAndroid Build Coastguard Worker   // Produce sorted list of all ranks that will be reassigned.
607*9356374aSAndroid Build Coastguard Worker   r->merged_.resize(r->deltab_.size() + r->deltaf_.size());
608*9356374aSAndroid Build Coastguard Worker   std::merge(r->deltab_.begin(), r->deltab_.end(),
609*9356374aSAndroid Build Coastguard Worker              r->deltaf_.begin(), r->deltaf_.end(),
610*9356374aSAndroid Build Coastguard Worker              r->merged_.begin());
611*9356374aSAndroid Build Coastguard Worker 
612*9356374aSAndroid Build Coastguard Worker   // Assign the ranks in order to the collected list.
613*9356374aSAndroid Build Coastguard Worker   for (uint32_t i = 0; i < r->list_.size(); i++) {
614*9356374aSAndroid Build Coastguard Worker     r->nodes_[static_cast<uint32_t>(r->list_[i])]->rank = r->merged_[i];
615*9356374aSAndroid Build Coastguard Worker   }
616*9356374aSAndroid Build Coastguard Worker }
617*9356374aSAndroid Build Coastguard Worker 
Sort(const Vec<Node * > & nodes,Vec<int32_t> * delta)618*9356374aSAndroid Build Coastguard Worker static void Sort(const Vec<Node*>& nodes, Vec<int32_t>* delta) {
619*9356374aSAndroid Build Coastguard Worker   struct ByRank {
620*9356374aSAndroid Build Coastguard Worker     const Vec<Node*>* nodes;
621*9356374aSAndroid Build Coastguard Worker     bool operator()(int32_t a, int32_t b) const {
622*9356374aSAndroid Build Coastguard Worker       return (*nodes)[static_cast<uint32_t>(a)]->rank <
623*9356374aSAndroid Build Coastguard Worker              (*nodes)[static_cast<uint32_t>(b)]->rank;
624*9356374aSAndroid Build Coastguard Worker     }
625*9356374aSAndroid Build Coastguard Worker   };
626*9356374aSAndroid Build Coastguard Worker   ByRank cmp;
627*9356374aSAndroid Build Coastguard Worker   cmp.nodes = &nodes;
628*9356374aSAndroid Build Coastguard Worker   std::sort(delta->begin(), delta->end(), cmp);
629*9356374aSAndroid Build Coastguard Worker }
630*9356374aSAndroid Build Coastguard Worker 
MoveToList(GraphCycles::Rep * r,Vec<int32_t> * src,Vec<int32_t> * dst)631*9356374aSAndroid Build Coastguard Worker static void MoveToList(
632*9356374aSAndroid Build Coastguard Worker     GraphCycles::Rep* r, Vec<int32_t>* src, Vec<int32_t>* dst) {
633*9356374aSAndroid Build Coastguard Worker   for (auto& v : *src) {
634*9356374aSAndroid Build Coastguard Worker     int32_t w = v;
635*9356374aSAndroid Build Coastguard Worker     // Replace v entry with its rank
636*9356374aSAndroid Build Coastguard Worker     v = r->nodes_[static_cast<uint32_t>(w)]->rank;
637*9356374aSAndroid Build Coastguard Worker     // Prepare for future DFS calls
638*9356374aSAndroid Build Coastguard Worker     r->nodes_[static_cast<uint32_t>(w)]->visited = false;
639*9356374aSAndroid Build Coastguard Worker     dst->push_back(w);
640*9356374aSAndroid Build Coastguard Worker   }
641*9356374aSAndroid Build Coastguard Worker }
642*9356374aSAndroid Build Coastguard Worker 
FindPath(GraphId idx,GraphId idy,int max_path_len,GraphId path[]) const643*9356374aSAndroid Build Coastguard Worker int GraphCycles::FindPath(GraphId idx, GraphId idy, int max_path_len,
644*9356374aSAndroid Build Coastguard Worker                           GraphId path[]) const {
645*9356374aSAndroid Build Coastguard Worker   Rep* r = rep_;
646*9356374aSAndroid Build Coastguard Worker   if (FindNode(r, idx) == nullptr || FindNode(r, idy) == nullptr) return 0;
647*9356374aSAndroid Build Coastguard Worker   const int32_t x = NodeIndex(idx);
648*9356374aSAndroid Build Coastguard Worker   const int32_t y = NodeIndex(idy);
649*9356374aSAndroid Build Coastguard Worker 
650*9356374aSAndroid Build Coastguard Worker   // Forward depth first search starting at x until we hit y.
651*9356374aSAndroid Build Coastguard Worker   // As we descend into a node, we push it onto the path.
652*9356374aSAndroid Build Coastguard Worker   // As we leave a node, we remove it from the path.
653*9356374aSAndroid Build Coastguard Worker   int path_len = 0;
654*9356374aSAndroid Build Coastguard Worker 
655*9356374aSAndroid Build Coastguard Worker   NodeSet seen;
656*9356374aSAndroid Build Coastguard Worker   r->stack_.clear();
657*9356374aSAndroid Build Coastguard Worker   r->stack_.push_back(x);
658*9356374aSAndroid Build Coastguard Worker   while (!r->stack_.empty()) {
659*9356374aSAndroid Build Coastguard Worker     int32_t n = r->stack_.back();
660*9356374aSAndroid Build Coastguard Worker     r->stack_.pop_back();
661*9356374aSAndroid Build Coastguard Worker     if (n < 0) {
662*9356374aSAndroid Build Coastguard Worker       // Marker to indicate that we are leaving a node
663*9356374aSAndroid Build Coastguard Worker       path_len--;
664*9356374aSAndroid Build Coastguard Worker       continue;
665*9356374aSAndroid Build Coastguard Worker     }
666*9356374aSAndroid Build Coastguard Worker 
667*9356374aSAndroid Build Coastguard Worker     if (path_len < max_path_len) {
668*9356374aSAndroid Build Coastguard Worker       path[path_len] =
669*9356374aSAndroid Build Coastguard Worker           MakeId(n, rep_->nodes_[static_cast<uint32_t>(n)]->version);
670*9356374aSAndroid Build Coastguard Worker     }
671*9356374aSAndroid Build Coastguard Worker     path_len++;
672*9356374aSAndroid Build Coastguard Worker     r->stack_.push_back(-1);  // Will remove tentative path entry
673*9356374aSAndroid Build Coastguard Worker 
674*9356374aSAndroid Build Coastguard Worker     if (n == y) {
675*9356374aSAndroid Build Coastguard Worker       return path_len;
676*9356374aSAndroid Build Coastguard Worker     }
677*9356374aSAndroid Build Coastguard Worker 
678*9356374aSAndroid Build Coastguard Worker     HASH_FOR_EACH(w, r->nodes_[static_cast<uint32_t>(n)]->out) {
679*9356374aSAndroid Build Coastguard Worker       if (seen.insert(w)) {
680*9356374aSAndroid Build Coastguard Worker         r->stack_.push_back(w);
681*9356374aSAndroid Build Coastguard Worker       }
682*9356374aSAndroid Build Coastguard Worker     }
683*9356374aSAndroid Build Coastguard Worker   }
684*9356374aSAndroid Build Coastguard Worker 
685*9356374aSAndroid Build Coastguard Worker   return 0;
686*9356374aSAndroid Build Coastguard Worker }
687*9356374aSAndroid Build Coastguard Worker 
IsReachable(GraphId x,GraphId y) const688*9356374aSAndroid Build Coastguard Worker bool GraphCycles::IsReachable(GraphId x, GraphId y) const {
689*9356374aSAndroid Build Coastguard Worker   return FindPath(x, y, 0, nullptr) > 0;
690*9356374aSAndroid Build Coastguard Worker }
691*9356374aSAndroid Build Coastguard Worker 
UpdateStackTrace(GraphId id,int priority,int (* get_stack_trace)(void ** stack,int))692*9356374aSAndroid Build Coastguard Worker void GraphCycles::UpdateStackTrace(GraphId id, int priority,
693*9356374aSAndroid Build Coastguard Worker                                    int (*get_stack_trace)(void** stack, int)) {
694*9356374aSAndroid Build Coastguard Worker   Node* n = FindNode(rep_, id);
695*9356374aSAndroid Build Coastguard Worker   if (n == nullptr || n->priority >= priority) {
696*9356374aSAndroid Build Coastguard Worker     return;
697*9356374aSAndroid Build Coastguard Worker   }
698*9356374aSAndroid Build Coastguard Worker   n->nstack = (*get_stack_trace)(n->stack, ABSL_ARRAYSIZE(n->stack));
699*9356374aSAndroid Build Coastguard Worker   n->priority = priority;
700*9356374aSAndroid Build Coastguard Worker }
701*9356374aSAndroid Build Coastguard Worker 
GetStackTrace(GraphId id,void *** ptr)702*9356374aSAndroid Build Coastguard Worker int GraphCycles::GetStackTrace(GraphId id, void*** ptr) {
703*9356374aSAndroid Build Coastguard Worker   Node* n = FindNode(rep_, id);
704*9356374aSAndroid Build Coastguard Worker   if (n == nullptr) {
705*9356374aSAndroid Build Coastguard Worker     *ptr = nullptr;
706*9356374aSAndroid Build Coastguard Worker     return 0;
707*9356374aSAndroid Build Coastguard Worker   } else {
708*9356374aSAndroid Build Coastguard Worker     *ptr = n->stack;
709*9356374aSAndroid Build Coastguard Worker     return n->nstack;
710*9356374aSAndroid Build Coastguard Worker   }
711*9356374aSAndroid Build Coastguard Worker }
712*9356374aSAndroid Build Coastguard Worker 
713*9356374aSAndroid Build Coastguard Worker }  // namespace synchronization_internal
714*9356374aSAndroid Build Coastguard Worker ABSL_NAMESPACE_END
715*9356374aSAndroid Build Coastguard Worker }  // namespace absl
716*9356374aSAndroid Build Coastguard Worker 
717*9356374aSAndroid Build Coastguard Worker #endif  // ABSL_LOW_LEVEL_ALLOC_MISSING
718