From 3d16e887967eb96476482a70da3b23d8234a1a4c Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 21 Jan 2011 23:33:52 +0000 Subject: [PATCH] Avoid deadlock when finalizer lock is held during gc. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@169112 138bc75d-0d04-0410-961f-82ee72b054a4 --- libgo/runtime/go-go.c | 9 +++++++++ libgo/runtime/mfinal.c | 32 ++++++++++++++++++++++++++++++-- libgo/runtime/runtime.h | 2 ++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/libgo/runtime/go-go.c b/libgo/runtime/go-go.c index 112d52fced8..7c5f40de5ef 100644 --- a/libgo/runtime/go-go.c +++ b/libgo/runtime/go-go.c @@ -315,6 +315,15 @@ gc_stop_handler (int sig __attribute__ ((unused))) return; } + if (__sync_bool_compare_and_swap (&pm->holds_finlock, 1, 1)) + { + /* Similarly, we can't interrupt the thread while it holds the + finalizer lock. Otherwise we can get into a deadlock when + mark calls runtime_walkfintab. */ + __sync_bool_compare_and_swap (&pm->gcing_for_finlock, 0, 1); + return; + } + stop_for_gc (); } diff --git a/libgo/runtime/mfinal.c b/libgo/runtime/mfinal.c index 5d32721e696..23c0d7a1663 100644 --- a/libgo/runtime/mfinal.c +++ b/libgo/runtime/mfinal.c @@ -106,9 +106,13 @@ runtime_addfinalizer(void *p, void (*f)(void*), const struct __go_func_type *ft) e->ft = ft; } + if(!__sync_bool_compare_and_swap(&m->holds_finlock, 0, 1)) + runtime_throw("finalizer deadlock"); + runtime_lock(&finlock); if(!runtime_mlookup(p, &base, nil, nil, &ref) || p != base) { runtime_unlock(&finlock); + __sync_bool_compare_and_swap(&m->holds_finlock, 1, 0); runtime_throw("addfinalizer on invalid pointer"); } if(f == nil) { @@ -116,12 +120,12 @@ runtime_addfinalizer(void *p, void (*f)(void*), const struct __go_func_type *ft) lookfintab(&fintab, p, 1); *ref &= ~RefHasFinalizer; } - runtime_unlock(&finlock); - return; + goto unlock; } if(*ref & RefHasFinalizer) { runtime_unlock(&finlock); + __sync_bool_compare_and_swap(&m->holds_finlock, 1, 0); runtime_throw("double finalizer"); } *ref |= RefHasFinalizer; @@ -156,7 +160,14 @@ runtime_addfinalizer(void *p, void (*f)(void*), const struct __go_func_type *ft) } addfintab(&fintab, p, e); + unlock: runtime_unlock(&finlock); + + __sync_bool_compare_and_swap(&m->holds_finlock, 1, 0); + + if(__sync_bool_compare_and_swap(&m->gcing_for_finlock, 1, 0)) { + __go_run_goroutine_gc(200); + } } // get finalizer; if del, delete finalizer. @@ -166,9 +177,18 @@ runtime_getfinalizer(void *p, bool del) { Finalizer *f; + if(!__sync_bool_compare_and_swap(&m->holds_finlock, 0, 1)) + runtime_throw("finalizer deadlock"); + runtime_lock(&finlock); f = lookfintab(&fintab, p, del); runtime_unlock(&finlock); + + __sync_bool_compare_and_swap(&m->holds_finlock, 1, 0); + if(__sync_bool_compare_and_swap(&m->gcing_for_finlock, 1, 0)) { + __go_run_goroutine_gc(201); + } + return f; } @@ -178,6 +198,9 @@ runtime_walkfintab(void (*fn)(void*), void (*scan)(byte *, int64)) void **key; void **ekey; + if(!__sync_bool_compare_and_swap(&m->holds_finlock, 0, 1)) + runtime_throw("finalizer deadlock"); + scan((byte*)&fintab, sizeof fintab); runtime_lock(&finlock); key = fintab.key; @@ -186,4 +209,9 @@ runtime_walkfintab(void (*fn)(void*), void (*scan)(byte *, int64)) if(*key != nil && *key != ((void*)-1)) fn(*key); runtime_unlock(&finlock); + + __sync_bool_compare_and_swap(&m->holds_finlock, 1, 0); + if(__sync_bool_compare_and_swap(&m->gcing_for_finlock, 1, 0)) { + runtime_throw("walkfintab not called from gc"); + } } diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h index 3027f0c42d2..e43177fef59 100644 --- a/libgo/runtime/runtime.h +++ b/libgo/runtime/runtime.h @@ -97,6 +97,8 @@ struct M int32 locks; int32 nomemprof; int32 gcing_for_prof; + int32 holds_finlock; + int32 gcing_for_finlock; MCache *mcache; /* For the list of all threads. */ -- 2.11.0