FinalizationGroupObject.h 8.72 KB
Newer Older
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
/* -*- 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/. */

/*
 * FinalizationGroup objects allow a program to register to receive a callback
 * after a 'target' object dies. The callback is passed a 'holdings' value (that
 * hopefully doesn't entrain the target). An 'unregister token' is an object
 * which can be used to remove multiple previous registrations in one go.
 *
 * To arrange this, the following data structures are used:
 *
 *   +--------------------------------+----------------------------+
 *   |  FinalizationGroup compartment |  Target zone / compartment |
 *   |                                |                            |
 *   |      +-------------------+     |    +------------------+    |
 *   |  +-->+ FinalizationGroup |     |    |       Zone       |    |
 *   |  |   +---------+---------+     |    +---------+--------+    |
 *   |  |             |               |              |             |
 *   |  |             |               |              |             |
 *   |  |             v               |              v             |
 *   |  |  +----------+----------+    |   +----------+---------+   |
 *   |  |  |    Registrations    |    |   | FinalizationRecord |   |
 *   |  |  |      weak map       |    |   |        map         |   |
 *   |  |  +---------------------+    |   +--------------------+   |
 *   |  |  | Unregister : Record |    |   |  Target  : Record  |   |
 *   |  |  |   token    :  list  |    |   |          :  list   |   |
 *   |  |  +-----------------+---+    |   +-----+---------+----+   |
 *   |  |                    |        |         |         |        |
 *   |  |             +------+        |         |         |        |
 *   |  |           * v               |         |         |        |
 *   |  |  +----------+----------+ *  |         |         |        |
 *   |  |  | FinalizationRecord  +<-----------------------+        |
 *   |  |  +---------------------+    |         |                  |
 *   |  +--+ Group               |    |         v                  |
 *   |     +---------------------+    |     +---+---------+        |
 *   |     | Holdings            |    |     |   Target    |        |
 *   |     +---------------------+    |     +-------------+        |
 *   |                                |                            |
 *   +--------------------------------+----------------------------+
 *
 * Registering a target with a FinalizationGroup creates a FinalizationRecord
 * containing the group and the holdings. This is added to a vector of records
 * associated with the target, implemented as a map on the target's Zone. All
 * finalization records are treated as GC roots.
 *
 * When a target is registered an unregister token may be supplied. If so, this
 * is also recorded by the group and is stored in a weak map of
 * registrations. The values of this map are FinalizationRecordVector
 * objects. It's necessary to have another JSObject here because our weak map
 * implementation only supports JS types as values.
 *
 * After a target object has been registered with a finalization group it is
 * expected that its callback will be called for that object even if the
 * finalization group itself is no longer reachable from JS. Thus the values of
 * each zone's finalization record map are treated as roots and marked at the
 * start of GC.
 *
 * The finalization record maps are also swept during GC to check for any
 * targets that are dying. For such targets the associated record list is
 * processed and for each record the holdings is queued on finalization
 * group. At a later time this causes the client's callback to be run.
 *
 * When targets are unregistered, the registration is looked up in the weakmap
 * and the corresponding records are cleared. These are removed from the zone's
 * record map when it is next swept.
 */

#ifndef builtin_FinalizationGroupObject_h
#define builtin_FinalizationGroupObject_h

#include "gc/Barrier.h"
#include "js/GCVector.h"
#include "vm/NativeObject.h"

namespace js {

class FinalizationGroupObject;
class FinalizationIteratorObject;
class FinalizationRecordObject;
class ObjectWeakMap;

using HandleFinalizationGroupObject = Handle<FinalizationGroupObject*>;
using HandleFinalizationRecordObject = Handle<FinalizationRecordObject*>;
using RootedFinalizationGroupObject = Rooted<FinalizationGroupObject*>;
using RootedFinalizationIteratorObject = Rooted<FinalizationIteratorObject*>;
using RootedFinalizationRecordObject = Rooted<FinalizationRecordObject*>;

// A finalization record: a pair of finalization group and holdings value.
class FinalizationRecordObject : public NativeObject {
  enum { GroupSlot = 0, HoldingsSlot, SlotCount };

 public:
  static const JSClass class_;

  // The group can be a CCW to a FinalizationGroupObject.
  static FinalizationRecordObject* create(JSContext* cx,
                                          HandleFinalizationGroupObject group,
                                          HandleValue holdings);

