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 #include "absl/base/config.h" 16 #include "absl/strings/internal/cordz_handle.h" 17 #include "absl/strings/internal/cordz_info.h" 18 19 #ifndef ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_ 20 #define ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_ 21 22 namespace absl { 23 ABSL_NAMESPACE_BEGIN 24 namespace cord_internal { 25 26 // The existence of a CordzSampleToken guarantees that a reader can traverse the 27 // global_cordz_infos_head linked-list without needing to hold a mutex. When a 28 // CordzSampleToken exists, all CordzInfo objects that would be destroyed are 29 // instead appended to a deletion queue. When the CordzSampleToken is destroyed, 30 // it will also clean up any of these CordzInfo objects. 31 // 32 // E.g., ST are CordzSampleToken objects and CH are CordzHandle objects. 33 // ST1 <- CH1 <- CH2 <- ST2 <- CH3 <- global_delete_queue_tail 34 // 35 // This list tracks that CH1 and CH2 were created after ST1, so the thread 36 // holding ST1 might have a referece to CH1, CH2, ST2, and CH3. However, ST2 was 37 // created later, so the thread holding the ST2 token cannot have a reference to 38 // ST1, CH1, or CH2. If ST1 is cleaned up first, that thread will delete ST1, 39 // CH1, and CH2. If instead ST2 is cleaned up first, that thread will only 40 // delete ST2. 41 // 42 // If ST1 is cleaned up first, the new list will be: 43 // ST2 <- CH3 <- global_delete_queue_tail 44 // 45 // If ST2 is cleaned up first, the new list will be: 46 // ST1 <- CH1 <- CH2 <- CH3 <- global_delete_queue_tail 47 // 48 // All new CordzHandle objects are appended to the list, so if a new thread 49 // comes along before either ST1 or ST2 are cleaned up, the new list will be: 50 // ST1 <- CH1 <- CH2 <- ST2 <- CH3 <- ST3 <- global_delete_queue_tail 51 // 52 // A thread must hold the global_delete_queue_mu mutex whenever it's altering 53 // this list. 54 // 55 // It is safe for thread that holds a CordzSampleToken to read 56 // global_cordz_infos at any time since the objects it is able to retrieve will 57 // not be deleted while the CordzSampleToken exists. 58 class CordzSampleToken : public CordzSnapshot { 59 public: 60 class Iterator { 61 public: 62 using iterator_category = std::input_iterator_tag; 63 using value_type = const CordzInfo&; 64 using difference_type = ptrdiff_t; 65 using pointer = const CordzInfo*; 66 using reference = value_type; 67 68 Iterator() = default; 69 70 Iterator& operator++(); 71 Iterator operator++(int); 72 friend bool operator==(const Iterator& lhs, const Iterator& rhs); 73 friend bool operator!=(const Iterator& lhs, const Iterator& rhs); 74 reference operator*() const; 75 pointer operator->() const; 76 77 private: 78 friend class CordzSampleToken; 79 explicit Iterator(const CordzSampleToken* token); 80 81 const CordzSampleToken* token_ = nullptr; 82 pointer current_ = nullptr; 83 }; 84 85 CordzSampleToken() = default; 86 CordzSampleToken(const CordzSampleToken&) = delete; 87 CordzSampleToken& operator=(const CordzSampleToken&) = delete; 88 begin()89 Iterator begin() { return Iterator(this); } end()90 Iterator end() { return Iterator(); } 91 }; 92 93 } // namespace cord_internal 94 ABSL_NAMESPACE_END 95 } // namespace absl 96 97 #endif // ABSL_STRINGS_CORDZ_SAMPLE_TOKEN_H_ 98