1 | //===- llvm/ADT/EpochTracker.h - ADT epoch tracking --------------*- C++ -*-==// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | /// |
9 | /// \file |
10 | /// This file defines the DebugEpochBase and DebugEpochBase::HandleBase classes. |
11 | /// These can be used to write iterators that are fail-fast when LLVM is built |
12 | /// with asserts enabled. |
13 | /// |
14 | //===----------------------------------------------------------------------===// |
15 | |
16 | #ifndef LLVM_ADT_EPOCHTRACKER_H |
17 | #define LLVM_ADT_EPOCHTRACKER_H |
18 | |
19 | #include "llvm/Config/abi-breaking.h" |
20 | |
21 | #include <cstdint> |
22 | |
23 | namespace llvm { |
24 | |
25 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS |
26 | #define LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE |
27 | |
28 | /// A base class for data structure classes wishing to make iterators |
29 | /// ("handles") pointing into themselves fail-fast. When building without |
30 | /// asserts, this class is empty and does nothing. |
31 | /// |
32 | /// DebugEpochBase does not by itself track handles pointing into itself. The |
33 | /// expectation is that routines touching the handles will poll on |
34 | /// isHandleInSync at appropriate points to assert that the handle they're using |
35 | /// is still valid. |
36 | /// |
37 | class DebugEpochBase { |
38 | uint64_t Epoch = 0; |
39 | |
40 | public: |
41 | DebugEpochBase() = default; |
42 | |
43 | /// Calling incrementEpoch invalidates all handles pointing into the |
44 | /// calling instance. |
45 | void incrementEpoch() { ++Epoch; } |
46 | |
47 | /// The destructor calls incrementEpoch to make use-after-free bugs |
48 | /// more likely to crash deterministically. |
49 | ~DebugEpochBase() { incrementEpoch(); } |
50 | |
51 | /// A base class for iterator classes ("handles") that wish to poll for |
52 | /// iterator invalidating modifications in the underlying data structure. |
53 | /// When LLVM is built without asserts, this class is empty and does nothing. |
54 | /// |
55 | /// HandleBase does not track the parent data structure by itself. It expects |
56 | /// the routines modifying the data structure to call incrementEpoch when they |
57 | /// make an iterator-invalidating modification. |
58 | /// |
59 | class HandleBase { |
60 | const uint64_t *EpochAddress = nullptr; |
61 | uint64_t EpochAtCreation = UINT64_MAX; |
62 | |
63 | public: |
64 | HandleBase() = default; |
65 | |
66 | explicit HandleBase(const DebugEpochBase *Parent) |
67 | : EpochAddress(&Parent->Epoch), EpochAtCreation(Parent->Epoch) {} |
68 | |
69 | /// Returns true if the DebugEpochBase this Handle is linked to has |
70 | /// not called incrementEpoch on itself since the creation of this |
71 | /// HandleBase instance. |
72 | bool isHandleInSync() const { return *EpochAddress == EpochAtCreation; } |
73 | |
74 | /// Returns a pointer to the epoch word stored in the data structure |
75 | /// this handle points into. Can be used to check if two iterators point |
76 | /// into the same data structure. |
77 | const void *getEpochAddress() const { return EpochAddress; } |
78 | }; |
79 | }; |
80 | |
81 | #else |
82 | #ifdef _MSC_VER |
83 | #define LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE __declspec(empty_bases) |
84 | #else |
85 | #define LLVM_DEBUGEPOCHBASE_HANDLEBASE_EMPTYBASE |
86 | #endif // _MSC_VER |
87 | |
88 | class DebugEpochBase { |
89 | public: |
90 | void incrementEpoch() {} |
91 | |
92 | class HandleBase { |
93 | public: |
94 | HandleBase() = default; |
95 | explicit HandleBase(const DebugEpochBase *) {} |
96 | bool isHandleInSync() const { return true; } |
97 | const void *getEpochAddress() const { return nullptr; } |
98 | }; |
99 | }; |
100 | |
101 | #endif // LLVM_ENABLE_ABI_BREAKING_CHECKS |
102 | |
103 | } // namespace llvm |
104 | |
105 | #endif |
106 | |