00001 // Copyright 2017 The Abseil Authors. 00002 // 00003 // Licensed under the Apache License, Version 2.0 (the "License"); 00004 // you may not use this file except in compliance with the License. 00005 // You may obtain a copy of the License at 00006 // 00007 // https://www.apache.org/licenses/LICENSE-2.0 00008 // 00009 // Unless required by applicable law or agreed to in writing, software 00010 // distributed under the License is distributed on an "AS IS" BASIS, 00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00012 // See the License for the specific language governing permissions and 00013 // limitations under the License. 00014 // 00015 00016 #ifndef ABSL_SYNCHRONIZATION_INTERNAL_GRAPHCYCLES_H_ 00017 #define ABSL_SYNCHRONIZATION_INTERNAL_GRAPHCYCLES_H_ 00018 00019 // GraphCycles detects the introduction of a cycle into a directed 00020 // graph that is being built up incrementally. 00021 // 00022 // Nodes are identified by small integers. It is not possible to 00023 // record multiple edges with the same (source, destination) pair; 00024 // requests to add an edge where one already exists are silently 00025 // ignored. 00026 // 00027 // It is also not possible to introduce a cycle; an attempt to insert 00028 // an edge that would introduce a cycle fails and returns false. 00029 // 00030 // GraphCycles uses no internal locking; calls into it should be 00031 // serialized externally. 00032 00033 // Performance considerations: 00034 // Works well on sparse graphs, poorly on dense graphs. 00035 // Extra information is maintained incrementally to detect cycles quickly. 00036 // InsertEdge() is very fast when the edge already exists, and reasonably fast 00037 // otherwise. 00038 // FindPath() is linear in the size of the graph. 00039 // The current implemenation uses O(|V|+|E|) space. 00040 00041 #include <cstdint> 00042 00043 namespace absl { 00044 namespace synchronization_internal { 00045 00046 // Opaque identifier for a graph node. 00047 struct GraphId { 00048 uint64_t handle; 00049 00050 bool operator==(const GraphId& x) const { return handle == x.handle; } 00051 bool operator!=(const GraphId& x) const { return handle != x.handle; } 00052 }; 00053 00054 // Return an invalid graph id that will never be assigned by GraphCycles. 00055 inline GraphId InvalidGraphId() { 00056 return GraphId{0}; 00057 } 00058 00059 class GraphCycles { 00060 public: 00061 GraphCycles(); 00062 ~GraphCycles(); 00063 00064 // Return the id to use for ptr, assigning one if necessary. 00065 // Subsequent calls with the same ptr value will return the same id 00066 // until Remove(). 00067 GraphId GetId(void* ptr); 00068 00069 // Remove "ptr" from the graph. Its corresponding node and all 00070 // edges to and from it are removed. 00071 void RemoveNode(void* ptr); 00072 00073 // Return the pointer associated with id, or nullptr if id is not 00074 // currently in the graph. 00075 void* Ptr(GraphId id); 00076 00077 // Attempt to insert an edge from source_node to dest_node. If the 00078 // edge would introduce a cycle, return false without making any 00079 // changes. Otherwise add the edge and return true. 00080 bool InsertEdge(GraphId source_node, GraphId dest_node); 00081 00082 // Remove any edge that exists from source_node to dest_node. 00083 void RemoveEdge(GraphId source_node, GraphId dest_node); 00084 00085 // Return whether node exists in the graph. 00086 bool HasNode(GraphId node); 00087 00088 // Return whether there is an edge directly from source_node to dest_node. 00089 bool HasEdge(GraphId source_node, GraphId dest_node) const; 00090 00091 // Return whether dest_node is reachable from source_node 00092 // by following edges. 00093 bool IsReachable(GraphId source_node, GraphId dest_node) const; 00094 00095 // Find a path from "source" to "dest". If such a path exists, 00096 // place the nodes on the path in the array path[], and return 00097 // the number of nodes on the path. If the path is longer than 00098 // max_path_len nodes, only the first max_path_len nodes are placed 00099 // in path[]. The client should compare the return value with 00100 // max_path_len" to see when this occurs. If no path exists, return 00101 // 0. Any valid path stored in path[] will start with "source" and 00102 // end with "dest". There is no guarantee that the path is the 00103 // shortest, but no node will appear twice in the path, except the 00104 // source and destination node if they are identical; therefore, the 00105 // return value is at most one greater than the number of nodes in 00106 // the graph. 00107 int FindPath(GraphId source, GraphId dest, int max_path_len, 00108 GraphId path[]) const; 00109 00110 // Update the stack trace recorded for id with the current stack 00111 // trace if the last time it was updated had a smaller priority 00112 // than the priority passed on this call. 00113 // 00114 // *get_stack_trace is called to get the stack trace. 00115 void UpdateStackTrace(GraphId id, int priority, 00116 int (*get_stack_trace)(void**, int)); 00117 00118 // Set *ptr to the beginning of the array that holds the recorded 00119 // stack trace for id and return the depth of the stack trace. 00120 int GetStackTrace(GraphId id, void*** ptr); 00121 00122 // Check internal invariants. Crashes on failure, returns true on success. 00123 // Expensive: should only be called from graphcycles_test.cc. 00124 bool CheckInvariants() const; 00125 00126 // ---------------------------------------------------- 00127 struct Rep; 00128 private: 00129 Rep *rep_; // opaque representation 00130 GraphCycles(const GraphCycles&) = delete; 00131 GraphCycles& operator=(const GraphCycles&) = delete; 00132 }; 00133 00134 } // namespace synchronization_internal 00135 } // namespace absl 00136 00137 #endif