| // Copyright 2017 The Abseil Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| #ifndef ABSL_SYNCHRONIZATION_INTERNAL_GRAPHCYCLES_H_ |
| #define ABSL_SYNCHRONIZATION_INTERNAL_GRAPHCYCLES_H_ |
| |
| // GraphCycles detects the introduction of a cycle into a directed |
| // graph that is being built up incrementally. |
| // |
| // Nodes are identified by small integers. It is not possible to |
| // record multiple edges with the same (source, destination) pair; |
| // requests to add an edge where one already exists are silently |
| // ignored. |
| // |
| // It is also not possible to introduce a cycle; an attempt to insert |
| // an edge that would introduce a cycle fails and returns false. |
| // |
| // GraphCycles uses no internal locking; calls into it should be |
| // serialized externally. |
| |
| // Performance considerations: |
| // Works well on sparse graphs, poorly on dense graphs. |
| // Extra information is maintained incrementally to detect cycles quickly. |
| // InsertEdge() is very fast when the edge already exists, and reasonably fast |
| // otherwise. |
| // FindPath() is linear in the size of the graph. |
| // The current implementation uses O(|V|+|E|) space. |
| |
| #include <cstdint> |
| |
| #include "absl/base/config.h" |
| |
| namespace absl { |
| ABSL_NAMESPACE_BEGIN |
| namespace synchronization_internal { |
| |
| // Opaque identifier for a graph node. |
| struct GraphId { |
| uint64_t handle; |
| |
| bool operator==(const GraphId& x) const { return handle == x.handle; } |
| bool operator!=(const GraphId& x) const { return handle != x.handle; } |
| }; |
| |
| // Return an invalid graph id that will never be assigned by GraphCycles. |
| inline GraphId InvalidGraphId() { |
| return GraphId{0}; |
| } |
| |
| class GraphCycles { |
| public: |
| GraphCycles(); |
| ~GraphCycles(); |
| |
| // Return the id to use for ptr, assigning one if necessary. |
| // Subsequent calls with the same ptr value will return the same id |
| // until Remove(). |
| GraphId GetId(void* ptr); |
| |
| // Remove "ptr" from the graph. Its corresponding node and all |
| // edges to and from it are removed. |
| void RemoveNode(void* ptr); |
| |
| // Return the pointer associated with id, or nullptr if id is not |
| // currently in the graph. |
| void* Ptr(GraphId id); |
| |
| // Attempt to insert an edge from source_node to dest_node. If the |
| // edge would introduce a cycle, return false without making any |
| // changes. Otherwise add the edge and return true. |
| bool InsertEdge(GraphId source_node, GraphId dest_node); |
| |
| // Remove any edge that exists from source_node to dest_node. |
| void RemoveEdge(GraphId source_node, GraphId dest_node); |
| |
| // Return whether node exists in the graph. |
| bool HasNode(GraphId node); |
| |
| // Return whether there is an edge directly from source_node to dest_node. |
| bool HasEdge(GraphId source_node, GraphId dest_node) const; |
| |
| // Return whether dest_node is reachable from source_node |
| // by following edges. |
| bool IsReachable(GraphId source_node, GraphId dest_node) const; |
| |
| // Find a path from "source" to "dest". If such a path exists, |
| // place the nodes on the path in the array path[], and return |
| // the number of nodes on the path. If the path is longer than |
| // max_path_len nodes, only the first max_path_len nodes are placed |
| // in path[]. The client should compare the return value with |
| // max_path_len" to see when this occurs. If no path exists, return |
| // 0. Any valid path stored in path[] will start with "source" and |
| // end with "dest". There is no guarantee that the path is the |
| // shortest, but no node will appear twice in the path, except the |
| // source and destination node if they are identical; therefore, the |
| // return value is at most one greater than the number of nodes in |
| // the graph. |
| int FindPath(GraphId source, GraphId dest, int max_path_len, |
| GraphId path[]) const; |
| |
| // Update the stack trace recorded for id with the current stack |
| // trace if the last time it was updated had a smaller priority |
| // than the priority passed on this call. |
| // |
| // *get_stack_trace is called to get the stack trace. |
| void UpdateStackTrace(GraphId id, int priority, |
| int (*get_stack_trace)(void**, int)); |
| |
| // Set *ptr to the beginning of the array that holds the recorded |
| // stack trace for id and return the depth of the stack trace. |
| int GetStackTrace(GraphId id, void*** ptr); |
| |
| // Check internal invariants. Crashes on failure, returns true on success. |
| // Expensive: should only be called from graphcycles_test.cc. |
| bool CheckInvariants() const; |
| |
| // Test-only method to add more nodes. The nodes will not be valid, and this |
| // method should only be used to test the behavior of the graph when it is |
| // very full. |
| void TestOnlyAddNodes(uint32_t n); |
| |
| // ---------------------------------------------------- |
| struct Rep; |
| private: |
| Rep *rep_; // opaque representation |
| GraphCycles(const GraphCycles&) = delete; |
| GraphCycles& operator=(const GraphCycles&) = delete; |
| }; |
| |
| } // namespace synchronization_internal |
| ABSL_NAMESPACE_END |
| } // namespace absl |
| |
| #endif |