Coverage Report

Created: 2023-09-25 06:27

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