graphcycles.h 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. // Copyright 2017 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. #ifndef ABSL_SYNCHRONIZATION_INTERNAL_GRAPHCYCLES_H_
  16. #define ABSL_SYNCHRONIZATION_INTERNAL_GRAPHCYCLES_H_
  17. // GraphCycles detects the introduction of a cycle into a directed
  18. // graph that is being built up incrementally.
  19. //
  20. // Nodes are identified by small integers. It is not possible to
  21. // record multiple edges with the same (source, destination) pair;
  22. // requests to add an edge where one already exists are silently
  23. // ignored.
  24. //
  25. // It is also not possible to introduce a cycle; an attempt to insert
  26. // an edge that would introduce a cycle fails and returns false.
  27. //
  28. // GraphCycles uses no internal locking; calls into it should be
  29. // serialized externally.
  30. // Performance considerations:
  31. // Works well on sparse graphs, poorly on dense graphs.
  32. // Extra information is maintained incrementally to detect cycles quickly.
  33. // InsertEdge() is very fast when the edge already exists, and reasonably fast
  34. // otherwise.
  35. // FindPath() is linear in the size of the graph.
  36. // The current implementation uses O(|V|+|E|) space.
  37. #include <cstdint>
  38. #include "absl/base/config.h"
  39. namespace absl {
  40. ABSL_NAMESPACE_BEGIN
  41. namespace synchronization_internal {
  42. // Opaque identifier for a graph node.
  43. struct GraphId {
  44. uint64_t handle;
  45. bool operator==(const GraphId& x) const { return handle == x.handle; }
  46. bool operator!=(const GraphId& x) const { return handle != x.handle; }
  47. };
  48. // Return an invalid graph id that will never be assigned by GraphCycles.
  49. inline GraphId InvalidGraphId() {
  50. return GraphId{0};
  51. }
  52. class GraphCycles {
  53. public:
  54. GraphCycles();
  55. ~GraphCycles();
  56. // Return the id to use for ptr, assigning one if necessary.
  57. // Subsequent calls with the same ptr value will return the same id
  58. // until Remove().
  59. GraphId GetId(void* ptr);
  60. // Remove "ptr" from the graph. Its corresponding node and all
  61. // edges to and from it are removed.
  62. void RemoveNode(void* ptr);
  63. // Return the pointer associated with id, or nullptr if id is not
  64. // currently in the graph.
  65. void* Ptr(GraphId id);
  66. // Attempt to insert an edge from source_node to dest_node. If the
  67. // edge would introduce a cycle, return false without making any
  68. // changes. Otherwise add the edge and return true.
  69. bool InsertEdge(GraphId source_node, GraphId dest_node);
  70. // Remove any edge that exists from source_node to dest_node.
  71. void RemoveEdge(GraphId source_node, GraphId dest_node);
  72. // Return whether node exists in the graph.
  73. bool HasNode(GraphId node);
  74. // Return whether there is an edge directly from source_node to dest_node.
  75. bool HasEdge(GraphId source_node, GraphId dest_node) const;
  76. // Return whether dest_node is reachable from source_node
  77. // by following edges.
  78. bool IsReachable(GraphId source_node, GraphId dest_node) const;
  79. // Find a path from "source" to "dest". If such a path exists,
  80. // place the nodes on the path in the array path[], and return
  81. // the number of nodes on the path. If the path is longer than
  82. // max_path_len nodes, only the first max_path_len nodes are placed
  83. // in path[]. The client should compare the return value with
  84. // max_path_len" to see when this occurs. If no path exists, return
  85. // 0. Any valid path stored in path[] will start with "source" and
  86. // end with "dest". There is no guarantee that the path is the
  87. // shortest, but no node will appear twice in the path, except the
  88. // source and destination node if they are identical; therefore, the
  89. // return value is at most one greater than the number of nodes in
  90. // the graph.
  91. int FindPath(GraphId source, GraphId dest, int max_path_len,
  92. GraphId path[]) const;
  93. // Update the stack trace recorded for id with the current stack
  94. // trace if the last time it was updated had a smaller priority
  95. // than the priority passed on this call.
  96. //
  97. // *get_stack_trace is called to get the stack trace.
  98. void UpdateStackTrace(GraphId id, int priority,
  99. int (*get_stack_trace)(void**, int));
  100. // Set *ptr to the beginning of the array that holds the recorded
  101. // stack trace for id and return the depth of the stack trace.
  102. int GetStackTrace(GraphId id, void*** ptr);
  103. // Check internal invariants. Crashes on failure, returns true on success.
  104. // Expensive: should only be called from graphcycles_test.cc.
  105. bool CheckInvariants() const;
  106. // ----------------------------------------------------
  107. struct Rep;
  108. private:
  109. Rep *rep_; // opaque representation
  110. GraphCycles(const GraphCycles&) = delete;
  111. GraphCycles& operator=(const GraphCycles&) = delete;
  112. };
  113. } // namespace synchronization_internal
  114. ABSL_NAMESPACE_END
  115. } // namespace absl
  116. #endif