1 // Copyright 2019 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_STRINGS_CORDZ_HANDLE_H_ 16 #define ABSL_STRINGS_CORDZ_HANDLE_H_ 17 18 #include <atomic> 19 #include <vector> 20 21 #include "absl/base/config.h" 22 #include "absl/base/internal/raw_logging.h" 23 #include "absl/base/internal/spinlock.h" 24 #include "absl/synchronization/mutex.h" 25 26 namespace absl { 27 ABSL_NAMESPACE_BEGIN 28 namespace cord_internal { 29 30 // This base class allows multiple types of object (CordzInfo and 31 // CordzSampleToken) to exist simultaneously on the delete queue (pointed to by 32 // global_dq_tail and traversed using dq_prev_ and dq_next_). The 33 // delete queue guarantees that once a profiler creates a CordzSampleToken and 34 // has gained visibility into a CordzInfo object, that CordzInfo object will not 35 // be deleted prematurely. This allows the profiler to inspect all CordzInfo 36 // objects that are alive without needing to hold a global lock. 37 class CordzHandle { 38 public: CordzHandle()39 CordzHandle() : CordzHandle(false) {} 40 is_snapshot()41 bool is_snapshot() const { return is_snapshot_; } 42 43 // Returns true if this instance is safe to be deleted because it is either a 44 // snapshot, which is always safe to delete, or not included in the global 45 // delete queue and thus not included in any snapshot. 46 // Callers are responsible for making sure this instance can not be newly 47 // discovered by other threads. For example, CordzInfo instances first de-list 48 // themselves from the global CordzInfo list before determining if they are 49 // safe to be deleted directly. 50 // If SafeToDelete returns false, callers MUST use the Delete() method to 51 // safely queue CordzHandle instances for deletion. 52 bool SafeToDelete() const; 53 54 // Deletes the provided instance, or puts it on the delete queue to be deleted 55 // once there are no more sample tokens (snapshot) instances potentially 56 // referencing the instance. `handle` should not be null. 57 static void Delete(CordzHandle* handle); 58 59 // Returns the current entries in the delete queue in LIFO order. 60 static std::vector<const CordzHandle*> DiagnosticsGetDeleteQueue(); 61 62 // Returns true if the provided handle is nullptr or guarded by this handle. 63 // Since the CordzSnapshot token is itself a CordzHandle, this method will 64 // allow tests to check if that token is keeping an arbitrary CordzHandle 65 // alive. 66 bool DiagnosticsHandleIsSafeToInspect(const CordzHandle* handle) const; 67 68 // Returns the current entries in the delete queue, in LIFO order, that are 69 // protected by this. CordzHandle objects are only placed on the delete queue 70 // after CordzHandle::Delete is called with them as an argument. Only 71 // CordzHandle objects that are not also CordzSnapshot objects will be 72 // included in the return vector. For each of the handles in the return 73 // vector, the earliest that their memory can be freed is when this 74 // CordzSnapshot object is deleted. 75 std::vector<const CordzHandle*> DiagnosticsGetSafeToInspectDeletedHandles(); 76 77 protected: 78 explicit CordzHandle(bool is_snapshot); 79 virtual ~CordzHandle(); 80 81 private: 82 // Global queue data. CordzHandle stores a pointer to the global queue 83 // instance to harden against ODR violations. 84 struct Queue { QueueQueue85 constexpr explicit Queue(absl::ConstInitType) 86 : mutex(absl::kConstInit, 87 absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) {} 88 89 absl::base_internal::SpinLock mutex; ABSL_GUARDED_BYQueue90 std::atomic<CordzHandle*> dq_tail ABSL_GUARDED_BY(mutex){nullptr}; 91 92 // Returns true if this delete queue is empty. This method does not acquire 93 // the lock, but does a 'load acquire' observation on the delete queue tail. 94 // It is used inside Delete() to check for the presence of a delete queue 95 // without holding the lock. The assumption is that the caller is in the 96 // state of 'being deleted', and can not be newly discovered by a concurrent 97 // 'being constructed' snapshot instance. Practically, this means that any 98 // such discovery (`find`, 'first' or 'next', etc) must have proper 'happens 99 // before / after' semantics and atomic fences. IsEmptyQueue100 bool IsEmpty() const ABSL_NO_THREAD_SAFETY_ANALYSIS { 101 return dq_tail.load(std::memory_order_acquire) == nullptr; 102 } 103 }; 104 ODRCheck()105 void ODRCheck() const { 106 #ifndef NDEBUG 107 ABSL_RAW_CHECK(queue_ == &global_queue_, "ODR violation in Cord"); 108 #endif 109 } 110 111 ABSL_CONST_INIT static Queue global_queue_; 112 Queue* const queue_ = &global_queue_; 113 const bool is_snapshot_; 114 115 // dq_prev_ and dq_next_ require the global queue mutex to be held. 116 // Unfortunately we can't use thread annotations such that the thread safety 117 // analysis understands that queue_ and global_queue_ are one and the same. 118 CordzHandle* dq_prev_ = nullptr; 119 CordzHandle* dq_next_ = nullptr; 120 }; 121 122 class CordzSnapshot : public CordzHandle { 123 public: CordzSnapshot()124 CordzSnapshot() : CordzHandle(true) {} 125 }; 126 127 } // namespace cord_internal 128 ABSL_NAMESPACE_END 129 } // namespace absl 130 131 #endif // ABSL_STRINGS_CORDZ_HANDLE_H_ 132