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
10using namespace QV4;
11
12namespace {
13
14struct Page;
15struct Header {
16 WTF::PageAllocation alloc;
17 ExecutionEngine *engine;
18 Page **prev;
19 Page *next;
20 int refCount;
21 int freeList;
22};
23
24static const int kEntriesPerPage = int((WTF::pageSize() - sizeof(Header)) / sizeof(Value));
25
26struct Page {
27 Header header;
28 Value values[1]; // Really kEntriesPerPage, but keep the compiler happy
29};
30
31Page *getPage(const Value *val) {
32 return reinterpret_cast<Page *>(reinterpret_cast<quintptr>(val) & ~((quintptr)(WTF::pageSize() - 1)));
33}
34
35QML_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
44QML_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
52Page *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
76PersistentValueStorage::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
84PersistentValueStorage::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
92PersistentValueStorage::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
106PersistentValueStorage::Iterator::~Iterator()
107{
108 Page *page = static_cast<Page *>(p);
109 if (page && !--page->header.refCount)
110 freePage(page);
111}
112
113PersistentValueStorage::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
132Value &PersistentValueStorage::Iterator::operator *()
133{
134 return static_cast<Page *>(p)->values[index];
135}
136
137PersistentValueStorage::PersistentValueStorage(ExecutionEngine *engine)
138 : engine(engine),
139 firstPage(nullptr)
140{
141}
142
143PersistentValueStorage::~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
161Value *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
195void 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
206void 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
219void 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
230ExecutionEngine *PersistentValueStorage::getEngine(const Value *v)
231{
232 return getPage(val: v)->header.engine;
233}
234
235void PersistentValueStorage::freePage(void *page)
236{
237 Page *p = static_cast<Page *>(page);
238 unlink(p);
239 p->header.alloc.deallocate();
240}
241
242
243PersistentValue::PersistentValue(const PersistentValue &other)
244 : val(nullptr)
245{
246 if (other.val)
247 set(engine: other.engine(), value: *other.val);
248}
249
250PersistentValue::PersistentValue(ExecutionEngine *engine, const Value &value)
251{
252 set(engine, value);
253}
254
255PersistentValue::PersistentValue(ExecutionEngine *engine, ReturnedValue value)
256{
257 set(engine, value);
258}
259
260PersistentValue::PersistentValue(ExecutionEngine *engine, Object *object)
261 : val(nullptr)
262{
263 if (!object)
264 return;
265 set(engine, value: *object);
266}
267
268PersistentValue &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
286PersistentValue &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
301PersistentValue &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
311void 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
322void 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
336void 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
348WeakValue::WeakValue(const WeakValue &other)
349 : val(nullptr)
350{
351 if (other.val) {
352 allocVal(engine: other.engine());
353 *val = *other.val;
354 }
355}
356
357WeakValue::WeakValue(ExecutionEngine *engine, const Value &value)
358{
359 allocVal(engine);
360 *val = value;
361}
362
363WeakValue &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
381WeakValue::~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 */
397void 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
410void 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
424void 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
436void WeakValue::allocVal(ExecutionEngine *engine)
437{
438 val = engine->memoryManager->m_weakValues->allocate();
439}
440
441void WeakValue::markOnce(MarkStack *markStack)
442{
443 if (!val)
444 return;
445 val->mark(markStack);
446}
447
448void 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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtdeclarative/src/qml/jsruntime/qv4persistent.cpp