Skip to content

Commit fe0fa4c

Browse files
authored
Delay the start of marking with a new GC phase (#2348)
Currently, this implementation is single-domain only. During the new GC phase, marking does not occur. This means that new allocations can be UNMARKED, and caml_modify need not darken the values it sees.
1 parent 57238b3 commit fe0fa4c

File tree

12 files changed

+115
-47
lines changed

12 files changed

+115
-47
lines changed

ocaml/runtime/array.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,8 @@ CAMLprim value caml_array_fill(value array,
860860
*fp = val;
861861
if (Is_block(old)) {
862862
if (Is_young(old)) continue;
863-
caml_darken(Caml_state, old, NULL);
863+
if (caml_marking_started())
864+
caml_darken(Caml_state, old, NULL);
864865
}
865866
if (is_val_young_block)
866867
Ref_table_add(&Caml_state->minor_tables->major_ref, fp);

ocaml/runtime/caml/major_gc.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#ifdef CAML_INTERNALS
2020

2121
typedef enum {
22+
Phase_sweep_main,
2223
Phase_sweep_and_mark_main,
2324
Phase_mark_final,
2425
Phase_sweep_ephe
@@ -27,6 +28,8 @@ extern gc_phase_t caml_gc_phase;
2728

2829
Caml_inline char caml_gc_phase_char(gc_phase_t phase) {
2930
switch (phase) {
31+
case Phase_sweep_main:
32+
return 'S';
3033
case Phase_sweep_and_mark_main:
3134
return 'M';
3235
case Phase_mark_final:
@@ -38,6 +41,10 @@ Caml_inline char caml_gc_phase_char(gc_phase_t phase) {
3841
}
3942
}
4043

44+
Caml_inline int caml_marking_started(void) {
45+
return caml_gc_phase != Phase_sweep_main;
46+
}
47+
4148
intnat caml_opportunistic_major_work_available (void);
4249
void caml_opportunistic_major_collection_slice (intnat);
4350
/* auto-triggered slice from within the GC */

ocaml/runtime/caml/shared_heap.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "domain.h"
2424
#include "misc.h"
2525
#include "gc_stats.h"
26+
#include "major_gc.h"
2627

2728
CAMLextern atomic_uintnat caml_compactions_count;
2829

@@ -92,6 +93,13 @@ Caml_inline int is_not_markable(value v) {
9293
return Has_status_val(v, NOT_MARKABLE);
9394
}
9495

96+
Caml_inline status caml_allocation_status(void) {
97+
return
98+
caml_marking_started()
99+
? caml_global_heap_state.MARKED
100+
: caml_global_heap_state.UNMARKED;
101+
}
102+
95103
void caml_redarken_pool(struct pool*, scanning_action, void*);
96104

97105
intnat caml_sweep(struct caml_heap_state*, intnat);

ocaml/runtime/domain.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,11 +1784,12 @@ static void handover_finalisers(caml_domain_state* domain_state)
17841784

17851785
if (f->todo_head != NULL || f->first.size != 0 || f->last.size != 0) {
17861786
/* have some final structures */
1787-
if (caml_gc_phase != Phase_sweep_and_mark_main) {
1787+
if (caml_gc_phase != Phase_sweep_main &&
1788+
caml_gc_phase != Phase_sweep_and_mark_main) {
17881789
/* Force a major GC cycle to simplify constraints for
17891790
* handing over finalisers. */
17901791
caml_finish_major_cycle(0);
1791-
CAMLassert(caml_gc_phase == Phase_sweep_and_mark_main);
1792+
CAMLassert(caml_gc_phase == Phase_sweep_main);
17921793
}
17931794
caml_add_orphaned_finalisers (f);
17941795
/* Create a dummy final info */

ocaml/runtime/fiber.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -845,9 +845,9 @@ CAMLprim value caml_continuation_use_noexc (value cont)
845845

846846
/* this forms a barrier between execution and any other domains
847847
that might be marking this continuation */
848-
if (!Is_young(cont) ) caml_darken_cont(cont);
848+
if (!Is_young(cont) && caml_marking_started())
849+
caml_darken_cont(cont);
849850

850-
/* at this stage the stack is assured to be marked */
851851
v = Field(cont, 0);
852852

853853
if (caml_domain_alone()) {

ocaml/runtime/intern.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ static value intern_alloc_obj(struct caml_intern_state* s, caml_domain_state* d,
407407
intern_cleanup (s);
408408
caml_raise_out_of_memory();
409409
}
410-
Hd_hp(p) = Make_header (wosize, tag, caml_global_heap_state.MARKED);
410+
Hd_hp(p) = Make_header (wosize, tag, caml_allocation_status());
411411
}
412412
return Val_hp(p);
413413
}

ocaml/runtime/major_gc.c

Lines changed: 68 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ static void ephe_next_cycle (void)
217217

218218
static void ephe_todo_list_emptied (void)
219219
{
220+
/* If we haven't started marking, the todo list can grow (during ephemeron
221+
allocation), so we should not yet announce that it has emptied */
222+
CAMLassert (caml_marking_started());
220223
caml_plat_lock(&ephe_lock);
221224

222225
/* Force next ephemeron marking cycle in order to avoid reasoning about
@@ -263,7 +266,7 @@ static caml_plat_mutex orphaned_lock = CAML_PLAT_MUTEX_INITIALIZER;
263266

264267
void caml_add_orphaned_finalisers (struct caml_final_info* f)
265268
{
266-
CAMLassert (caml_gc_phase == Phase_sweep_and_mark_main);
269+
CAMLassert (caml_gc_phase == Phase_sweep_main);
267270
CAMLassert (!f->updated_first);
268271
CAMLassert (!f->updated_last);
269272

@@ -384,7 +387,7 @@ void caml_adopt_orphaned_work (void)
384387
CAMLassert (!f->updated_last);
385388
CAMLassert (!myf->updated_first);
386389
CAMLassert (!myf->updated_last);
387-
CAMLassert (caml_gc_phase == Phase_sweep_and_mark_main);
390+
CAMLassert (caml_gc_phase == Phase_sweep_main);
388391
if (f->todo_head) {
389392
if (myf->todo_tail == NULL) {
390393
CAMLassert(myf->todo_head == NULL);
@@ -1053,6 +1056,7 @@ void caml_darken(void* state, value v, volatile value* ignored) {
10531056
header_t hd;
10541057
if (!Is_markable (v)) return; /* foreign stack, at least */
10551058

1059+
CAMLassert(caml_marking_started());
10561060
hd = Hd_val(v);
10571061
if (Tag_hd(hd) == Infix_tag) {
10581062
v -= Infix_offset_hd(hd);
@@ -1089,6 +1093,7 @@ static intnat ephe_mark (intnat budget, uintnat for_cycle,
10891093
int alive_data;
10901094
intnat marked = 0, made_live = 0;
10911095

1096+
CAMLassert(caml_marking_started());
10921097
if (domain_state->ephe_info->cursor.cycle == for_cycle &&
10931098
!force_alive) {
10941099
prev_linkp = domain_state->ephe_info->cursor.todop;
@@ -1189,6 +1194,51 @@ static intnat ephe_sweep (caml_domain_state* domain_state, intnat budget)
11891194
return budget;
11901195
}
11911196

1197+
static void start_marking (int participant_count, caml_domain_state** barrier_participants)
1198+
{
1199+
caml_domain_state* domain = Caml_state;
1200+
/* Need to ensure the minor heap is empty before we snapshot the roots,
1201+
because the minor heap may currently point to UNMARKED major blocks */
1202+
if (barrier_participants) {
1203+
caml_empty_minor_heap_no_major_slice_from_stw
1204+
(domain, (void*)0, participant_count, barrier_participants);
1205+
} else {
1206+
caml_empty_minor_heaps_once ();
1207+
}
1208+
1209+
/* CR ocaml 5 domains (sdolan):
1210+
Either this transition needs to be synchronised between domains,
1211+
or a different write barrier needs to be used while some domains
1212+
have started marking and others have not. */
1213+
CAMLassert(caml_domain_alone());
1214+
caml_gc_phase = Phase_sweep_and_mark_main;
1215+
1216+
CAML_EV_BEGIN(EV_MAJOR_MARK_ROOTS);
1217+
caml_do_roots (&caml_darken, darken_scanning_flags, domain, domain, 0);
1218+
{
1219+
uintnat work_unstarted = WORK_UNSTARTED;
1220+
if(atomic_compare_exchange_strong(&domain_global_roots_started,
1221+
&work_unstarted,
1222+
WORK_STARTED)){
1223+
caml_scan_global_roots(&caml_darken, domain);
1224+
}
1225+
}
1226+
CAML_EV_END(EV_MAJOR_MARK_ROOTS);
1227+
caml_gc_log("Marking started, %ld entries on mark stack",
1228+
(long)domain->mark_stack->count);
1229+
1230+
if (domain->mark_stack->count == 0 &&
1231+
!caml_addrmap_iter_ok(&domain->mark_stack->compressed_stack,
1232+
domain->mark_stack->compressed_stack_iter)
1233+
) {
1234+
atomic_fetch_add_verify_ge0(&num_domains_to_mark, -1);
1235+
domain->marking_done = 1;
1236+
}
1237+
1238+
if (domain->ephe_info->todo == (value) NULL)
1239+
ephe_todo_list_emptied();
1240+
}
1241+
11921242
struct cycle_callback_params {
11931243
int force_compaction;
11941244
};
@@ -1286,7 +1336,7 @@ static void stw_cycle_all_domains(caml_domain_state* domain, void* args,
12861336
atomic_store_release(&num_domains_to_sweep, num_domains_in_stw);
12871337
atomic_store_release(&num_domains_to_mark, num_domains_in_stw);
12881338

1289-
caml_gc_phase = Phase_sweep_and_mark_main;
1339+
caml_gc_phase = Phase_sweep_main;
12901340
atomic_store(&ephe_cycle_info.num_domains_todo, num_domains_in_stw);
12911341
atomic_store(&ephe_cycle_info.ephe_cycle, 1);
12921342
atomic_store(&ephe_cycle_info.num_domains_done, 0);
@@ -1345,30 +1395,8 @@ static void stw_cycle_all_domains(caml_domain_state* domain, void* args,
13451395
(uintnat)local_stats.large_blocks);
13461396

13471397
domain->sweeping_done = 0;
1348-
1349-
/* Mark roots for new cycle */
13501398
domain->marking_done = 0;
13511399

1352-
CAML_EV_BEGIN(EV_MAJOR_MARK_ROOTS);
1353-
caml_do_roots (&caml_darken, darken_scanning_flags, domain, domain, 0);
1354-
{
1355-
uintnat work_unstarted = WORK_UNSTARTED;
1356-
if(atomic_compare_exchange_strong(&domain_global_roots_started,
1357-
&work_unstarted,
1358-
WORK_STARTED)){
1359-
caml_scan_global_roots(&caml_darken, domain);
1360-
}
1361-
}
1362-
CAML_EV_END(EV_MAJOR_MARK_ROOTS);
1363-
1364-
if (domain->mark_stack->count == 0 &&
1365-
!caml_addrmap_iter_ok(&domain->mark_stack->compressed_stack,
1366-
domain->mark_stack->compressed_stack_iter)
1367-
) {
1368-
atomic_fetch_add_verify_ge0(&num_domains_to_mark, -1);
1369-
domain->marking_done = 1;
1370-
}
1371-
13721400
/* Ephemerons */
13731401
// Adopt orphaned work from domains that were spawned and terminated in
13741402
// the previous cycle.
@@ -1383,8 +1411,6 @@ static void stw_cycle_all_domains(caml_domain_state* domain, void* args,
13831411
domain->ephe_info->cycle = 0;
13841412
domain->ephe_info->cursor.todop = NULL;
13851413
domain->ephe_info->cursor.cycle = 0;
1386-
if (domain->ephe_info->todo == (value) NULL)
1387-
ephe_todo_list_emptied();
13881414

13891415
/* Finalisers */
13901416
domain->final_info->updated_first = 0;
@@ -1539,8 +1565,17 @@ static void major_collection_slice(intnat howmuch,
15391565
if (log_events) CAML_EV_END(EV_MAJOR_SWEEP);
15401566
}
15411567

1568+
if (domain_state->sweeping_done &&
1569+
caml_gc_phase == Phase_sweep_main &&
1570+
get_major_slice_work(mode) > 0 &&
1571+
mode != Slice_opportunistic) {
1572+
start_marking(participant_count, barrier_participants);
1573+
}
1574+
1575+
15421576
mark_again:
1543-
if (!domain_state->marking_done &&
1577+
if (caml_marking_started() &&
1578+
!domain_state->marking_done &&
15441579
get_major_slice_work(mode) > 0) {
15451580
if (log_events) CAML_EV_BEGIN(EV_MAJOR_MARK);
15461581

@@ -1555,7 +1590,7 @@ static void major_collection_slice(intnat howmuch,
15551590
if (log_events) CAML_EV_END(EV_MAJOR_MARK);
15561591
}
15571592

1558-
if (mode != Slice_opportunistic) {
1593+
if (mode != Slice_opportunistic && caml_marking_started()) {
15591594
/* Finalisers */
15601595
if (caml_gc_phase == Phase_mark_final &&
15611596
get_major_slice_work(mode) > 0 &&
@@ -1810,6 +1845,9 @@ void caml_finish_marking (void)
18101845
{
18111846
if (!Caml_state->marking_done) {
18121847
CAML_EV_BEGIN(EV_MAJOR_FINISH_MARKING);
1848+
if (!caml_marking_started()) {
1849+
start_marking(0, NULL);
1850+
}
18131851
caml_empty_mark_stack();
18141852
caml_shrink_mark_stack();
18151853
Caml_state->stat_major_words += Caml_state->allocated_words;
@@ -1930,6 +1968,7 @@ int caml_init_major_gc(caml_domain_state* d) {
19301968
caml_addrmap_iterator(&d->mark_stack->compressed_stack);
19311969

19321970
/* Fresh domains do not need to performing marking or sweeping. */
1971+
/* CR ocaml 5 domains: how does this interact with Phase_sweep_main? */
19331972
d->sweeping_done = 1;
19341973
d->marking_done = 1;
19351974
/* Finalisers. Fresh domains participate in updating finalisers. */

ocaml/runtime/memory.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ Caml_inline void write_barrier(
137137
then this is in a remembered set already */
138138
if (Is_young(old_val)) return;
139139
/* old is a block and in the major heap */
140-
caml_darken(Caml_state, old_val, 0);
140+
if (caml_marking_started())
141+
caml_darken(Caml_state, old_val, 0);
141142
}
142143
/* this update is creating a new link from major to minor, remember it */
143144
if (Is_block_and_young(new_val)) {

ocaml/runtime/minor_gc.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ static void oldify_one (void* st_v, value v, volatile value *p)
297297
{
298298
/* Conflict - fix up what we allocated on the major heap */
299299
*Hp_val(result) = Make_header(1, No_scan_tag,
300-
caml_global_heap_state.MARKED);
300+
caml_allocation_status());
301301
#ifdef DEBUG
302302
Field(result, 0) = Val_long(1);
303303
#endif
@@ -322,7 +322,7 @@ static void oldify_one (void* st_v, value v, volatile value *p)
322322
} else {
323323
/* Conflict - fix up what we allocated on the major heap */
324324
*Hp_val(result) = Make_header(sz, No_scan_tag,
325-
caml_global_heap_state.MARKED);
325+
caml_allocation_status());
326326
#ifdef DEBUG
327327
{
328328
int c;
@@ -344,7 +344,7 @@ static void oldify_one (void* st_v, value v, volatile value *p)
344344
if( !try_update_object_header(v, p, result, 0) ) {
345345
/* Conflict */
346346
*Hp_val(result) = Make_header(sz, No_scan_tag,
347-
caml_global_heap_state.MARKED);
347+
caml_allocation_status());
348348
#ifdef DEBUG
349349
for( i = 0; i < sz ; i++ ) {
350350
Field(result, i) = Val_long(1);
@@ -376,7 +376,7 @@ static void oldify_one (void* st_v, value v, volatile value *p)
376376
goto tail_call;
377377
} else {
378378
*Hp_val(result) = Make_header(1, No_scan_tag,
379-
caml_global_heap_state.MARKED);
379+
caml_allocation_status());
380380
#ifdef DEBUG
381381
Field(result, 0) = Val_long(1);
382382
#endif

ocaml/runtime/shared_heap.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ value* caml_shared_try_alloc(struct caml_heap_state* local, mlsize_t wosize,
474474
p = large_allocate(local, Bsize_wsize(whsize));
475475
if (!p) return 0;
476476
}
477-
colour = caml_global_heap_state.MARKED;
477+
colour = caml_allocation_status();
478478
Hd_hp (p) = Make_header_with_reserved(wosize, tag, colour, reserved);
479479
#ifdef DEBUG
480480
{

ocaml/runtime/weak.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,16 @@ CAMLprim value caml_ephe_create (value len)
5959
caml_invalid_argument ("Weak.create");
6060
res = caml_alloc_shr (size, Abstract_tag);
6161

62-
Ephe_link(res) = domain_state->ephe_info->live;
63-
domain_state->ephe_info->live = res;
62+
/* The new ephemeron needs to be added to:
63+
live, if marking has started, to be marked next cycle
64+
todo, if marking has not started, to be marked this cycle */
65+
if (caml_marking_started()) {
66+
Ephe_link(res) = domain_state->ephe_info->live;
67+
domain_state->ephe_info->live = res;
68+
} else {
69+
Ephe_link(res) = domain_state->ephe_info->todo;
70+
domain_state->ephe_info->todo = res;
71+
}
6472
for (i = CAML_EPHE_DATA_OFFSET; i < size; i++)
6573
Field(res, i) = caml_ephe_none;
6674
/* run memprof callbacks */
@@ -255,7 +263,8 @@ static value ephe_get_field (value e, mlsize_t offset)
255263
if (elt == caml_ephe_none) {
256264
res = Val_none;
257265
} else {
258-
caml_darken (Caml_state, elt, 0);
266+
if (caml_marking_started())
267+
caml_darken (Caml_state, elt, 0);
259268
res = caml_alloc_small (1, Tag_some);
260269
Field(res, 0) = elt;
261270
}
@@ -307,7 +316,8 @@ static void ephe_copy_and_darken(value from, value to)
307316
mlsize_t to_size = Wosize_val(to);
308317
while (i < to_size) {
309318
value field = Field(from, i);
310-
caml_darken (domain_state, field, 0);
319+
if (caml_marking_started())
320+
caml_darken (domain_state, field, 0);
311321
Store_field(to, i, field);
312322
++ i;
313323
}
@@ -469,7 +479,8 @@ CAMLprim value caml_ephe_blit_key (value es, value ofs,
469479
CAMLprim value caml_ephe_blit_data (value es, value ed)
470480
{
471481
ephe_blit_field (es, CAML_EPHE_DATA_OFFSET, ed, CAML_EPHE_DATA_OFFSET, 1);
472-
caml_darken(0, Field(ed, CAML_EPHE_DATA_OFFSET), 0);
482+
if (caml_marking_started())
483+
caml_darken(0, Field(ed, CAML_EPHE_DATA_OFFSET), 0);
473484
/* [ed] may be in [Caml_state->ephe_info->live] list. The data value may be
474485
unmarked. The ephemerons on the live list are not scanned during ephemeron
475486
marking. Hence, unconditionally darken the data value. */
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
lost_event_words: 0, total_sizes: 2000004, total_minors: 15
1+
lost_event_words: 0, total_sizes: 2000004, total_minors: 20

0 commit comments

Comments
 (0)