• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "absl/strings/internal/cordz_handle.h"
15 
16 #include <atomic>
17 
18 #include "absl/base/internal/raw_logging.h"  // For ABSL_RAW_CHECK
19 #include "absl/base/internal/spinlock.h"
20 
21 namespace absl {
22 ABSL_NAMESPACE_BEGIN
23 namespace cord_internal {
24 
25 using ::absl::base_internal::SpinLockHolder;
26 
27 ABSL_CONST_INIT CordzHandle::Queue CordzHandle::global_queue_(absl::kConstInit);
28 
CordzHandle(bool is_snapshot)29 CordzHandle::CordzHandle(bool is_snapshot) : is_snapshot_(is_snapshot) {
30   if (is_snapshot) {
31     SpinLockHolder lock(&queue_->mutex);
32     CordzHandle* dq_tail = queue_->dq_tail.load(std::memory_order_acquire);
33     if (dq_tail != nullptr) {
34       dq_prev_ = dq_tail;
35       dq_tail->dq_next_ = this;
36     }
37     queue_->dq_tail.store(this, std::memory_order_release);
38   }
39 }
40 
~CordzHandle()41 CordzHandle::~CordzHandle() {
42   ODRCheck();
43   if (is_snapshot_) {
44     std::vector<CordzHandle*> to_delete;
45     {
46       SpinLockHolder lock(&queue_->mutex);
47       CordzHandle* next = dq_next_;
48       if (dq_prev_ == nullptr) {
49         // We were head of the queue, delete every CordzHandle until we reach
50         // either the end of the list, or a snapshot handle.
51         while (next && !next->is_snapshot_) {
52           to_delete.push_back(next);
53           next = next->dq_next_;
54         }
55       } else {
56         // Another CordzHandle existed before this one, don't delete anything.
57         dq_prev_->dq_next_ = next;
58       }
59       if (next) {
60         next->dq_prev_ = dq_prev_;
61       } else {
62         queue_->dq_tail.store(dq_prev_, std::memory_order_release);
63       }
64     }
65     for (CordzHandle* handle : to_delete) {
66       delete handle;
67     }
68   }
69 }
70 
SafeToDelete() const71 bool CordzHandle::SafeToDelete() const {
72   return is_snapshot_ || queue_->IsEmpty();
73 }
74 
Delete(CordzHandle * handle)75 void CordzHandle::Delete(CordzHandle* handle) {
76   assert(handle);
77   if (handle) {
78     handle->ODRCheck();
79     Queue* const queue = handle->queue_;
80     if (!handle->SafeToDelete()) {
81       SpinLockHolder lock(&queue->mutex);
82       CordzHandle* dq_tail = queue->dq_tail.load(std::memory_order_acquire);
83       if (dq_tail != nullptr) {
84         handle->dq_prev_ = dq_tail;
85         dq_tail->dq_next_ = handle;
86         queue->dq_tail.store(handle, std::memory_order_release);
87         return;
88       }
89     }
90     delete handle;
91   }
92 }
93 
DiagnosticsGetDeleteQueue()94 std::vector<const CordzHandle*> CordzHandle::DiagnosticsGetDeleteQueue() {
95   std::vector<const CordzHandle*> handles;
96   SpinLockHolder lock(&global_queue_.mutex);
97   CordzHandle* dq_tail = global_queue_.dq_tail.load(std::memory_order_acquire);
98   for (const CordzHandle* p = dq_tail; p; p = p->dq_prev_) {
99     handles.push_back(p);
100   }
101   return handles;
102 }
103 
DiagnosticsHandleIsSafeToInspect(const CordzHandle * handle) const104 bool CordzHandle::DiagnosticsHandleIsSafeToInspect(
105     const CordzHandle* handle) const {
106   ODRCheck();
107   if (!is_snapshot_) return false;
108   if (handle == nullptr) return true;
109   if (handle->is_snapshot_) return false;
110   bool snapshot_found = false;
111   SpinLockHolder lock(&queue_->mutex);
112   for (const CordzHandle* p = queue_->dq_tail; p; p = p->dq_prev_) {
113     if (p == handle) return !snapshot_found;
114     if (p == this) snapshot_found = true;
115   }
116   ABSL_ASSERT(snapshot_found);  // Assert that 'this' is in delete queue.
117   return true;
118 }
119 
120 std::vector<const CordzHandle*>
DiagnosticsGetSafeToInspectDeletedHandles()121 CordzHandle::DiagnosticsGetSafeToInspectDeletedHandles() {
122   ODRCheck();
123   std::vector<const CordzHandle*> handles;
124   if (!is_snapshot()) {
125     return handles;
126   }
127 
128   SpinLockHolder lock(&queue_->mutex);
129   for (const CordzHandle* p = dq_next_; p != nullptr; p = p->dq_next_) {
130     if (!p->is_snapshot()) {
131       handles.push_back(p);
132     }
133   }
134   return handles;
135 }
136 
137 }  // namespace cord_internal
138 ABSL_NAMESPACE_END
139 }  // namespace absl
140