Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
port_set.h
Go to the documentation of this file.
1// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#ifndef RUNTIME_VM_PORT_SET_H_
6#define RUNTIME_VM_PORT_SET_H_
7
8#include "include/dart_api.h"
9
10#include "platform/allocation.h"
11#include "platform/globals.h"
12#include "platform/utils.h"
13
14namespace dart {
15
16template <typename T /* :public PortSet<T>::Entry */>
17class PortSet {
18 public:
19 static constexpr Dart_Port kFreePort = static_cast<Dart_Port>(0);
20 static constexpr Dart_Port kDeletedPort = static_cast<Dart_Port>(3);
21
22 struct Entry : public MallocAllocated {
24
25 // Free entries have set this to 0.
27 };
28
29 class Iterator {
30 public:
31 Iterator(PortSet<T>* ports, intptr_t index) : ports_(ports), index_(index) {
32#if defined(DEBUG)
33 dirty_counter_ = ports_->dirty_counter_;
34#endif
35 }
36
37 DART_FORCE_INLINE T& operator->() const {
38 ASSERT(index_ >= 0 && index_ < ports_->capacity_);
39 DEBUG_ASSERT(!WasModified());
40 return ports_->map_[index_];
41 }
42 DART_FORCE_INLINE T& operator*() const {
43 ASSERT(index_ >= 0 && index_ < ports_->capacity_);
44 DEBUG_ASSERT(!WasModified());
45 return ports_->map_[index_];
46 }
47
48 DART_FORCE_INLINE bool operator==(const Iterator& other) const {
49 DEBUG_ASSERT(!WasModified());
50 return ports_ == other.ports_ && index_ == other.index_;
51 }
52
53 DART_FORCE_INLINE bool operator!=(const Iterator& other) const {
54 DEBUG_ASSERT(!WasModified());
55 return !(*this == other);
56 }
57
58 DART_FORCE_INLINE Iterator& operator++() {
59 DEBUG_ASSERT(!WasModified());
60 index_++;
61 while (index_ < ports_->capacity_) {
62 const Dart_Port port = ports_->map_[index_].port;
63 if (port == kFreePort || port == kDeletedPort) {
64 index_++;
65 continue;
66 } else {
67 break;
68 }
69 }
70 return *this;
71 }
72
73 // The caller must ensure to call [PortSet::Rebalance] once the iterator is
74 // not used anymore.
75 DART_FORCE_INLINE void Delete() {
76 DEBUG_ASSERT(!WasModified());
77 ports_->map_[index_] = T();
78 ports_->map_[index_].port = kDeletedPort;
79 ports_->used_--;
80 ports_->deleted_++;
81 }
82
83 private:
84 friend class PortSet;
85
86#if defined(DEBUG)
87 // Whether the underlying [PortSet] was modified in a way that would render
88 // the iterator unusable.
89 bool WasModified() const {
90 return dirty_counter_ != ports_->dirty_counter_;
91 }
92#endif
93
94 PortSet<T>* ports_;
95 intptr_t index_ = 0;
96#if defined(DEBUG)
97 intptr_t dirty_counter_ = 0;
98#endif
99 };
100
102 const intptr_t kInitialCapacity = 8;
103 ASSERT(Utils::IsPowerOfTwo(kInitialCapacity));
104 map_ = new T[kInitialCapacity];
105 capacity_ = kInitialCapacity;
106 }
108 delete[] map_;
109 map_ = nullptr;
110 }
111
112 bool IsEmpty() const { return used_ == 0; }
113
114 DART_FORCE_INLINE Iterator begin() {
115 for (intptr_t i = 0; i < capacity_; ++i) {
116 auto& entry = map_[i];
117 if (entry.port != kFreePort && entry.port != kDeletedPort) {
118 return Iterator(this, i);
119 }
120 }
121 return end();
122 }
123
124 DART_FORCE_INLINE Iterator end() { return Iterator(this, capacity_); }
125
126 void Insert(const T& entry) {
127 // Search for the first unused slot. Make use of the knowledge that here is
128 // currently no port with this id in the port map.
129 ASSERT(FindIndexOfPort(entry.port) < 0);
130 intptr_t index = entry.port % capacity_;
131 T cur = map_[index];
132
133 // Stop the search at the first found unused (free or deleted) slot.
134 while (cur.port != kFreePort && cur.port != kDeletedPort) {
135 index = (index + 1) % capacity_;
136 cur = map_[index];
137 }
138
139 // Insert the newly created port at the index.
140 ASSERT(map_[index].port == kFreePort || map_[index].port == kDeletedPort);
141 if (map_[index].port == kDeletedPort) {
142 deleted_--;
143 }
144 map_[index] = entry;
145 ASSERT(FindIndexOfPort(entry.port) >= 0);
146
147 // Increment number of used slots and grow if necessary.
148 used_++;
149 MaintainInvariants();
150
151#if defined(DEBUG)
152 dirty_counter_++;
153#endif
154 }
155
157 const intptr_t index = FindIndexOfPort(port);
158 if (index >= 0) return Iterator(this, index);
159 return Iterator(this, capacity_);
160 }
161
162 bool Contains(Dart_Port port) { return FindIndexOfPort(port) >= 0; }
163
164 // To be called if an iterator was used to delete an entry.
165 void Rebalance() { MaintainInvariants(); }
166
167 private:
168 intptr_t FindIndexOfPort(Dart_Port port) {
169 // ILLEGAL_PORT (0) is used as a sentinel value in Entry.port. The loop
170 // below could return the index to a deleted port when we are searching for
171 // port id ILLEGAL_PORT. Return -1 immediately to indicate the port
172 // does not exist.
173 if (port == ILLEGAL_PORT) {
174 return -1;
175 }
176 ASSERT(port != ILLEGAL_PORT);
177 intptr_t index = port % capacity_;
178 intptr_t start_index = index;
179 T entry = map_[index];
180 while (entry.port != kFreePort) {
181 if (entry.port == port) {
182 return index;
183 }
184 index = (index + 1) % capacity_;
185 // Prevent endless loops.
186 ASSERT(index != start_index);
187 entry = map_[index];
188 }
189 return -1;
190 }
191
192 void MaintainInvariants() {
193 const intptr_t empty = capacity_ - used_ - deleted_;
194 if (used_ > ((capacity_ / 4) * 3)) {
195 // Grow the port map.
196 Rehash(capacity_ * 2);
197 } else if (empty < deleted_) {
198 // Rehash without growing the table to flush the deleted slots out of the
199 // map.
200 Rehash(capacity_);
201 }
202 }
203
204 void Rehash(intptr_t new_capacity) {
205 T* new_ports = new T[new_capacity];
206
207 for (auto entry : *this) {
208 intptr_t new_index = entry.port % new_capacity;
209 while (new_ports[new_index].port != 0) {
210 new_index = (new_index + 1) % new_capacity;
211 }
212 new_ports[new_index] = entry;
213 }
214 delete[] map_;
215 map_ = new_ports;
216 capacity_ = new_capacity;
217 deleted_ = 0;
218
219#if defined(DEBUG)
220 dirty_counter_++;
221#endif
222 }
223
224 T* map_ = nullptr;
225 intptr_t capacity_ = 0;
226 intptr_t used_ = 0;
227 intptr_t deleted_ = 0;
228
229#if defined(DEBUG)
230 intptr_t dirty_counter_ = 0;
231#endif
232};
233
234} // namespace dart
235
236#endif // RUNTIME_VM_PORT_SET_H_
#define DEBUG_ASSERT(cond)
Definition assert.h:321
DART_FORCE_INLINE bool operator==(const Iterator &other) const
Definition port_set.h:48
DART_FORCE_INLINE T & operator*() const
Definition port_set.h:42
DART_FORCE_INLINE Iterator & operator++()
Definition port_set.h:58
DART_FORCE_INLINE void Delete()
Definition port_set.h:75
Iterator(PortSet< T > *ports, intptr_t index)
Definition port_set.h:31
DART_FORCE_INLINE bool operator!=(const Iterator &other) const
Definition port_set.h:53
DART_FORCE_INLINE T & operator->() const
Definition port_set.h:37
void Insert(const T &entry)
Definition port_set.h:126
static constexpr Dart_Port kDeletedPort
Definition port_set.h:20
Iterator TryLookup(Dart_Port port)
Definition port_set.h:156
DART_FORCE_INLINE Iterator begin()
Definition port_set.h:114
DART_FORCE_INLINE Iterator end()
Definition port_set.h:124
void Rebalance()
Definition port_set.h:165
bool IsEmpty() const
Definition port_set.h:112
bool Contains(Dart_Port port)
Definition port_set.h:162
static constexpr Dart_Port kFreePort
Definition port_set.h:19
static constexpr bool IsPowerOfTwo(T x)
Definition utils.h:61
#define ILLEGAL_PORT
Definition dart_api.h:1530
int64_t Dart_Port
Definition dart_api.h:1524
#define ASSERT(E)
EMSCRIPTEN_KEEPALIVE void empty()
#define T