1 | // Copyright (C) 2016 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qv4persistent_p.h" |
5 | #include <private/qv4mm_p.h> |
6 | #include "qv4object_p.h" |
7 | #include "qv4qobjectwrapper_p.h" |
8 | #include "PageAllocation.h" |
9 | |
10 | using namespace QV4; |
11 | |
12 | namespace { |
13 | |
14 | struct Page; |
15 | struct Header { |
16 | WTF::PageAllocation alloc; |
17 | ExecutionEngine *engine; |
18 | Page **prev; |
19 | Page *next; |
20 | int refCount; |
21 | int freeList; |
22 | }; |
23 | |
24 | static const int kEntriesPerPage = int((WTF::pageSize() - sizeof(Header)) / sizeof(Value)); |
25 | |
26 | struct Page { |
27 | Header header; |
28 | Value values[1]; // Really kEntriesPerPage, but keep the compiler happy |
29 | }; |
30 | |
31 | Page *getPage(const Value *val) { |
32 | return reinterpret_cast<Page *>(reinterpret_cast<quintptr>(val) & ~((quintptr)(WTF::pageSize() - 1))); |
33 | } |
34 | |
35 | QML_NEARLY_ALWAYS_INLINE void insertInFront(PersistentValueStorage *storage, Page *p) |
36 | { |
37 | p->header.next = reinterpret_cast<Page *>(storage->firstPage); |
38 | p->header.prev = reinterpret_cast<Page **>(&storage->firstPage); |
39 | if (p->header.next) |
40 | p->header.next->header.prev = &p->header.next; |
41 | storage->firstPage = p; |
42 | } |
43 | |
44 | QML_NEARLY_ALWAYS_INLINE void unlink(Page *p) |
45 | { |
46 | if (p->header.prev) |
47 | *p->header.prev = p->header.next; |
48 | if (p->header.next) |
49 | p->header.next->header.prev = p->header.prev; |
50 | } |
51 | |
52 | Page *allocatePage(PersistentValueStorage *storage) |
53 | { |
54 | PageAllocation page = WTF::PageAllocation::allocate(size: WTF::pageSize()); |
55 | Page *p = reinterpret_cast<Page *>(page.base()); |
56 | |
57 | Q_ASSERT(!((quintptr)p & (WTF::pageSize() - 1))); |
58 | |
59 | p->header.engine = storage->engine; |
60 | p->header.alloc = page; |
61 | p->header.refCount = 0; |
62 | p->header.freeList = 0; |
63 | insertInFront(storage, p); |
64 | for (int i = 0; i < kEntriesPerPage - 1; ++i) { |
65 | p->values[i] = Encode(i + 1); |
66 | } |
67 | p->values[kEntriesPerPage - 1] = Encode(-1); |
68 | |
69 | return p; |
70 | } |
71 | |
72 | |
73 | } |
74 | |
75 | |
76 | PersistentValueStorage::Iterator::Iterator(void *p, int idx) |
77 | : p(p), index(idx) |
78 | { |
79 | Page *page = static_cast<Page *>(p); |
80 | if (page) |
81 | ++page->header.refCount; |
82 | } |
83 | |
84 | PersistentValueStorage::Iterator::Iterator(const PersistentValueStorage::Iterator &o) |
85 | : p(o.p), index(o.index) |
86 | { |
87 | Page *page = static_cast<Page *>(p); |
88 | if (page) |
89 | ++page->header.refCount; |
90 | } |
91 | |
92 | PersistentValueStorage::Iterator &PersistentValueStorage::Iterator::operator=(const PersistentValueStorage::Iterator &o) |
93 | { |
94 | Page *page = static_cast<Page *>(p); |
95 | if (page && !--page->header.refCount) |
96 | freePage(page: p); |
97 | p = o.p; |
98 | index = o.index; |
99 | page = static_cast<Page *>(p); |
100 | if (page) |
101 | ++page->header.refCount; |
102 | |
103 | return *this; |
104 | } |
105 | |
106 | PersistentValueStorage::Iterator::~Iterator() |
107 | { |
108 | Page *page = static_cast<Page *>(p); |
109 | if (page && !--page->header.refCount) |
110 | freePage(page); |
111 | } |
112 | |
113 | PersistentValueStorage::Iterator &PersistentValueStorage::Iterator::operator++() { |
114 | while (p) { |
115 | while (index < kEntriesPerPage - 1) { |
116 | ++index; |
117 | if (!static_cast<Page *>(p)->values[index].isEmpty()) |
118 | return *this; |
119 | } |
120 | index = -1; |
121 | Page *next = static_cast<Page *>(p)->header.next; |
122 | if (!--static_cast<Page *>(p)->header.refCount) |
123 | freePage(page: p); |
124 | p = next; |
125 | if (next) |
126 | ++next->header.refCount; |
127 | } |
128 | index = 0; |
129 | return *this; |
130 | } |
131 | |
132 | Value &PersistentValueStorage::Iterator::operator *() |
133 | { |
134 | return static_cast<Page *>(p)->values[index]; |
135 | } |
136 | |
137 | PersistentValueStorage::PersistentValueStorage(ExecutionEngine *engine) |
138 | : engine(engine), |
139 | firstPage(nullptr) |
140 | { |
141 | } |
142 | |
143 | PersistentValueStorage::~PersistentValueStorage() |
144 | { |
145 | clearFreePageHint(); |
146 | Page *p = static_cast<Page *>(firstPage); |
147 | while (p) { |
148 | for (int i = 0; i < kEntriesPerPage; ++i) { |
149 | if (!p->values[i].isEmpty()) |
150 | p->values[i] = Encode::undefined(); |
151 | } |
152 | Page *n = p->header.next; |
153 | p->header.engine = nullptr; |
154 | p->header.prev = nullptr; |
155 | p->header.next = nullptr; |
156 | Q_ASSERT(p->header.refCount); |
157 | p = n; |
158 | } |
159 | } |
160 | |
161 | Value *PersistentValueStorage::allocate() |
162 | { |
163 | Page *p = static_cast<Page *>(freePageHint); |
164 | if (p && p->header.freeList == -1) |
165 | p = static_cast<Page *>(firstPage); |
166 | while (p) { |
167 | if (p->header.freeList != -1) |
168 | break; |
169 | p = p->header.next; |
170 | } |
171 | if (!p) |
172 | p = allocatePage(storage: this); |
173 | |
174 | Value *v = p->values + p->header.freeList; |
175 | p->header.freeList = v->int_32(); |
176 | |
177 | if (p->header.freeList != -1 && p != freePageHint) { |
178 | if (auto oldHint = static_cast<Page *>(freePageHint)) { |
179 | oldHint->header.refCount--; |
180 | // no need to free - if the old page were unused, |
181 | // we would have used it to serve the allocation |
182 | Q_ASSERT(oldHint->header.refCount); |
183 | } |
184 | freePageHint = p; |
185 | p->header.refCount++; |
186 | } |
187 | |
188 | ++p->header.refCount; |
189 | |
190 | v->setRawValue(Encode::undefined()); |
191 | |
192 | return v; |
193 | } |
194 | |
195 | void PersistentValueStorage::freeUnchecked(Value *v) |
196 | { |
197 | Q_ASSERT(v); |
198 | Page *p = getPage(val: v); |
199 | |
200 | *v = Encode(p->header.freeList); |
201 | p->header.freeList = v - p->values; |
202 | if (!--p->header.refCount) |
203 | freePage(page: p); |
204 | } |
205 | |
206 | void PersistentValueStorage::mark(MarkStack *markStack) |
207 | { |
208 | Page *p = static_cast<Page *>(firstPage); |
209 | while (p) { |
210 | for (int i = 0; i < kEntriesPerPage; ++i) { |
211 | if (Managed *m = p->values[i].as<Managed>()) |
212 | m->mark(markStack); |
213 | } |
214 | |
215 | p = p->header.next; |
216 | } |
217 | } |
218 | |
219 | void PersistentValueStorage::clearFreePageHint() |
220 | { |
221 | if (!freePageHint) |
222 | return; |
223 | auto page = static_cast<Page *>(freePageHint); |
224 | if (!--page->header.refCount) |
225 | freePage(page); |
226 | freePageHint = nullptr; |
227 | |
228 | } |
229 | |
230 | ExecutionEngine *PersistentValueStorage::getEngine(const Value *v) |
231 | { |
232 | return getPage(val: v)->header.engine; |
233 | } |
234 | |
235 | void PersistentValueStorage::freePage(void *page) |
236 | { |
237 | Page *p = static_cast<Page *>(page); |
238 | unlink(p); |
239 | p->header.alloc.deallocate(); |
240 | } |
241 | |
242 | |
243 | PersistentValue::PersistentValue(const PersistentValue &other) |
244 | : val(nullptr) |
245 | { |
246 | if (other.val) |
247 | set(engine: other.engine(), value: *other.val); |
248 | } |
249 | |
250 | PersistentValue::PersistentValue(ExecutionEngine *engine, const Value &value) |
251 | { |
252 | set(engine, value); |
253 | } |
254 | |
255 | PersistentValue::PersistentValue(ExecutionEngine *engine, ReturnedValue value) |
256 | { |
257 | set(engine, value); |
258 | } |
259 | |
260 | PersistentValue::PersistentValue(ExecutionEngine *engine, Object *object) |
261 | : val(nullptr) |
262 | { |
263 | if (!object) |
264 | return; |
265 | set(engine, value: *object); |
266 | } |
267 | |
268 | PersistentValue &PersistentValue::operator=(const PersistentValue &other) |
269 | { |
270 | if (!val) { |
271 | if (!other.val) |
272 | return *this; |
273 | val = other.engine()->memoryManager->m_persistentValues->allocate(); |
274 | } |
275 | if (!other.val) { |
276 | *val = Encode::undefined(); |
277 | return *this; |
278 | } |
279 | |
280 | Q_ASSERT(engine() == other.engine()); |
281 | |
282 | *val = *other.val; |
283 | return *this; |
284 | } |
285 | |
286 | PersistentValue &PersistentValue::operator=(const WeakValue &other) |
287 | { |
288 | if (!val && !other.valueRef()) |
289 | return *this; |
290 | if (!other.valueRef()) { |
291 | *val = Encode::undefined(); |
292 | return *this; |
293 | } |
294 | |
295 | Q_ASSERT(!engine() || engine() == other.engine()); |
296 | |
297 | set(engine: other.engine(), value: *other.valueRef()); |
298 | return *this; |
299 | } |
300 | |
301 | PersistentValue &PersistentValue::operator=(Object *object) |
302 | { |
303 | if (!object) { |
304 | PersistentValueStorage::free(v: val); |
305 | return *this; |
306 | } |
307 | set(engine: object->engine(), value: *object); |
308 | return *this; |
309 | } |
310 | |
311 | void PersistentValue::set(ExecutionEngine *engine, const Value &value) |
312 | { |
313 | if (!val) |
314 | val = engine->memoryManager->m_persistentValues->allocate(); |
315 | QV4::WriteBarrier::markCustom(engine, markFunction: [&](QV4::MarkStack *stack){ |
316 | if (QV4::WriteBarrier::isInsertionBarrier && value.isManaged()) |
317 | value.heapObject()->mark(markStack: stack); |
318 | }); |
319 | *val = value; |
320 | } |
321 | |
322 | void PersistentValue::set(ExecutionEngine *engine, ReturnedValue value) |
323 | { |
324 | if (!val) |
325 | val = engine->memoryManager->m_persistentValues->allocate(); |
326 | QV4::WriteBarrier::markCustom(engine, markFunction: [&](QV4::MarkStack *stack){ |
327 | if constexpr (!QV4::WriteBarrier::isInsertionBarrier) |
328 | return; |
329 | auto val = Value::fromReturnedValue(val: value); |
330 | if (val.isManaged()) |
331 | val.heapObject()->mark(markStack: stack); |
332 | }); |
333 | *val = value; |
334 | } |
335 | |
336 | void PersistentValue::set(ExecutionEngine *engine, Heap::Base *obj) |
337 | { |
338 | if (!val) |
339 | val = engine->memoryManager->m_persistentValues->allocate(); |
340 | QV4::WriteBarrier::markCustom(engine, markFunction: [&](QV4::MarkStack *stack){ |
341 | if constexpr (QV4::WriteBarrier::isInsertionBarrier) |
342 | obj->mark(markStack: stack); |
343 | }); |
344 | |
345 | *val = obj; |
346 | } |
347 | |
348 | WeakValue::WeakValue(const WeakValue &other) |
349 | : val(nullptr) |
350 | { |
351 | if (other.val) { |
352 | allocVal(engine: other.engine()); |
353 | *val = *other.val; |
354 | } |
355 | } |
356 | |
357 | WeakValue::WeakValue(ExecutionEngine *engine, const Value &value) |
358 | { |
359 | allocVal(engine); |
360 | *val = value; |
361 | } |
362 | |
363 | WeakValue &WeakValue::operator=(const WeakValue &other) |
364 | { |
365 | if (!val) { |
366 | if (!other.val) |
367 | return *this; |
368 | allocVal(engine: other.engine()); |
369 | } |
370 | if (!other.val) { |
371 | *val = Encode::undefined(); |
372 | return *this; |
373 | } |
374 | |
375 | Q_ASSERT(engine() == other.engine()); |
376 | |
377 | *val = *other.val; |
378 | return *this; |
379 | } |
380 | |
381 | WeakValue::~WeakValue() |
382 | { |
383 | free(); |
384 | } |
385 | |
386 | /* |
387 | WeakValue::set shold normally not mark objects, after all a weak value |
388 | is not supposed to keep an object alive. |
389 | However, if we are past GCState::HandleQObjectWrappers, nothing will |
390 | reset weak values referencing unmarked values, but those values will |
391 | still be swept. |
392 | That lead to stale pointers, and potentially to crashes. To avoid this, |
393 | we mark the objects here (they might still get collected in the next gc |
394 | run). |
395 | This is especially important due to the way we handle QObjectWrappers. |
396 | */ |
397 | void WeakValue::set(ExecutionEngine *engine, const Value &value) |
398 | { |
399 | if (!val) |
400 | allocVal(engine); |
401 | QV4::WriteBarrier::markCustom(engine, markFunction: [&](QV4::MarkStack *ms) { |
402 | if (engine->memoryManager->gcStateMachine->state <= GCState::HandleQObjectWrappers) |
403 | return; |
404 | if (auto *h = value.heapObject()) |
405 | h->mark(markStack: ms); |
406 | }); |
407 | *val = value; |
408 | } |
409 | |
410 | void WeakValue::set(ExecutionEngine *engine, ReturnedValue value) |
411 | { |
412 | if (!val) |
413 | allocVal(engine); |
414 | QV4::WriteBarrier::markCustom(engine, markFunction: [&](QV4::MarkStack *ms) { |
415 | if (engine->memoryManager->gcStateMachine->state <= GCState::HandleQObjectWrappers) |
416 | return; |
417 | if (auto *h = QV4::Value::fromReturnedValue(val: value).heapObject()) |
418 | h->mark(markStack: ms); |
419 | }); |
420 | |
421 | *val = value; |
422 | } |
423 | |
424 | void WeakValue::set(ExecutionEngine *engine, Heap::Base *obj) |
425 | { |
426 | if (!val) |
427 | allocVal(engine); |
428 | QV4::WriteBarrier::markCustom(engine, markFunction: [&](QV4::MarkStack *ms) { |
429 | if (engine->memoryManager->gcStateMachine->state <= GCState::HandleQObjectWrappers) |
430 | return; |
431 | obj->mark(markStack: ms); |
432 | }); |
433 | *val = obj; |
434 | } |
435 | |
436 | void WeakValue::allocVal(ExecutionEngine *engine) |
437 | { |
438 | val = engine->memoryManager->m_weakValues->allocate(); |
439 | } |
440 | |
441 | void WeakValue::markOnce(MarkStack *markStack) |
442 | { |
443 | if (!val) |
444 | return; |
445 | val->mark(markStack); |
446 | } |
447 | |
448 | void WeakValue::free() |
449 | { |
450 | if (!val) |
451 | return; |
452 | |
453 | ExecutionEngine *e = engine(); |
454 | if (e && val->as<QObjectWrapper>()) { |
455 | // Some QV4::QObjectWrapper Value will be freed in WeakValue::~WeakValue() before MemoryManager::sweep() is being called, |
456 | // in this case we will never have a chance to call detroyObject() on those QV4::QObjectWrapper objects. |
457 | // Here we don't free these Value immediately, instead we keep track of them to free them later in MemoryManager::sweep() |
458 | e->memoryManager->m_pendingFreedObjectWrapperValue.push_back(t: val); |
459 | } else { |
460 | PersistentValueStorage::free(v: val); |
461 | } |
462 | |
463 | val = nullptr; |
464 | } |
465 | |
466 |
Definitions
- Header
- kEntriesPerPage
- Page
- getPage
- insertInFront
- unlink
- allocatePage
- Iterator
- Iterator
- operator=
- ~Iterator
- operator++
- operator *
- PersistentValueStorage
- ~PersistentValueStorage
- allocate
- freeUnchecked
- mark
- clearFreePageHint
- getEngine
- freePage
- PersistentValue
- PersistentValue
- PersistentValue
- PersistentValue
- operator=
- operator=
- operator=
- set
- set
- set
- WeakValue
- WeakValue
- operator=
- ~WeakValue
- set
- set
- set
- allocVal
- markOnce
Learn Advanced QML with KDAB
Find out more