-
Notifications
You must be signed in to change notification settings - Fork 54
/
Copy pathFinalizationRegistry.cpp
131 lines (113 loc) · 4.67 KB
/
FinalizationRegistry.cpp
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
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Finalization registry GC implementation.
*/
#include "builtin/FinalizationRegistryObject.h"
#include "gc/GCRuntime.h"
#include "gc/Zone.h"
#include "vm/JSContext.h"
#include "gc/PrivateIterators-inl.h"
using namespace js;
using namespace js::gc;
bool GCRuntime::addFinalizationRegistry(JSContext* cx,
FinalizationRegistryObject* registry) {
if (!cx->zone()->finalizationRegistries().put(registry)) {
ReportOutOfMemory(cx);
return false;
}
return true;
}
bool GCRuntime::registerWithFinalizationRegistry(JSContext* cx,
HandleObject target,
HandleObject record) {
MOZ_ASSERT(!IsCrossCompartmentWrapper(target));
MOZ_ASSERT(
UncheckedUnwrapWithoutExpose(record)->is<FinalizationRecordObject>());
MOZ_ASSERT(target->compartment() == record->compartment());
auto& map = target->zone()->finalizationRecordMap();
auto ptr = map.lookupForAdd(target);
if (!ptr) {
if (!map.add(ptr, target, FinalizationRecordVector(target->zone()))) {
ReportOutOfMemory(cx);
return false;
}
}
if (!ptr->value().append(record)) {
ReportOutOfMemory(cx);
return false;
}
return true;
}
void GCRuntime::markFinalizationRegistryRoots(JSTracer* trc) {
// The held values for all finalization records store in zone maps are marked
// as roots. Finalization records store a finalization registry as a weak
// pointer in a private value, which does not get marked.
for (GCZonesIter zone(this); !zone.done(); zone.next()) {
Zone::FinalizationRecordMap& map = zone->finalizationRecordMap();
for (Zone::FinalizationRecordMap::Enum e(map); !e.empty(); e.popFront()) {
e.front().value().trace(trc);
}
}
}
static FinalizationRecordObject* UnwrapFinalizationRecord(JSObject* obj) {
obj = UncheckedUnwrapWithoutExpose(obj);
if (!obj->is<FinalizationRecordObject>()) {
MOZ_ASSERT(JS_IsDeadWrapper(obj));
// CCWs between the compartments have been nuked. The
// FinalizationRegistry's callback doesn't run in this case.
return nullptr;
}
return &obj->as<FinalizationRecordObject>();
}
void GCRuntime::sweepFinalizationRegistries(Zone* zone) {
// Sweep finalization registry data and queue finalization records for cleanup
// for any entries whose target is dying and remove them from the map.
Zone::FinalizationRegistrySet& set = zone->finalizationRegistries();
set.sweep();
for (auto r = set.all(); !r.empty(); r.popFront()) {
r.front()->as<FinalizationRegistryObject>().sweep();
}
Zone::FinalizationRecordMap& map = zone->finalizationRecordMap();
for (Zone::FinalizationRecordMap::Enum e(map); !e.empty(); e.popFront()) {
FinalizationRecordVector& records = e.front().value();
// Update any pointers moved by the GC.
records.sweep();
// Sweep finalization records and remove records for:
records.eraseIf([](JSObject* obj) {
FinalizationRecordObject* record = UnwrapFinalizationRecord(obj);
return !record || // Nuked CCW to record.
!record->isActive() || // Unregistered record or dead finalization
// registry in previous sweep group.
!record->sweep(); // Dead finalization registry in this sweep
// group.
});
// Queue finalization records for targets that are dying.
if (IsAboutToBeFinalized(&e.front().mutableKey())) {
for (JSObject* obj : records) {
FinalizationRecordObject* record = UnwrapFinalizationRecord(obj);
FinalizationRegistryObject* registry = record->registryDuringGC(this);
registry->queueRecordToBeCleanedUp(record);
queueFinalizationRegistryForCleanup(registry);
}
e.removeFront();
}
}
}
void GCRuntime::queueFinalizationRegistryForCleanup(
FinalizationRegistryObject* registry) {
// Prod the embedding to call us back later to run the finalization callbacks.
if (!registry->isQueuedForCleanup()) {
callHostCleanupFinalizationRegistryCallback(registry);
registry->setQueuedForCleanup(true);
}
}
bool GCRuntime::cleanupQueuedFinalizationRegistry(
JSContext* cx, HandleFinalizationRegistryObject registry) {
registry->setQueuedForCleanup(false);
bool ok = FinalizationRegistryObject::cleanupQueuedRecords(cx, registry);
return ok;
}