File: heap_observer_list.h

package info (click to toggle)
chromium 141.0.7390.107-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 6,254,812 kB
  • sloc: cpp: 35,264,957; ansic: 7,169,920; javascript: 4,250,185; python: 1,460,636; asm: 950,788; xml: 751,751; pascal: 187,972; sh: 89,459; perl: 88,691; objc: 79,953; sql: 53,924; cs: 44,622; fortran: 24,137; makefile: 22,319; tcl: 15,277; php: 14,018; yacc: 8,995; ruby: 7,553; awk: 3,720; lisp: 3,096; lex: 1,330; ada: 727; jsp: 228; sed: 36
file content (193 lines) | stat: -rw-r--r-- 7,819 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_OBSERVER_LIST_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_OBSERVER_LIST_H_

#include <algorithm>

#include "base/auto_reset.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
#include "third_party/blink/renderer/platform/heap/member.h"
#include "third_party/blink/renderer/platform/heap/visitor.h"
#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"

namespace blink {

// A list of observers. Ensures that the list is not mutated while iterating.
// Observers are not retained by the list. The implementation favors performance
// of iteration over modifications via add and remove.
template <class ObserverType>
class PLATFORM_EXPORT HeapObserverList final {
  DISALLOW_NEW();

 public:
  // Adds an observer to this list. An observer must not be added to the same
  // list more than once. Adding an observer is O(n) but amortized constant.
  void AddObserver(ObserverType* observer) {
    CHECK(mutation_state_ & kAllowAddition);
    DCHECK(!HasObserver(observer));
    DCHECK_EQ(observers_to_indices_.size(), observers_.size());
    // `push_back()` may trigger GC which results in `observers_to_indices_`
    // missing the to-be-added observer. This is okay in general as `observer`
    // is alive and in particular for `CleanupDeadObservers()` which is aware of
    // this fact.
    observers_.push_back(observer);
    observers_to_indices_.insert(observer, observers_.size() - 1);
    if (check_capacity_) [[unlikely]] {
      // On first addition after GC, check whether we need to shrink to a
      // reasoanble capacity.
      observers_.ShrinkToReasonableCapacity();
      // observers_to_indices_ did already shrink if necessary due to insert().
      check_capacity_ = false;
    }
  }

  // Removes the given observer from this list. Does nothing if this observer is
  // not in this list. Removing an observer is O(n) but amortized constant.
  void RemoveObserver(ObserverType* observer) {
    CHECK(mutation_state_ & kAllowRemoval);
    DCHECK_EQ(observers_to_indices_.size(), observers_.size());

    const auto it = observers_to_indices_.find(observer);
    if (it == observers_to_indices_.end()) [[unlikely]] {
      return;
    }
    const wtf_size_t index = it->value;
    const wtf_size_t last_observer_index = observers_.size() - 1;
    CHECK_LT(index, observers_.size());
    if (index != last_observer_index) {
      auto* last_observer = observers_[last_observer_index].Get();
      observers_[index] = last_observer;
      observers_to_indices_.find(last_observer)->value = index;
    }
    // `pop_back()` cannot invoke GC.
    observers_.pop_back();

    // `erase()` may trigger GC which is not a problem at this point as
    // `observers_` is already clean. It may just mean that more observer may be
    // removed from the data structure.
    observers_to_indices_.erase(it);

    observers_.ShrinkToReasonableCapacity();
    // observers_to_indices_ did already shrink if necessary due to erase().
    check_capacity_ = false;
  }

  // Determine whether a particular observer is in the list. Checking
  // containment is O(1).
  bool HasObserver(ObserverType* observer) const {
    DCHECK(!IsIteratingOverObservers());
    return observers_to_indices_.Contains(observer);
  }

  // Returns true if the list is being iterated over.
  bool IsIteratingOverObservers() const { return mutation_state_ != kAllowAll; }

  // Removes all the observers from this list.
  void Clear() {
    CHECK(mutation_state_ & kAllowRemoval);
    observers_.clear();
    observers_to_indices_.clear();
  }

  // Iterate over the registered lifecycle observers in an unpredictable order.
  //
  // Adding or removing observers is not allowed during iteration. The callable
  // will be called synchronously inside `ForEachObserver()`.
  //
  // Sample usage:
  // ```
  // ForEachObserver([](ObserverType* observer) {
  //   observer->SomeMethod();
  // });
  // ```
  template <typename ForEachCallable>
  void ForEachObserver(const ForEachCallable& callable) const {
    base::AutoReset<MutationState> scope(&mutation_state_, kNoMutationAllowed);
    for (ObserverType* observer : observers_) {
      // See CleanupDeadObservers() for why we can receive nullptr here.
      if (observer) {
        callable(observer);
      }
    }
  }

  void Trace(Visitor* visitor) const {
    visitor->Trace(observers_);
    visitor->RegisterWeakCallbackMethod<
        HeapObserverList, &HeapObserverList::CleanupDeadObservers>(this);
    visitor->Trace(observers_to_indices_);
  }

 private:
  void CleanupDeadObservers(const LivenessBroker& broker) {
    // This method must not allocate.

    // The GC currently does not strongify UntracedMember, even during
    // iteration. This implies that we must not move around items as using the
    // erase/remove_if pattern may move a valid object in a slot where we just
    // retrieved a nullptr.
    if (mutation_state_ == kNoMutationAllowed) {
      for (auto& observer : observers_) {
        if (!broker.IsHeapObjectAlive(observer)) {
          observer.Clear();
        }
      }
    } else {
      // We are not iterating, so we we can prepare the backing store by moving
      // dead slots to the end. Backing store reallocations will happen during
      // addition and removal.
      observers_.erase(
          std::remove_if(observers_.begin(), observers_.end(),
                         [broker](auto& observer) {
                           // If there's no iteration allowed we just `Clear()`
                           // the entry which writes nullptr.
                           // `IsHeapObjectAlive()` then treats nullptr as live
                           // object for other reasons. Consequently, we require
                           // an explicit nullptr check here.
                           return !observer ||
                                  !broker.IsHeapObjectAlive(observer);
                         }),
          observers_.end());
      // We also need to fix up the indices map. This works because we only
      // query and update live indices.
      for (wtf_size_t i = 0; i < observers_.size(); ++i) {
        // The callback here may be executed while `observers_to_indices_`
        // misses out on the observer that is either added or removed.
        const auto it = observers_to_indices_.find(observers_[i]);
        if (it != observers_to_indices_.end()) [[likely]] {
          it->value = i;
        }
      }
      check_capacity_ = true;
    }
  }

  // TODO(keishi): Clean up iteration state once transition from
  // LifecycleObserver is complete.
  enum MutationState {
    kNoMutationAllowed = 0,
    kAllowAddition = 1,
    kAllowRemoval = 1 << 1,
    kAllowAll = kAllowAddition | kAllowRemoval,
  };

  // MutationState records whether mutations are allowed to the set of
  // observers. Iteration e.g. prohibits any mutations.
  mutable MutationState mutation_state_ = kAllowAll;
  bool check_capacity_ = false;
  // Compact list of observers used for iteration. HeapVector doesn't allow
  // WeakMember at this point. Instead, use UntracedMember with a custom weak
  // callback.
  HeapVector<UntracedMember<ObserverType>> observers_;
  // Mapping observers to indices in `observers_`. Observers may be missing in
  // here temporarily as modification treats `observers_` as source of truth.
  HeapHashMap<WeakMember<ObserverType>, wtf_size_t> observers_to_indices_;
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_OBSERVER_LIST_H_