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