  FinalizationGroupObject* group() const;
  Value holdings() const;
105
  bool wasCleared() const;
106
107
108
  void clear();
};

109
110
111
112
113
114
// A vector of FinalizationRecordObjects.
using FinalizationRecordVector =
    GCVector<HeapPtr<FinalizationRecordObject*>, 1, js::ZoneAllocPolicy>;

// A JS object that wraps a FinalizationRecordVector. Used as the values in the
// registration weakmap.
115
116
117
118
119
120
121
122
123
class FinalizationRecordVectorObject : public NativeObject {
  enum { RecordsSlot = 0, SlotCount };

 public:

  static const JSClass class_;

  static FinalizationRecordVectorObject* create(JSContext* cx);

124
125
  FinalizationRecordVector* records();
  const FinalizationRecordVector* records() const;
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

  bool isEmpty() const;

  bool append(HandleFinalizationRecordObject record);
  void remove(HandleFinalizationRecordObject record);

 private:
  static const JSClassOps classOps_;

  void* privatePtr() const;

  static void trace(JSTracer* trc, JSObject* obj);
  static void finalize(JSFreeOp* fop, JSObject* obj);
};

// The FinalizationGroup object itself.
class FinalizationGroupObject : public NativeObject {
  enum {
    CleanupCallbackSlot = 0,
    RegistrationsSlot,
146
    RecordsToBeCleanedUpSlot,
147
148
149
150
151
152
153
154
155
156
157
    IsQueuedForCleanupSlot,
    IsCleanupJobActiveSlot,
    SlotCount
  };

 public:
  static const JSClass class_;
  static const JSClass protoClass_;

  JSObject* cleanupCallback() const;
  ObjectWeakMap* registrations() const;
158
  FinalizationRecordVector* recordsToBeCleanedUp() const;
159
160
161
  bool isQueuedForCleanup() const;
  bool isCleanupJobActive() const;

162
  void queueRecordToBeCleanedUp(FinalizationRecordObject* record);
163
164
165
  void setQueuedForCleanup(bool value);
  void setCleanupJobActive(bool value);

166
167
168
  static bool cleanupQueuedRecords(JSContext* cx,
                                   HandleFinalizationGroupObject group,
                                   HandleObject callback = nullptr);
169
170
171
172
173
174
175
176

 private:
  static const JSClassOps classOps_;
  static const ClassSpec classSpec_;
  static const JSFunctionSpec methods_[];
  static const JSPropertySpec properties_[];

  static bool construct(JSContext* cx, unsigned argc, Value* vp);
177
178
179
180
181
182
183
184
185
186
187
  static bool register_(JSContext* cx, unsigned argc, Value* vp);
  static bool unregister(JSContext* cx, unsigned argc, Value* vp);
  static bool cleanupSome(JSContext* cx, unsigned argc, Value* vp);

  static bool addRegistration(JSContext* cx,
                              HandleFinalizationGroupObject group,
                              HandleObject unregisterToken,
                              HandleFinalizationRecordObject record);
  static void removeRegistrationOnError(HandleFinalizationGroupObject group,
                                        HandleObject unregisterToken,
                                        HandleFinalizationRecordObject record);
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206

  static void trace(JSTracer* trc, JSObject* obj);
  static void finalize(JSFreeOp* fop, JSObject* obj);
};

// An iterator over a finalization group's queued holdings. In the spec this is
// called FinalizationGroupCleanupIterator.
class FinalizationIteratorObject : public NativeObject {
  enum { FinalizationGroupSlot = 0, IndexSlot, SlotCount };

 public:
  static const JSClass class_;

  static FinalizationIteratorObject* create(
      JSContext* cx, HandleFinalizationGroupObject group);

  FinalizationGroupObject* finalizationGroup() const;
  size_t index() const;

207
  void setIndex(size_t index);
208
209
  void clearFinalizationGroup();

210
211
212
213
214
215
216
217
218
219
220
 private:
  friend class GlobalObject;
  static const JSFunctionSpec methods_[];
  static const JSPropertySpec properties_[];

  static bool next(JSContext* cx, unsigned argc, Value* vp);
};

}  // namespace js

#endif /* builtin_FinalizationGroupObject_h */