xref: /aosp_15_r20/external/abseil-cpp/absl/synchronization/internal/graphcycles.h (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 
16*9356374aSAndroid Build Coastguard Worker #ifndef ABSL_SYNCHRONIZATION_INTERNAL_GRAPHCYCLES_H_
17*9356374aSAndroid Build Coastguard Worker #define ABSL_SYNCHRONIZATION_INTERNAL_GRAPHCYCLES_H_
18*9356374aSAndroid Build Coastguard Worker 
19*9356374aSAndroid Build Coastguard Worker // GraphCycles detects the introduction of a cycle into a directed
20*9356374aSAndroid Build Coastguard Worker // graph that is being built up incrementally.
21*9356374aSAndroid Build Coastguard Worker //
22*9356374aSAndroid Build Coastguard Worker // Nodes are identified by small integers.  It is not possible to
23*9356374aSAndroid Build Coastguard Worker // record multiple edges with the same (source, destination) pair;
24*9356374aSAndroid Build Coastguard Worker // requests to add an edge where one already exists are silently
25*9356374aSAndroid Build Coastguard Worker // ignored.
26*9356374aSAndroid Build Coastguard Worker //
27*9356374aSAndroid Build Coastguard Worker // It is also not possible to introduce a cycle; an attempt to insert
28*9356374aSAndroid Build Coastguard Worker // an edge that would introduce a cycle fails and returns false.
29*9356374aSAndroid Build Coastguard Worker //
30*9356374aSAndroid Build Coastguard Worker // GraphCycles uses no internal locking; calls into it should be
31*9356374aSAndroid Build Coastguard Worker // serialized externally.
32*9356374aSAndroid Build Coastguard Worker 
33*9356374aSAndroid Build Coastguard Worker // Performance considerations:
34*9356374aSAndroid Build Coastguard Worker //   Works well on sparse graphs, poorly on dense graphs.
35*9356374aSAndroid Build Coastguard Worker //   Extra information is maintained incrementally to detect cycles quickly.
36*9356374aSAndroid Build Coastguard Worker //   InsertEdge() is very fast when the edge already exists, and reasonably fast
37*9356374aSAndroid Build Coastguard Worker //   otherwise.
38*9356374aSAndroid Build Coastguard Worker //   FindPath() is linear in the size of the graph.
39*9356374aSAndroid Build Coastguard Worker // The current implementation uses O(|V|+|E|) space.
40*9356374aSAndroid Build Coastguard Worker 
41*9356374aSAndroid Build Coastguard Worker #include <cstdint>
42*9356374aSAndroid Build Coastguard Worker 
43*9356374aSAndroid Build Coastguard Worker #include "absl/base/config.h"
44*9356374aSAndroid Build Coastguard Worker 
45*9356374aSAndroid Build Coastguard Worker namespace absl {
46*9356374aSAndroid Build Coastguard Worker ABSL_NAMESPACE_BEGIN
47*9356374aSAndroid Build Coastguard Worker namespace synchronization_internal {
48*9356374aSAndroid Build Coastguard Worker 
49*9356374aSAndroid Build Coastguard Worker // Opaque identifier for a graph node.
50*9356374aSAndroid Build Coastguard Worker struct GraphId {
51*9356374aSAndroid Build Coastguard Worker   uint64_t handle;
52*9356374aSAndroid Build Coastguard Worker 
53*9356374aSAndroid Build Coastguard Worker   bool operator==(const GraphId& x) const { return handle == x.handle; }
54*9356374aSAndroid Build Coastguard Worker   bool operator!=(const GraphId& x) const { return handle != x.handle; }
55*9356374aSAndroid Build Coastguard Worker };
56*9356374aSAndroid Build Coastguard Worker 
57*9356374aSAndroid Build Coastguard Worker // Return an invalid graph id that will never be assigned by GraphCycles.
InvalidGraphId()58*9356374aSAndroid Build Coastguard Worker inline GraphId InvalidGraphId() {
59*9356374aSAndroid Build Coastguard Worker   return GraphId{0};
60*9356374aSAndroid Build Coastguard Worker }
61*9356374aSAndroid Build Coastguard Worker 
62*9356374aSAndroid Build Coastguard Worker class GraphCycles {
63*9356374aSAndroid Build Coastguard Worker  public:
64*9356374aSAndroid Build Coastguard Worker   GraphCycles();
65*9356374aSAndroid Build Coastguard Worker   ~GraphCycles();
66*9356374aSAndroid Build Coastguard Worker 
67*9356374aSAndroid Build Coastguard Worker   // Return the id to use for ptr, assigning one if necessary.
68*9356374aSAndroid Build Coastguard Worker   // Subsequent calls with the same ptr value will return the same id
69*9356374aSAndroid Build Coastguard Worker   // until Remove().
70*9356374aSAndroid Build Coastguard Worker   GraphId GetId(void* ptr);
71*9356374aSAndroid Build Coastguard Worker 
72*9356374aSAndroid Build Coastguard Worker   // Remove "ptr" from the graph.  Its corresponding node and all
73*9356374aSAndroid Build Coastguard Worker   // edges to and from it are removed.
74*9356374aSAndroid Build Coastguard Worker   void RemoveNode(void* ptr);
75*9356374aSAndroid Build Coastguard Worker 
76*9356374aSAndroid Build Coastguard Worker   // Return the pointer associated with id, or nullptr if id is not
77*9356374aSAndroid Build Coastguard Worker   // currently in the graph.
78*9356374aSAndroid Build Coastguard Worker   void* Ptr(GraphId id);
79*9356374aSAndroid Build Coastguard Worker 
80*9356374aSAndroid Build Coastguard Worker   // Attempt to insert an edge from source_node to dest_node.  If the
81*9356374aSAndroid Build Coastguard Worker   // edge would introduce a cycle, return false without making any
82*9356374aSAndroid Build Coastguard Worker   // changes. Otherwise add the edge and return true.
83*9356374aSAndroid Build Coastguard Worker   bool InsertEdge(GraphId source_node, GraphId dest_node);
84*9356374aSAndroid Build Coastguard Worker 
85*9356374aSAndroid Build Coastguard Worker   // Remove any edge that exists from source_node to dest_node.
86*9356374aSAndroid Build Coastguard Worker   void RemoveEdge(GraphId source_node, GraphId dest_node);
87*9356374aSAndroid Build Coastguard Worker 
88*9356374aSAndroid Build Coastguard Worker   // Return whether node exists in the graph.
89*9356374aSAndroid Build Coastguard Worker   bool HasNode(GraphId node);
90*9356374aSAndroid Build Coastguard Worker 
91*9356374aSAndroid Build Coastguard Worker   // Return whether there is an edge directly from source_node to dest_node.
92*9356374aSAndroid Build Coastguard Worker   bool HasEdge(GraphId source_node, GraphId dest_node) const;
93*9356374aSAndroid Build Coastguard Worker 
94*9356374aSAndroid Build Coastguard Worker   // Return whether dest_node is reachable from source_node
95*9356374aSAndroid Build Coastguard Worker   // by following edges.
96*9356374aSAndroid Build Coastguard Worker   bool IsReachable(GraphId source_node, GraphId dest_node) const;
97*9356374aSAndroid Build Coastguard Worker 
98*9356374aSAndroid Build Coastguard Worker   // Find a path from "source" to "dest".  If such a path exists,
99*9356374aSAndroid Build Coastguard Worker   // place the nodes on the path in the array path[], and return
100*9356374aSAndroid Build Coastguard Worker   // the number of nodes on the path.  If the path is longer than
101*9356374aSAndroid Build Coastguard Worker   // max_path_len nodes, only the first max_path_len nodes are placed
102*9356374aSAndroid Build Coastguard Worker   // in path[].  The client should compare the return value with
103*9356374aSAndroid Build Coastguard Worker   // max_path_len" to see when this occurs.  If no path exists, return
104*9356374aSAndroid Build Coastguard Worker   // 0.  Any valid path stored in path[] will start with "source" and
105*9356374aSAndroid Build Coastguard Worker   // end with "dest".  There is no guarantee that the path is the
106*9356374aSAndroid Build Coastguard Worker   // shortest, but no node will appear twice in the path, except the
107*9356374aSAndroid Build Coastguard Worker   // source and destination node if they are identical; therefore, the
108*9356374aSAndroid Build Coastguard Worker   // return value is at most one greater than the number of nodes in
109*9356374aSAndroid Build Coastguard Worker   // the graph.
110*9356374aSAndroid Build Coastguard Worker   int FindPath(GraphId source, GraphId dest, int max_path_len,
111*9356374aSAndroid Build Coastguard Worker                GraphId path[]) const;
112*9356374aSAndroid Build Coastguard Worker 
113*9356374aSAndroid Build Coastguard Worker   // Update the stack trace recorded for id with the current stack
114*9356374aSAndroid Build Coastguard Worker   // trace if the last time it was updated had a smaller priority
115*9356374aSAndroid Build Coastguard Worker   // than the priority passed on this call.
116*9356374aSAndroid Build Coastguard Worker   //
117*9356374aSAndroid Build Coastguard Worker   // *get_stack_trace is called to get the stack trace.
118*9356374aSAndroid Build Coastguard Worker   void UpdateStackTrace(GraphId id, int priority,
119*9356374aSAndroid Build Coastguard Worker                         int (*get_stack_trace)(void**, int));
120*9356374aSAndroid Build Coastguard Worker 
121*9356374aSAndroid Build Coastguard Worker   // Set *ptr to the beginning of the array that holds the recorded
122*9356374aSAndroid Build Coastguard Worker   // stack trace for id and return the depth of the stack trace.
123*9356374aSAndroid Build Coastguard Worker   int GetStackTrace(GraphId id, void*** ptr);
124*9356374aSAndroid Build Coastguard Worker 
125*9356374aSAndroid Build Coastguard Worker   // Check internal invariants. Crashes on failure, returns true on success.
126*9356374aSAndroid Build Coastguard Worker   // Expensive: should only be called from graphcycles_test.cc.
127*9356374aSAndroid Build Coastguard Worker   bool CheckInvariants() const;
128*9356374aSAndroid Build Coastguard Worker 
129*9356374aSAndroid Build Coastguard Worker   // Test-only method to add more nodes. The nodes will not be valid, and this
130*9356374aSAndroid Build Coastguard Worker   // method should only be used to test the behavior of the graph when it is
131*9356374aSAndroid Build Coastguard Worker   // very full.
132*9356374aSAndroid Build Coastguard Worker   void TestOnlyAddNodes(uint32_t n);
133*9356374aSAndroid Build Coastguard Worker 
134*9356374aSAndroid Build Coastguard Worker   // ----------------------------------------------------
135*9356374aSAndroid Build Coastguard Worker   struct Rep;
136*9356374aSAndroid Build Coastguard Worker  private:
137*9356374aSAndroid Build Coastguard Worker   Rep *rep_;      // opaque representation
138*9356374aSAndroid Build Coastguard Worker   GraphCycles(const GraphCycles&) = delete;
139*9356374aSAndroid Build Coastguard Worker   GraphCycles& operator=(const GraphCycles&) = delete;
140*9356374aSAndroid Build Coastguard Worker };
141*9356374aSAndroid Build Coastguard Worker 
142*9356374aSAndroid Build Coastguard Worker }  // namespace synchronization_internal
143*9356374aSAndroid Build Coastguard Worker ABSL_NAMESPACE_END
144*9356374aSAndroid Build Coastguard Worker }  // namespace absl
145*9356374aSAndroid Build Coastguard Worker 
146*9356374aSAndroid Build Coastguard Worker #endif
147