1 /*
2 * Copyright (c) 2011 by Hewlett-Packard Company. All rights reserved.
3 *
4 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
5 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
6 *
7 * Permission is hereby granted to use or copy this program
8 * for any purpose, provided the above notices are retained on all copies.
9 * Permission to modify the code and to distribute modified code is granted,
10 * provided the above notices are retained, and a notice that the code was
11 * modified is included with the above copyright notice.
12 */
13 14 #include "private/gc_priv.h"
15 16 #ifdef ENABLE_DISCLAIM
17 18 # include "gc/gc_disclaim.h"
19 # include "private/dbg_mlc.h" /*< for `oh` type */
20 21 # if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
22 /* The first bit is already used for a debug purpose. */
23 # define FINALIZER_CLOSURE_FLAG 0x2
24 # else
25 # define FINALIZER_CLOSURE_FLAG 0x1
26 # endif
27 28 STATIC int GC_CALLBACK
29 GC_finalized_disclaim(void *obj)
30 {
31 # ifdef AO_HAVE_load
32 ptr_t fc_p = GC_cptr_load((volatile ptr_t *)obj);
33 # else
34 ptr_t fc_p = *(ptr_t *)obj;
35 # endif
36 37 if ((ADDR(fc_p) & FINALIZER_CLOSURE_FLAG) != 0) {
38 /*
39 * The disclaim function may be passed fragments from the free-list,
40 * on which it should not run finalization. To recognize this case,
41 * we use the fact that the value of the first pointer of such
42 * fragments is always, at least, multiple of a pointer size (a link
43 * to the next fragment, or `NULL`).
44 *
45 * Note: if it is desirable to have a finalizer which does not use
46 * the first pointer for storing the finalization information,
47 * `GC_disclaim_and_reclaim()` must be extended to clear fragments
48 * so that the assumption holds for the selected pointer location.
49 */
50 const struct GC_finalizer_closure *fc
51 = (struct GC_finalizer_closure *)CPTR_CLEAR_FLAGS(
52 fc_p, FINALIZER_CLOSURE_FLAG);
53 54 GC_ASSERT(!GC_find_leak_inner);
55 fc->proc((ptr_t *)obj + 1, fc->cd);
56 }
57 return 0;
58 }
59 60 STATIC void
61 GC_register_disclaim_proc_inner(unsigned kind, GC_disclaim_proc proc,
62 GC_bool mark_unconditionally)
63 {
64 GC_ASSERT(kind < MAXOBJKINDS);
65 if (UNLIKELY(GC_find_leak_inner))
66 return;
67 68 GC_obj_kinds[kind].ok_disclaim_proc = proc;
69 GC_obj_kinds[kind].ok_mark_unconditionally = mark_unconditionally;
70 }
71 72 GC_API void GC_CALL
73 GC_init_finalized_malloc(void)
74 {
75 /* Initialize the collector just in case it is not done yet. */
76 GC_init();
77 78 LOCK();
79 if (GC_finalized_kind != 0) {
80 UNLOCK();
81 return;
82 }
83 84 /*
85 * The finalizer closure is placed in the first pointer of the
86 * object in order to use the lower bits to distinguish live
87 * objects from objects on the free list. The downside of this is
88 * that we need one-pointer offset interior pointers, and that
89 * `GC_base()` does not return the start of the user region.
90 */
91 GC_register_displacement_inner(sizeof(ptr_t));
92 93 /*
94 * And, the pointer to the finalizer closure object itself is displaced
95 * due to baking in this indicator.
96 */
97 GC_register_displacement_inner(FINALIZER_CLOSURE_FLAG);
98 GC_register_displacement_inner(sizeof(oh) | FINALIZER_CLOSURE_FLAG);
99 100 GC_finalized_kind
101 = GC_new_kind_inner(GC_new_free_list_inner(), GC_DS_LENGTH, TRUE, TRUE);
102 GC_ASSERT(GC_finalized_kind != 0);
103 GC_register_disclaim_proc_inner(GC_finalized_kind, GC_finalized_disclaim,
104 TRUE);
105 UNLOCK();
106 }
107 108 GC_API void GC_CALL
109 GC_register_disclaim_proc(int kind, GC_disclaim_proc proc,
110 int mark_unconditionally)
111 {
112 LOCK();
113 GC_register_disclaim_proc_inner((unsigned)kind, proc,
114 (GC_bool)mark_unconditionally);
115 UNLOCK();
116 }
117 118 GC_API GC_ATTR_MALLOC void *GC_CALL
119 GC_finalized_malloc(size_t lb, const struct GC_finalizer_closure *fclos)
120 {
121 void *op;
122 ptr_t fc_p;
123 124 # ifndef LINT2
125 /* Actually, there is no data race because the variable is set once. */
126 GC_ASSERT(GC_finalized_kind != 0);
127 # endif
128 GC_ASSERT(NONNULL_ARG_NOT_NULL(fclos));
129 GC_ASSERT((ADDR(fclos) & FINALIZER_CLOSURE_FLAG) == 0);
130 op = GC_malloc_kind(SIZET_SAT_ADD(lb, sizeof(ptr_t)),
131 (int)GC_finalized_kind);
132 if (UNLIKELY(NULL == op))
133 return NULL;
134 135 /*
136 * Set the flag (w/o conversion to a numeric type) and store
137 * the finalizer closure.
138 */
139 fc_p = CPTR_SET_FLAGS(GC_CAST_AWAY_CONST_PVOID(fclos),
140 FINALIZER_CLOSURE_FLAG);
141 # ifdef AO_HAVE_store
142 GC_cptr_store((volatile ptr_t *)op, fc_p);
143 # else
144 *(ptr_t *)op = fc_p;
145 # endif
146 GC_dirty(op);
147 REACHABLE_AFTER_DIRTY(fc_p);
148 return (ptr_t *)op + 1;
149 }
150 151 #endif /* ENABLE_DISCLAIM */
152