Skip to content

Commit 0a4187d

Browse files
committed
Move execution of finalizes out of jl_gc_running = 1.
Rename `jl_in_gc` to `jl_in_finalizer` Allow running GC in finalizers
1 parent 36d3445 commit 0a4187d

File tree

4 files changed

+26
-46
lines changed

4 files changed

+26
-46
lines changed

src/gc.c

Lines changed: 23 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,10 @@ JL_DEFINE_MUTEX(finalizers)
6363
* the safepoint. This is fine since the thread won't be executing any GC
6464
* critical region during that time).
6565
*
66-
* When the GC needs to run the finalizers, it cannot keep the safepoint
67-
* activate since the code in the finalizer might trigger it and falls into
68-
* a dead loop. It also (not required since the lock is recursive) release the
69-
* `finalizers` lock so that other threads can update the finalizers list at
70-
* the same time. Since the safe point is deactivated in this phase and other
71-
* threads might have entered managed state from unmanaged state, when the
72-
* finalizers finish running, the GC thread wait for other threads to enter a
73-
* safe state again before continuing the GC. It is not possible for other
74-
* threads to enter the GC since `jl_gc_running` is still `1` in this phase.
75-
* In the future, it might be better to delay this after the GC is finished so
76-
* that we can wake up other threads and do more useful works as the finalizers
77-
* runs.
66+
* The finalizers are run after the GC finishes in normal mode (the `gc_state`
67+
* when `jl_gc_collect` is called) with `jl_in_finalizer = 1`. (TODO:) When we
68+
* have proper support of GC transition in codegen, we should execute the
69+
* finalizers in unmanaged (GC safe) mode.
7870
*/
7971

8072
// manipulating mark bits
@@ -363,11 +355,6 @@ JL_DLLEXPORT volatile size_t *jl_gc_signal_page = NULL;
363355

