Skip to content

Commit 398cd5b

Browse files
committed
backport improvements on GC scheduler shutdown
1 parent b1e181c commit 398cd5b

File tree

3 files changed

+111
-73
lines changed

3 files changed

+111
-73
lines changed

src/gc.c

Lines changed: 108 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2856,13 +2856,16 @@ JL_EXTENSION NOINLINE void gc_mark_loop_serial(jl_ptls_t ptls)
28562856
gc_drain_own_chunkqueue(ptls, &ptls->mark_queue);
28572857
}
28582858

2859-
void gc_mark_and_steal(jl_ptls_t ptls)
2859+
int gc_mark_and_steal(jl_ptls_t ptls)
28602860
{
28612861
jl_gc_markqueue_t *mq = &ptls->mark_queue;
28622862
jl_gc_markqueue_t *mq_master = NULL;
28632863
int master_tid = jl_atomic_load(&gc_master_tid);
2864-
if (master_tid != -1)
2865-
mq_master = &gc_all_tls_states[master_tid]->mark_queue;
2864+
if (master_tid == -1) {
2865+
return 0;
2866+
}
2867+
mq_master = &gc_all_tls_states[master_tid]->mark_queue;
2868+
int marked = 0;
28662869
void *new_obj;
28672870
jl_gc_chunk_t c;
28682871
pop : {
@@ -2878,6 +2881,7 @@ void gc_mark_and_steal(jl_ptls_t ptls)
28782881
goto steal;
28792882
}
28802883
mark : {
2884+
marked = 1;
28812885
gc_mark_outrefs(ptls, mq, new_obj, 0);
28822886
goto pop;
28832887
}
@@ -2906,12 +2910,10 @@ void gc_mark_and_steal(jl_ptls_t ptls)
29062910
}
29072911
}
29082912
// Try to steal chunk from master thread
2909-
if (mq_master != NULL) {
2910-
c = gc_chunkqueue_steal_from(mq_master);
2911-
if (c.cid != GC_empty_chunk) {
2912-
gc_mark_chunk(ptls, mq, &c);
2913-
goto pop;
2914-
}
2913+
c = gc_chunkqueue_steal_from(mq_master);
2914+
if (c.cid != GC_empty_chunk) {
2915+
gc_mark_chunk(ptls, mq, &c);
2916+
goto pop;
29152917
}
29162918
// Try to steal pointer from random GC thread
29172919
for (int i = 0; i < 4 * jl_n_gcthreads; i++) {
@@ -2928,37 +2930,110 @@ void gc_mark_and_steal(jl_ptls_t ptls)
29282930
if (new_obj != NULL)
29292931
goto mark;
29302932
}
2931-
// Try to steal pointer from master thread
2932-
if (mq_master != NULL) {
2933-
new_obj = gc_ptr_queue_steal_from(mq_master);
2934-
if (new_obj != NULL)
2935-
goto mark;
2933+
new_obj = gc_ptr_queue_steal_from(mq_master);
2934+
if (new_obj != NULL)
2935+
goto mark;
2936+
}
2937+
return marked;
2938+
}
2939+
2940+
int gc_some_work_left_in_queue(jl_ptls_t ptls) JL_NOTSAFEPOINT
2941+
{
2942+
if (jl_atomic_load_relaxed(&ptls->mark_queue.ptr_queue.bottom) !=
2943+
jl_atomic_load_relaxed(&ptls->mark_queue.ptr_queue.top)) {
2944+
return 1;
2945+
}
2946+
if (jl_atomic_load_relaxed(&ptls->mark_queue.chunk_queue.bottom) !=
2947+
jl_atomic_load_relaxed(&ptls->mark_queue.chunk_queue.top)) {
2948+
return 1;
2949+
}
2950+
return 0;
2951+
}
2952+
2953+
int gc_some_work_left(void) JL_NOTSAFEPOINT
2954+
{
2955+
for (int i = gc_first_tid; i < gc_first_tid + jl_n_gcthreads; i++) {
2956+
jl_ptls_t ptls2 = gc_all_tls_states[i];
2957+
if (gc_some_work_left_in_queue(ptls2)) {
2958+
return 1;
2959+
}
2960+
}
2961+
int master_tid = jl_atomic_load(&gc_master_tid);
2962+
if (master_tid != -1) {
2963+
jl_ptls_t ptls2 = gc_all_tls_states[master_tid];
2964+
if (gc_some_work_left_in_queue(ptls2)) {
2965+
return 1;
2966+
}
2967+
}
2968+
return 0;
2969+
}
2970+
2971+
void gc_mark_loop_master_init(jl_ptls_t ptls)
2972+
{
2973+
jl_atomic_store(&gc_master_tid, ptls->tid);
2974+
// Wake threads up and try to do some work
2975+
uv_mutex_lock(&gc_threads_lock);
2976+
jl_atomic_fetch_add(&gc_n_threads_marking, 1);
2977+
uv_cond_broadcast(&gc_threads_cond);
2978+
uv_mutex_unlock(&gc_threads_lock);
2979+
gc_mark_and_steal(ptls);
2980+
jl_atomic_fetch_add(&gc_n_threads_marking, -1);
2981+
}
2982+
2983+
#define GC_MIN_BACKOFF_LG2 (4)
2984+
#define GC_MAX_BACKOFF_LG2 (12)
2985+
2986+
void gc_mark_loop_parallel(jl_ptls_t ptls)
2987+
{
2988+
int b = GC_MIN_BACKOFF_LG2;
2989+
while (jl_atomic_load(&gc_n_threads_marking) > 0) {
2990+
if (gc_some_work_left()) {
2991+
// Try to become a thief while other threads are marking
2992+
jl_atomic_fetch_add(&gc_n_threads_marking, 1);
2993+
int marked = gc_mark_and_steal(ptls);
2994+
jl_atomic_fetch_add(&gc_n_threads_marking, -1);
2995+
if (marked) {
2996+
b = GC_MIN_BACKOFF_LG2;
2997+
}
29362998
}
2999+
uint64_t c0 = cycleclock();
3000+
do {
3001+
jl_cpu_pause();
3002+
} while (cycleclock() - c0 < (1 << b));
29373003
}
29383004
}
29393005

