Skip to content

Commit d365a92

Browse files
committed
Add an acquire fence after young_limit triggers an allocation failure
The output has been disassembled (gcc -O2, arch=x86-64) to confirm that `CAMLunlikely` works as intended and produces equivalent code as before.
1 parent c08f324 commit d365a92

3 files changed

Lines changed: 21 additions & 12 deletions

File tree

runtime/caml/domain.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,29 @@ extern "C" {
2323

2424
#ifdef CAML_INTERNALS
2525

26+
#include "camlatomic.h"
2627
#include "config.h"
2728
#include "mlvalues.h"
2829
#include "domain_state.h"
2930
#include "platform.h"
3031

3132
/* is the minor heap full or an external interrupt has been triggered */
32-
#define Caml_check_gc_interrupt(dom_st) \
33-
(CAMLalloc_point_here, \
34-
CAMLunlikely( \
35-
(uintnat)(dom_st)->young_ptr < \
36-
atomic_load_explicit(&((dom_st)->young_limit), memory_order_relaxed)))
33+
Caml_inline int caml_check_gc_interrupt(caml_domain_state * dom_st)
34+
{
35+
CAMLalloc_point_here;
36+
uintnat young_limit =
37+
atomic_load_explicit(&dom_st->young_limit, memory_order_relaxed);
38+
if ((uintnat)dom_st->young_ptr < young_limit) {
39+
/* Synchronise for the case when [young_limit] was used to interrupt
40+
us. */
41+
atomic_thread_fence(memory_order_acquire);
42+
return 1;
43+
}
44+
return 0;
45+
}
46+
47+
#define Caml_check_gc_interrupt(dom_st) \
48+
(CAMLunlikely(caml_check_gc_interrupt(dom_st)))
3749

3850
asize_t caml_norm_minor_heap_size (intnat);
3951
int caml_reallocate_minor_heap(asize_t);

runtime/signals.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,6 @@ static caml_plat_mutex signal_install_mutex = CAML_PLAT_MUTEX_INITIALIZER;
4545
int caml_check_pending_signals(void)
4646
{
4747
int i;
48-
/* [MM] This fence compensates for the fact that Caml_check_gc_interrupt
49-
reads young_limit with a relaxed load. It is possible in theory to
50-
see young_limit updated without caml_pending_signals being set
51-
and then resetting young_limit after the check. This would delay
52-
processing the pending signal until young_limit is updated again.
53-
There may be nicer ways to address this scenario. */
54-
atomic_thread_fence(memory_order_acquire);
5548
for (i = 0; i < NSIG_WORDS; i++) {
5649
if (atomic_load_explicit(&caml_pending_signals[i], memory_order_relaxed))
5750
return 1;

runtime/signals_nat.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ void caml_garbage_collection(void)
5454
Pop_frame_pointer(sp);
5555
retaddr = *(uintnat*)sp;
5656

57+
/* Synchronise for the case when [young_limit] was used to interrupt
58+
us. */
59+
atomic_thread_fence(memory_order_acquire);
60+
5761
{ /* Find the frame descriptor for the current allocation */
5862
uintnat h = Hash_retaddr(retaddr, fds.mask);
5963
while (1) {

0 commit comments

Comments
 (0)