364356
static void jl_wait_for_gc(void)
365357
{
366-
assert(!jl_in_gc && "Safepoint triggered in GC");
367-
// In case assertion is off. Make safepoint in GC a segfault instead
368-
// of a infinite loop.
369-
if (jl_in_gc)
370-
return;
371358
while (jl_gc_running) {
372359
jl_cpu_pause(); // yield?
373360
}
@@ -550,10 +537,10 @@ void jl_gc_inhibit_finalizers(int state)
550537
{
551538
// NOTE: currently only called with the codegen lock held, but might need
552539
// more synchronization in the future
553-
if (jl_gc_finalizers_inhibited && !state && !jl_in_gc) {
554-
jl_in_gc = 1;
540+
if (jl_gc_finalizers_inhibited && !state && !jl_in_finalizer) {
541+
jl_in_finalizer = 1;
555542
run_finalizers();
556-
jl_in_gc = 0;
543+
jl_in_finalizer = 0;
557544
}
558545
jl_gc_finalizers_inhibited = state;
559546
}
@@ -2312,7 +2299,7 @@ static void _jl_gc_collect(int full, char *stack_hi)
23122299
int64_t estimate_freed = -1;
23132300

23142301
#if defined(GC_TIME) || defined(GC_FINAL_STATS)
2315-
uint64_t post_time = 0, finalize_time = 0;
2302+
uint64_t post_time = 0;
23162303
#endif
23172304
if (mark_sp == 0 || sweeping) {
23182305
#if defined(GC_TIME) || defined(GC_FINAL_STATS)
@@ -2424,28 +2411,16 @@ static void _jl_gc_collect(int full, char *stack_hi)
24242411
allocd_bytes_since_sweep = 0;
24252412
jl_gc_total_freed_bytes += freed_bytes;
24262413
freed_bytes = 0;
2427-
2428-
#if defined(GC_FINAL_STATS) || defined(GC_TIME)
2429-
finalize_time = jl_hrtime();
2430-
#endif
2431-
if (!jl_gc_finalizers_inhibited && to_finalize.len) {
2432-
jl_gc_signal_end();
2433-
run_finalizers();
2434-
jl_gc_signal_begin();
2435-
}
2436-
#if defined(GC_FINAL_STATS) || defined(GC_TIME)
2437-
finalize_time = jl_hrtime() - finalize_time;
2438-
#endif
24392414
}
24402415
#if defined(GC_FINAL_STATS) || defined(GC_TIME)
24412416
uint64_t sweep_pause = jl_hrtime() - sweep_t0;
24422417
#endif
24432418
#ifdef GC_FINAL_STATS
2444-
total_sweep_time += sweep_pause - finalize_time - post_time;
2445-
total_fin_time += finalize_time + post_time;
2419+
total_sweep_time += sweep_pause - post_time;
2420+
total_fin_time += + post_time;
24462421
#endif
24472422
#ifdef GC_TIME
2448-
jl_printf(JL_STDOUT, "GC sweep pause %.2f ms live %ld kB (freed %d kB EST %d kB [error %d] = %d%% of allocd %d kB b/r %ld/%ld) (%.2f ms in post_mark, %.2f ms in %d fin) (marked in %d inc) mask %d | next in %d kB\n", NS2MS(sweep_pause), live_bytes/1024, SAVE2/1024, estimate_freed/1024, (SAVE2 - estimate_freed), pct, SAVE3/1024, bonus/1024, SAVE/1024, NS2MS(post_time), NS2MS(finalize_time), n_finalized, inc_count, sweep_mask, -allocd_bytes/1024);
2423+
jl_printf(JL_STDOUT, "GC sweep pause %.2f ms live %ld kB (freed %d kB EST %d kB [error %d] = %d%% of allocd %d kB b/r %ld/%ld) (%.2f ms in post_mark) (marked in %d inc) mask %d | next in %d kB\n", NS2MS(sweep_pause), live_bytes/1024, SAVE2/1024, estimate_freed/1024, (SAVE2 - estimate_freed), pct, SAVE3/1024, bonus/1024, SAVE/1024, NS2MS(post_time), inc_count, sweep_mask, -allocd_bytes/1024);
24492424
#endif
24502425
}
24512426
n_pause++;
@@ -2469,7 +2444,7 @@ static void _jl_gc_collect(int full, char *stack_hi)
24692444

24702445
JL_DLLEXPORT void jl_gc_collect(int full)
24712446
{
2472-
if (jl_gc_disable_counter || jl_in_gc)
2447+
if (jl_gc_disable_counter)
24732448
return;
24742449
char *stack_hi = (char*)gc_get_stack_ptr();
24752450
gc_debug_print();
@@ -2487,26 +2462,31 @@ JL_DLLEXPORT void jl_gc_collect(int full)
24872462
jl_wait_for_gc();
24882463
jl_gc_state_set(old_state, JL_GC_STATE_WAITING);
24892464
#else
2490-
// For single thread, jl_in_gc is always true when jl_gc_running is
2491-
// true so this should never happen.
2465+
// For single thread, GC should not call itself (in finalizers) before
2466+
// setting jl_gc_running to false so this should never happen.
24922467
assert(0 && "GC synchronization failure");
24932468
#endif
24942469
return;
24952470
}
24962471
jl_gc_signal_begin();
24972472

2498-
jl_in_gc = 1;
24992473
if (!jl_gc_disable_counter)
25002474
_jl_gc_collect(full, stack_hi);
2501-
jl_in_gc = 0;
25022475

25032476
// Need to reset the page protection before resetting the flag since
2504-
// the thread will trigger a segfault immediately after returning from the
2505-
// signal handler.
2477+
// the thread will trigger a segfault immediately after returning from
2478+
// the signal handler.
25062479
jl_gc_signal_end();
25072480
jl_gc_running = 0;
25082481
JL_SIGATOMIC_END();
25092482
jl_gc_state_set(old_state, JL_GC_STATE_WAITING);
2483+
2484+
if (!jl_gc_finalizers_inhibited) {
2485+
int8_t was_in_finalizer = jl_in_finalizer;
2486+
jl_in_finalizer = 1;
2487+
run_finalizers();
2488+
jl_in_finalizer = was_in_finalizer;
2489+
}
25102490
}
25112491

25122492
// allocator entry points

src/julia.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1458,7 +1458,7 @@ typedef struct _jl_tls_states_t {
14581458
// gc_state = 2 means the thread is running unmanaged code that can be
14591459
// execute at the same time with the GC.
14601460
volatile int8_t gc_state;
1461-
volatile int8_t in_gc;
1461+
volatile int8_t in_finalizer;
14621462
int8_t disable_gc;
14631463
struct _jl_thread_heap_t *heap;
14641464
jl_task_t *volatile current_task;

src/julia_internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ extern unsigned sig_stack_size;
2121

2222
JL_DLLEXPORT extern int jl_lineno;
2323
JL_DLLEXPORT extern const char *jl_filename;
24-
#define jl_in_gc (jl_get_ptls_states()->in_gc)
24+
#define jl_in_finalizer (jl_get_ptls_states()->in_finalizer)
2525

2626
STATIC_INLINE jl_value_t *newobj(jl_value_t *type, size_t nfields)
2727
{

src/task.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ JL_DLLEXPORT jl_value_t *jl_switchto(jl_task_t *t, jl_value_t *arg)
367367
jl_throw(t->exception);
368368
return t->result;
369369
}
370-
if (jl_in_gc)
370+
if (jl_in_finalizer)
371371
jl_error("task switch not allowed from inside gc finalizer");
372372
int8_t gc_state = jl_gc_unsafe_enter();
373373
jl_task_arg_in_transit = arg;

0 commit comments

Comments
 (0)