-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathfnlz_mlc.c
More file actions
208 lines (185 loc) · 6.91 KB
/
Copy pathfnlz_mlc.c
File metadata and controls
208 lines (185 loc) · 6.91 KB
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/*
* Copyright (c) 2011 by Hewlett-Packard Company. All rights reserved.
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
*
* Permission is hereby granted to use or copy this program
* for any purpose, provided the above notices are retained on all copies.
* Permission to modify the code and to distribute modified code is granted,
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
#include "private/gc_priv.h"
#ifdef ENABLE_DISCLAIM
# include "gc/gc_disclaim.h"
# include "private/dbg_mlc.h" /*< for `oh` type */
# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
/* The first bit is already used for a debug purpose. */
# define FINALIZER_CLOSURE_FLAG 0x2
# else
# define FINALIZER_CLOSURE_FLAG 0x1
# endif
STATIC int GC_CALLBACK
GC_finalized_disclaim(void *obj)
{
# ifdef AO_HAVE_load
ptr_t fc_p = GC_cptr_load((volatile ptr_t *)obj);
# else
ptr_t fc_p = *(ptr_t *)obj;
# endif
if ((ADDR(fc_p) & FINALIZER_CLOSURE_FLAG) != 0) {
/*
* The disclaim function may be passed fragments from the free-list,
* on which it should not run finalization. To recognize this case,
* we use the fact that the value of the first pointer of such
* fragments is always, at least, multiple of a pointer size (a link
* to the next fragment, or `NULL`).
*
* Note: if it is desirable to have a finalizer which does not use
* the first pointer for storing the finalization information,
* `GC_disclaim_and_reclaim()` must be extended to clear fragments
* so that the assumption holds for the selected pointer location.
*/
const struct GC_finalizer_closure *fc
= (struct GC_finalizer_closure *)CPTR_CLEAR_FLAGS(
fc_p, FINALIZER_CLOSURE_FLAG);
GC_ASSERT(!GC_find_leak_inner);
fc->proc((ptr_t *)obj + 1, fc->cd);
}
return 0;
}
STATIC void
GC_register_disclaim_proc_inner(unsigned kind, GC_disclaim_proc proc,
GC_bool mark_unconditionally)
{
GC_ASSERT(kind < MAXOBJKINDS);
if (UNLIKELY(GC_find_leak_inner))
return;
GC_obj_kinds[kind].ok_disclaim_proc = proc;
GC_obj_kinds[kind].ok_mark_unconditionally = mark_unconditionally;
}
GC_API void GC_CALL
GC_init_finalized_malloc(void)
{
/* Initialize the collector just in case it is not done yet. */
GC_init();
LOCK();
if (GC_finalized_kind != 0) {
UNLOCK();
return;
}
/*
* The finalizer closure is placed in the first pointer of the
* object in order to use the lower bits to distinguish live
* objects from objects on the free list. The downside of this is
* that we need one-pointer offset interior pointers, and that
* `GC_base()` does not return the start of the user region.
*/
GC_register_displacement_inner(sizeof(ptr_t));
/*
* And, the pointer to the finalizer closure object itself is displaced
* due to baking in this indicator.
*/
GC_register_displacement_inner(FINALIZER_CLOSURE_FLAG);
GC_register_displacement_inner(sizeof(oh) | FINALIZER_CLOSURE_FLAG);
GC_finalized_kind
= GC_new_kind_inner(GC_new_free_list_inner(), GC_DS_LENGTH, TRUE, TRUE);
GC_ASSERT(GC_finalized_kind != 0);
/*
* Escargot note: upstream registers this with mark_unconditionally=TRUE so
* that fields a finalizer reads stay valid even for a garbage object. We
* ship it as FALSE instead: every GC_finalized_malloc() caller in this tree
* only touches native (non-GC) handles from its closure, never a separately
* GC-managed pointer, so that guarantee buys nothing here. Its cost is real:
* with mark_unconditionally=TRUE, an unconditional visit to a dead object
* treats any pointer inside its (conservatively scanned, GC_DS_LENGTH)
* memory as a live reference -- including one the object's own property
* storage holds back to itself. A script as simple as
* `let d = new Intl.DateTimeFormat(); d.self = d; d = null;` then
* resurrects `d` forever, because the forced scan finds `d.self` and marks
* `d` reachable again every single cycle. mark_unconditionally=FALSE means
* this kind is only ever mark-scanned when actually reachable, so a
* self-referencing island with no external roots is collected normally;
* GC_finalized_disclaim() still runs during reclaim regardless of this
* flag, so finalization itself is unaffected.
*/
GC_register_disclaim_proc_inner(GC_finalized_kind, GC_finalized_disclaim,
FALSE);
GC_finalized_ptrfree_kind
= GC_new_kind_inner(GC_new_free_list_inner(), GC_DS_LENGTH, FALSE, TRUE);
GC_ASSERT(GC_finalized_ptrfree_kind != 0);
GC_register_disclaim_proc_inner(GC_finalized_ptrfree_kind,
GC_finalized_disclaim, TRUE);
UNLOCK();
}
GC_API void GC_CALL
GC_register_disclaim_proc(int kind, GC_disclaim_proc proc,
int mark_unconditionally)
{
LOCK();
GC_register_disclaim_proc_inner((unsigned)kind, proc,
mark_unconditionally != 0);
UNLOCK();
}
GC_API GC_ATTR_MALLOC void *GC_CALL
GC_finalized_malloc(size_t lb, const struct GC_finalizer_closure *fclos)
{
void *op;
ptr_t fc_p;
# ifndef LINT2
/* Actually, there is no data race because the variable is set once. */
GC_ASSERT(GC_finalized_kind != 0);
# endif
GC_ASSERT(NONNULL_ARG_NOT_NULL(fclos));
GC_ASSERT((ADDR(fclos) & FINALIZER_CLOSURE_FLAG) == 0);
op = GC_malloc_kind(SIZET_SAT_ADD(lb, sizeof(ptr_t)),
(int)GC_finalized_kind);
if (UNLIKELY(NULL == op))
return NULL;
/*
* Set the flag (w/o conversion to a numeric type) and store
* the finalizer closure.
*/
fc_p = CPTR_SET_FLAGS(GC_CAST_AWAY_CONST_PVOID(fclos),
FINALIZER_CLOSURE_FLAG);
# ifdef AO_HAVE_store
GC_cptr_store((volatile ptr_t *)op, fc_p);
# else
*(ptr_t *)op = fc_p;
# endif
GC_dirty(op);
REACHABLE_AFTER_DIRTY(fc_p);
return (ptr_t *)op + 1;
}
GC_API GC_ATTR_MALLOC void *GC_CALL
GC_finalized_atomic_malloc(size_t lb, const struct GC_finalizer_closure *fclos)
{
void *op;
ptr_t fc_p;
# ifndef LINT2
/* Actually, there is no data race because the variable is set once. */
GC_ASSERT(GC_finalized_ptrfree_kind != 0);
# endif
GC_ASSERT(NONNULL_ARG_NOT_NULL(fclos));
GC_ASSERT((ADDR(fclos) & FINALIZER_CLOSURE_FLAG) == 0);
op = GC_malloc_kind(SIZET_SAT_ADD(lb, sizeof(ptr_t)),
(int)GC_finalized_ptrfree_kind);
if (UNLIKELY(NULL == op))
return NULL;
/*
* Set the flag (w/o conversion to a numeric type) and store
* the finalizer closure.
*/
fc_p = CPTR_SET_FLAGS(GC_CAST_AWAY_CONST_PVOID(fclos),
FINALIZER_CLOSURE_FLAG);
# ifdef AO_HAVE_store
GC_cptr_store((volatile ptr_t *)op, fc_p);
# else
*(ptr_t *)op = fc_p;
# endif
GC_dirty(op);
REACHABLE_AFTER_DIRTY(fc_p);
return (ptr_t *)op + 1;
}
#endif /* ENABLE_DISCLAIM */