2940-
void gc_mark_loop_parallel(jl_ptls_t ptls, int master)
3006+
void gc_mark_loop_master(jl_ptls_t ptls)
29413007
{
2942-
int backoff = GC_BACKOFF_MIN;
2943-
if (master) {
2944-
jl_atomic_store(&gc_master_tid, ptls->tid);
2945-
// Wake threads up and try to do some work
3008+
gc_mark_loop_master_init(ptls);
3009+
gc_mark_loop_parallel(ptls);
3010+
}
3011+
3012+
STATIC_INLINE int gc_may_mark(void) JL_NOTSAFEPOINT
3013+
{
3014+
return jl_atomic_load(&gc_n_threads_marking) > 0;
3015+
}
3016+
3017+
STATIC_INLINE int gc_may_sweep(jl_ptls_t ptls) JL_NOTSAFEPOINT
3018+
{
3019+
return jl_atomic_load(&ptls->gc_sweeps_requested) > 0;
3020+
}
3021+
3022+
void gc_worker_loop(jl_ptls_t ptls)
3023+
{
3024+
while (1) {
29463025
uv_mutex_lock(&gc_threads_lock);
2947-
jl_atomic_fetch_add(&gc_n_threads_marking, 1);
2948-
uv_cond_broadcast(&gc_threads_cond);
3026+
while (!gc_may_mark() && !gc_may_sweep(ptls)) {
3027+
uv_cond_wait(&gc_threads_cond, &gc_threads_lock);
3028+
}
29493029
uv_mutex_unlock(&gc_threads_lock);
2950-
gc_mark_and_steal(ptls);
2951-
jl_atomic_fetch_add(&gc_n_threads_marking, -1);
2952-
}
2953-
while (jl_atomic_load(&gc_n_threads_marking) > 0) {
2954-
// Try to become a thief while other threads are marking
2955-
jl_atomic_fetch_add(&gc_n_threads_marking, 1);
2956-
if (jl_atomic_load(&gc_master_tid) != -1) {
2957-
gc_mark_and_steal(ptls);
3030+
if (gc_may_mark()) {
3031+
gc_mark_loop_parallel(ptls);
3032+
}
3033+
if (gc_may_sweep(ptls)) { // not an else!
3034+
gc_sweep_pool_parallel();
3035+
jl_atomic_fetch_add(&ptls->gc_sweeps_requested, -1);
29583036
}
2959-
jl_atomic_fetch_add(&gc_n_threads_marking, -1);
2960-
// Failed to steal
2961-
gc_backoff(&backoff);
29623037
}
29633038
}
29643039

@@ -2968,7 +3043,7 @@ void gc_mark_loop(jl_ptls_t ptls)
29683043
gc_mark_loop_serial(ptls);
29693044
}
29703045
else {
2971-
gc_mark_loop_parallel(ptls, 1);
3046+
gc_mark_loop_master(ptls);
29723047
}
29733048
}
29743049

