Skip to content

Commit 742f4a5

Browse files
committed
Reland "gc: avoid cpu stalls when starting"
This reverts commit 4801b6c and adds the safepoints needed to catch the unsafe->safe transition also.
1 parent ec0027f commit 742f4a5

File tree

5 files changed

+39
-27
lines changed

5 files changed

+39
-27
lines changed

src/gc.c

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -190,22 +190,7 @@ NOINLINE uintptr_t gc_get_stack_ptr(void)
190190

191191
#define should_timeout() 0
192192

193-
static void jl_gc_wait_for_the_world(void)
194-
{
195-
if (jl_n_threads > 1)
196-
jl_wake_libuv();
197-
for (int i = 0; i < jl_n_threads; i++) {
198-
jl_ptls_t ptls2 = jl_all_tls_states[i];
199-
// This acquire load pairs with the release stores
200-
// in the signal handler of safepoint so we are sure that
201-
// all the stores on those threads are visible.
202-
// We're currently also using atomic store release in mutator threads
203-
// (in jl_gc_state_set), but we may want to use signals to flush the
204-
// memory operations on those threads lazily instead.
205-
while (!jl_atomic_load_relaxed(&ptls2->gc_state) || !jl_atomic_load_acquire(&ptls2->gc_state))
206-
jl_cpu_pause(); // yield?
207-
}
208-
}
193+
void jl_gc_wait_for_the_world(void);
209194

210195
// malloc wrappers, aligned allocation
211196

src/julia_threads.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,9 +340,9 @@ STATIC_INLINE int8_t jl_gc_state_set(jl_ptls_t ptls, int8_t state,
340340
int8_t old_state)
341341
{
342342
jl_atomic_store_release(&ptls->gc_state, state);
343-
// A safe point is required if we transition from GC-safe region to
344-
// non GC-safe region.
345-
if (old_state && !state)
343+
if (state == JL_GC_STATE_SAFE && old_state == 0)
344+
jl_gc_safepoint_(ptls);
345+
if (state == 0 && old_state == JL_GC_STATE_SAFE)
346346
jl_gc_safepoint_(ptls);
347347
return old_state;
348348
}

src/rtutils.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,7 @@ JL_DLLEXPORT void jl_eh_restore_state(jl_handler_t *eh)
276276
ct->ptls->defer_signal = eh->defer_signal;
277277
if (old_gc_state != eh->gc_state) {
278278
jl_atomic_store_release(&ct->ptls->gc_state, eh->gc_state);
279-
if (old_gc_state) {
280-
jl_gc_safepoint_(ct->ptls);
281-
}
279+
jl_gc_safepoint_(ct->ptls);
282280
}
283281
if (old_defer_signal && !eh->defer_signal) {
284282
jl_sigint_safepoint(ct->ptls);

src/safepoint.c

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ uint8_t jl_safepoint_enable_cnt[3] = {0, 0, 0};
4343
// load/store so that threads waiting for the GC doesn't have to also
4444
// fight on the safepoint lock...
4545
uv_mutex_t safepoint_lock;
46-
uv_cond_t safepoint_cond;
46+
uv_cond_t safepoint_cond_begin;
47+
uv_cond_t safepoint_cond_end;
4748

4849
static void jl_safepoint_enable(int idx) JL_NOTSAFEPOINT
4950
{
@@ -88,7 +89,8 @@ static void jl_safepoint_disable(int idx) JL_NOTSAFEPOINT
8889
void jl_safepoint_init(void)
8990
{
9091
uv_mutex_init(&safepoint_lock);
91-
uv_cond_init(&safepoint_cond);
92+
uv_cond_init(&safepoint_cond_begin);
93+
uv_cond_init(&safepoint_cond_end);
9294
// jl_page_size isn't available yet.
9395
size_t pgsz = jl_getpagesize();
9496
#ifdef _OS_WINDOWS_
@@ -109,6 +111,31 @@ void jl_safepoint_init(void)
109111
jl_safepoint_pages = addr;
110112
}
111113

114+
void jl_gc_wait_for_the_world(void)
115+
{
116+
assert(jl_n_threads);
117+
if (jl_n_threads > 1)
118+
jl_wake_libuv();
119+
for (int i = 0; i < jl_n_threads; i++) {
120+
jl_ptls_t ptls2 = jl_all_tls_states[i];
121+
// This acquire load pairs with the release stores
122+
// in the signal handler of safepoint so we are sure that
123+
// all the stores on those threads are visible.
124+
// We're currently also using atomic store release in mutator threads
125+
// (in jl_gc_state_set), but we may want to use signals to flush the
126+
// memory operations on those threads lazily instead.
127+
while (!jl_atomic_load_relaxed(&ptls2->gc_state) || !jl_atomic_load_acquire(&ptls2->gc_state)) {
128+
// Use system mutexes rather than spin locking to minimize wasted CPU time
129+
// while we wait for other threads reach a safepoint.
130+
// This is particularly important when run under rr.
131+
uv_mutex_lock(&safepoint_lock);
132+
if (!jl_atomic_load_relaxed(&ptls2->gc_state))
133+
uv_cond_wait(&safepoint_cond_begin, &safepoint_lock);
134+
uv_mutex_unlock(&safepoint_lock);
135+
}
136+
}
137+
}
138+
112139
int jl_safepoint_start_gc(void)
113140
{
114141
if (jl_n_threads == 1) {
@@ -153,13 +180,14 @@ void jl_safepoint_end_gc(void)
153180
jl_mach_gc_end();
154181
# endif
155182
uv_mutex_unlock(&safepoint_lock);
156-
uv_cond_broadcast(&safepoint_cond);
183+
uv_cond_broadcast(&safepoint_cond_end);
157184
}
158185

159186
void jl_safepoint_wait_gc(void)
160187
{
161188
// The thread should have set this is already
162189
assert(jl_atomic_load_relaxed(&jl_current_task->ptls->gc_state) != 0);
190+
uv_cond_broadcast(&safepoint_cond_begin);
163191
// Use normal volatile load in the loop for speed until GC finishes.
164192
// Then use an acquire load to make sure the GC result is visible on this thread.
165193
while (jl_atomic_load_relaxed(&jl_gc_running) || jl_atomic_load_acquire(&jl_gc_running)) {
@@ -168,7 +196,7 @@ void jl_safepoint_wait_gc(void)
168196
// This is particularly important when run under rr.
169197
uv_mutex_lock(&safepoint_lock);
170198
if (jl_atomic_load_relaxed(&jl_gc_running))
171-
uv_cond_wait(&safepoint_cond, &safepoint_lock);
199+
uv_cond_wait(&safepoint_cond_end, &safepoint_lock);
172200
uv_mutex_unlock(&safepoint_lock);
173201
}
174202
}

src/signals-mach.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ static void attach_exception_port(thread_port_t thread, int segv_only);
4242
// low 16 bits are the thread id, the next 8 bits are the original gc_state
4343
static arraylist_t suspended_threads;
4444
extern uv_mutex_t safepoint_lock;
45-
extern uv_cond_t safepoint_cond;
45+
extern uv_cond_t safepoint_cond_begin;
4646
void jl_mach_gc_end(void)
4747
{
4848
// Requires the safepoint lock to be held
@@ -85,6 +85,7 @@ static int jl_mach_gc_wait(jl_ptls_t ptls2,
8585
arraylist_push(&suspended_threads, (void*)item);
8686
thread_suspend(thread);
8787
uv_mutex_unlock(&safepoint_lock);
88+
uv_cond_broadcast(&safepoint_cond_begin);
8889
return 1;
8990
}
9091

0 commit comments

Comments
 (0)