Avoid deadlock when finalizer lock is held during gc.
authorIan Lance Taylor <ian@gcc.gnu.org>
Fri, 21 Jan 2011 23:33:52 +0000 (23:33 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Fri, 21 Jan 2011 23:33:52 +0000 (23:33 +0000)
From-SVN: r169112

libgo/runtime/go-go.c
libgo/runtime/mfinal.c
libgo/runtime/runtime.h

index 112d52fced82076d2d46dfb918ab42b7dcfe9760..7c5f40de5efeb0e1869419ae0ab74b69fd5aba52 100644 (file)
@@ -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 ();
 }
 
index 5d32721e696a146b5c3f3ca37b77c2d1020a3e29..23c0d7a16636d64e313190266be4106038c513b6 100644 (file)
@@ -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");
+       }
 }
index 3027f0c42d26fcb959dde2a10370da4e7d408139..e43177fef59a7bb9bc2c00a13309dfd807f50267 100644 (file)
@@ -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.  */