src/gc.h

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -189,19 +189,6 @@ extern jl_gc_page_stack_t global_page_pool_lazily_freed;
189189
extern jl_gc_page_stack_t global_page_pool_clean;
190190
extern jl_gc_page_stack_t global_page_pool_freed;
191191

192-
#define GC_BACKOFF_MIN 4
193-
#define GC_BACKOFF_MAX 12
194-
195-
STATIC_INLINE void gc_backoff(int *i) JL_NOTSAFEPOINT
196-
{
197-
if (*i < GC_BACKOFF_MAX) {
198-
(*i)++;
199-
}
200-
for (int j = 0; j < (1 << *i); j++) {
201-
jl_cpu_pause();
202-
}
203-
}
204-
205192
// Lock-free stack implementation taken
206193
// from Herlihy's "The Art of Multiprocessor Programming"
207194
// XXX: this is not a general-purpose lock-free stack. We can
@@ -452,8 +439,7 @@ void gc_mark_finlist_(jl_gc_markqueue_t *mq, jl_value_t **fl_begin, jl_value_t *
452439
void gc_mark_finlist(jl_gc_markqueue_t *mq, arraylist_t *list, size_t start) JL_NOTSAFEPOINT;
453440
void gc_mark_loop_serial_(jl_ptls_t ptls, jl_gc_markqueue_t *mq);
454441
void gc_mark_loop_serial(jl_ptls_t ptls);
455-
void gc_mark_loop_parallel(jl_ptls_t ptls, int master);
456-
void gc_sweep_pool_parallel(void);
442+
void gc_worker_loop(jl_ptls_t ptls);
457443
void gc_free_pages(void);
458444
void sweep_stack_pools(void);
459445
void jl_gc_debug_init(void);

src/partr.c

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -108,17 +108,7 @@ void jl_init_threadinginfra(void)
108108

109109
void JL_NORETURN jl_finish_task(jl_task_t *t);
110110

111-
static inline int may_mark(void) JL_NOTSAFEPOINT
112-
{
113-
return (jl_atomic_load(&gc_n_threads_marking) > 0);
114-
}
115-
116-
static inline int may_sweep(jl_ptls_t ptls) JL_NOTSAFEPOINT
117-
{
118-
return (jl_atomic_load(&ptls->gc_sweeps_requested) > 0);
119-
}
120-
121-
// gc thread function
111+
// parallel gc thread function
122112
void jl_gc_threadfun(void *arg)
123113
{
124114
jl_threadarg_t *targ = (jl_threadarg_t*)arg;
@@ -133,20 +123,7 @@ void jl_gc_threadfun(void *arg)
133123
// free the thread argument here
134124
free(targ);
135125

136-
while (1) {
137-
uv_mutex_lock(&gc_threads_lock);
138-
while (!may_mark() && !may_sweep(ptls)) {
139-
uv_cond_wait(&gc_threads_cond, &gc_threads_lock);
140-
}
141-
uv_mutex_unlock(&gc_threads_lock);
142-
if (may_mark()) {
143-
gc_mark_loop_parallel(ptls, 0);
144-
}
145-
if (may_sweep(ptls)) { // not an else!
146-
gc_sweep_pool_parallel();
147-
jl_atomic_fetch_add(&ptls->gc_sweeps_requested, -1);
148-
}
149-
}
126+
gc_worker_loop(ptls);
150127
}
151128

152129
// thread function: used by all mutator threads except the main thread

0 commit comments

Comments
 (0)