/home/travis/build/MoarVM/MoarVM/src/gc/finalize.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | /* Turns finalization on or off for a type. */ |
4 | 0 | void MVM_gc_finalize_set(MVMThreadContext *tc, MVMObject *type, MVMint64 finalize) { |
5 | 0 | MVMSTable *st = STABLE(type); |
6 | 0 | MVMint64 new_flags = st->mode_flags & (~MVM_FINALIZE_TYPE); |
7 | 0 | if (finalize) |
8 | 0 | new_flags |= MVM_FINALIZE_TYPE; |
9 | 0 | st->mode_flags = new_flags; |
10 | 0 | MVM_SC_WB_ST(tc, st); |
11 | 0 | } |
12 | | |
13 | | /* Adds an object we've just allocated to the queue of those with finalizers |
14 | | * that will need calling upon collection. */ |
15 | 0 | void MVM_gc_finalize_add_to_queue(MVMThreadContext *tc, MVMObject *obj) { |
16 | 0 | MVM_ASSERT_NOT_FROMSPACE(tc, obj); |
17 | 0 | if (tc->num_finalize == tc->alloc_finalize) { |
18 | 0 | if (tc->alloc_finalize) |
19 | 0 | tc->alloc_finalize *= 2; |
20 | 0 | else |
21 | 0 | tc->alloc_finalize = 64; |
22 | 0 | tc->finalize = MVM_realloc(tc->finalize, |
23 | 0 | sizeof(MVMCollectable **) * tc->alloc_finalize); |
24 | 0 | } |
25 | 0 | tc->finalize[tc->num_finalize] = obj; |
26 | 0 | tc->num_finalize++; |
27 | 0 | } |
28 | | |
29 | | /* Sets the passed thread context's thread up so that we'll run a finalize |
30 | | * handler on it in the near future. */ |
31 | 0 | static void finalize_handler_caller(MVMThreadContext *tc, void *sr_data) { |
32 | 0 | MVMObject *handler = MVM_hll_current(tc)->finalize_handler; |
33 | 0 | if (handler) { |
34 | 0 | MVMCallsite *inv_arg_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INV_ARG); |
35 | 0 |
|
36 | 0 | /* Drain the finalizing queue to an array. */ |
37 | 0 | MVMObject *drain = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray); |
38 | 0 | while (tc->num_finalizing > 0) |
39 | 0 | MVM_repr_push_o(tc, drain, tc->finalizing[--tc->num_finalizing]); |
40 | 0 |
|
41 | 0 | /* Invoke the handler. */ |
42 | 0 | handler = MVM_frame_find_invokee(tc, handler, NULL); |
43 | 0 | MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, inv_arg_callsite); |
44 | 0 | tc->cur_frame->args[0].o = drain; |
45 | 0 | STABLE(handler)->invoke(tc, handler, inv_arg_callsite, tc->cur_frame->args); |
46 | 0 | } |
47 | 0 | } |
48 | 0 | static void setup_finalize_handler_call(MVMThreadContext *tc) { |
49 | 0 | MVMFrame *install_on = tc->cur_frame; |
50 | 0 | while (install_on) { |
51 | 0 | if (!install_on->extra || !install_on->extra->special_return) |
52 | 0 | if (install_on->static_info->body.cu->body.hll_config) |
53 | 0 | break; |
54 | 0 | install_on = install_on->caller; |
55 | 0 | } |
56 | 0 | if (install_on) |
57 | 0 | MVM_frame_special_return(tc, install_on, finalize_handler_caller, NULL, |
58 | 0 | NULL, NULL); |
59 | 0 | } |
60 | | |
61 | | /* Walks through the per-thread finalize queues, identifying objects that |
62 | | * should be finalized, pushing them onto a finalize list, and then marking |
63 | | * that list entry. Assumes the world is stopped. */ |
64 | 0 | static void add_to_finalizing(MVMThreadContext *tc, MVMObject *obj) { |
65 | 0 | if (tc->num_finalizing == tc->alloc_finalizing) { |
66 | 0 | if (tc->alloc_finalizing) |
67 | 0 | tc->alloc_finalizing *= 2; |
68 | 0 | else |
69 | 0 | tc->alloc_finalizing = 64; |
70 | 0 | tc->finalizing = MVM_realloc(tc->finalizing, |
71 | 0 | sizeof(MVMCollectable **) * tc->alloc_finalizing); |
72 | 0 | } |
73 | 0 | tc->finalizing[tc->num_finalizing] = obj; |
74 | 0 | tc->num_finalizing++; |
75 | 0 | } |
76 | 750 | static void walk_thread_finalize_queue(MVMThreadContext *tc, MVMuint8 gen) { |
77 | 750 | MVMuint32 collapse_pos = 0; |
78 | 750 | MVMuint32 i; |
79 | 750 | for (i = 0; i < tc->num_finalize; i++) { |
80 | 0 | /* See if it's dead, taking which generation we've marked into |
81 | 0 | * account. */ |
82 | 0 | MVMuint32 flags = tc->finalize[i]->header.flags; |
83 | 0 | MVMuint32 in_gen2 = flags & MVM_CF_SECOND_GEN; |
84 | 0 | if (gen == MVMGCGenerations_Both || !in_gen2) { |
85 | 0 | MVMuint32 live = flags & (MVM_CF_GEN2_LIVE | MVM_CF_FORWARDER_VALID); |
86 | 0 | if (live) { |
87 | 0 | /* Alive, so just leave it in finalized queue, taking updated |
88 | 0 | * address if needed. */ |
89 | 0 | tc->finalize[collapse_pos++] = flags & MVM_CF_FORWARDER_VALID |
90 | 0 | ? (MVMObject *)tc->finalize[i]->header.sc_forward_u.forwarder |
91 | 0 | : tc->finalize[i]; |
92 | 0 | } |
93 | 0 | else { |
94 | 0 | /* Dead; needs finalizing, so pop it on the finalizing list. */ |
95 | 0 | add_to_finalizing(tc, tc->finalize[i]); |
96 | 0 | } |
97 | 0 | } |
98 | 0 | } |
99 | 750 | tc->num_finalize = collapse_pos; |
100 | 750 | } |
101 | 338 | void MVM_finalize_walk_queues(MVMThreadContext *tc, MVMuint8 gen) { |
102 | 338 | MVMThread *cur_thread = (MVMThread *)MVM_load(&tc->instance->threads); |
103 | 1.18k | while (cur_thread) { |
104 | 850 | if (cur_thread->body.tc) { |
105 | 750 | walk_thread_finalize_queue(cur_thread->body.tc, gen); |
106 | 750 | if (cur_thread->body.tc->num_finalizing > 0) { |
107 | 0 | MVM_gc_collect(cur_thread->body.tc, MVMGCWhatToDo_Finalizing, gen); |
108 | 0 | setup_finalize_handler_call(cur_thread->body.tc); |
109 | 0 | } |
110 | 750 | } |
111 | 850 | cur_thread = cur_thread->body.next; |
112 | 850 | } |
113 | 338 | } |