diff --git a/libjava/ChangeLog b/libjava/ChangeLog
index a2cc01e4ad71488795e2f7dbf220ac0f92427123..4cbb9e738ddf773f3c51fc1e62b29624421069aa 100644
--- a/libjava/ChangeLog
+++ b/libjava/ChangeLog
@@ -1,3 +1,15 @@
+2006-01-05  Tom Tromey  <tromey@redhat.com>
+
+	* java/lang/natThread.cc (finish_): Don't clear 'group'.
+	* sources.am, Makefile.in: Rebuilt.
+	* java/lang/Runtime.java (exit): Merged with Classpath.
+	(runShutdownHooks): New method from Classpath.
+	* java/io/File.java (deleteOnExit): Use DeleteFileHelper, not
+	FileDeleter.
+	* gnu/gcj/runtime/FileDeleter.java: Removed.
+	* java/lang/natRuntime.cc (runFinalizationForExit): New method.
+	(exitInternal): Don't run finalizers or delete files.
+
 2006-01-05  Tom Tromey  <tromey@redhat.com>
 
 	* java/lang/natPosixProcess.cc (reap): Ignore unknown children.
diff --git a/libjava/Makefile.in b/libjava/Makefile.in
index cec8b3875a8707fef82b03f358b879cdb3ce7d59..66e5fc5c3c77b49040d61a78992b462de9da0dec 100644
--- a/libjava/Makefile.in
+++ b/libjava/Makefile.in
@@ -1168,7 +1168,6 @@ gnu_gcj_io_header_files = $(patsubst %.java,%.h,$(gnu_gcj_io_source_files))
 gnu_gcj_runtime_source_files = \
 gnu/gcj/runtime/BootClassLoader.java \
 gnu/gcj/runtime/ExtensionClassLoader.java \
-gnu/gcj/runtime/FileDeleter.java \
 gnu/gcj/runtime/FinalizerThread.java \
 gnu/gcj/runtime/HelperClassLoader.java \
 gnu/gcj/runtime/JNIWeakRef.java \
diff --git a/libjava/gnu/gcj/runtime/FileDeleter.java b/libjava/gnu/gcj/runtime/FileDeleter.java
deleted file mode 100644
index d5f99d04c4712e457b8b0b57d36a454b62e888dd..0000000000000000000000000000000000000000
--- a/libjava/gnu/gcj/runtime/FileDeleter.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/* Copyright (C) 2000  Free Software Foundation
-
-   This file is part of libgcj.
-
-This software is copyrighted work licensed under the terms of the
-Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
-details.  */
-
-package gnu.gcj.runtime;
-
-import java.io.*;
-import java.util.*;
-
-public final class FileDeleter
-{
-  public synchronized static void add (File f)
-  {
-    if (deleteOnExitStack == null)
-      deleteOnExitStack = new Stack ();
-
-    deleteOnExitStack.push (f);
-  }
-
-  // Helper method called by java.lang.Runtime.exit() to perform
-  // pending deletions.
-  public synchronized static void deleteOnExitNow ()
-  {
-    if (deleteOnExitStack != null)
-      while (!deleteOnExitStack.empty ())
-	((File)(deleteOnExitStack.pop ())).delete ();
-  }
-
-  // A stack of files to delete upon normal termination.
-  private static Stack deleteOnExitStack;
-}
diff --git a/libjava/gnu/java/lang/natMainThread.cc b/libjava/gnu/java/lang/natMainThread.cc
index 7e8e4223dfece1e6be735ad5b1419f309524ce13..95626eb3a71a40e422ba108d8dae8c7a241e5feb 100644
--- a/libjava/gnu/java/lang/natMainThread.cc
+++ b/libjava/gnu/java/lang/natMainThread.cc
@@ -1,6 +1,6 @@
 // natMainThread.cc - Implementation of MainThread native methods.
 
-/* Copyright (C) 1998, 1999, 2000, 2001, 2003  Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000, 2001, 2003, 2006  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -15,8 +15,11 @@ details.  */
 
 #include <gcj/cni.h>
 #include <jvm.h>
+#include <java-threads.h>
 
 #include <gnu/java/lang/MainThread.h>
+#include <java/lang/Runtime.h>
+#include <java/lang/ThreadGroup.h>
 
 typedef void main_func (jobject);
 
@@ -45,4 +48,15 @@ gnu::java::lang::MainThread::call_main (void)
 
   main_func *real_main = (main_func *) meth->ncode;
   (*real_main) (args);
+
+  // Note that we do thread cleanup here.  We have to do this here and
+  // not in _Jv_RunMain; if we do if after the main thread has exited,
+  // our ThreadGroup will be null, and if Runtime.exit tries to create
+  // a new Thread (which it does when running shutdown hooks), it will
+  // eventually NPE due to this.
+  _Jv_ThreadWait ();
+
+  int status = (int) ::java::lang::ThreadGroup::had_uncaught_exception;
+  ::java::lang::Runtime *runtime = ::java::lang::Runtime::getRuntime ();
+  runtime->exit (status);
 }
diff --git a/libjava/java/io/File.java b/libjava/java/io/File.java
index e3d59cb7d2465a03a0fe9169cc447feb5cbf04f7..1b02b601aaf6a48c81ebe01ba12957bd544d8656 100644
--- a/libjava/java/io/File.java
+++ b/libjava/java/io/File.java
@@ -1,5 +1,5 @@
 /* File.java -- Class representing a file on disk
-   Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005
+   Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
@@ -44,7 +44,6 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import gnu.classpath.Configuration;
-import gnu.gcj.runtime.FileDeleter;
 
 /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
  * "The Java Language Specification", ISBN 0-201-63451-1
@@ -1375,7 +1374,7 @@ public class File implements Serializable, Comparable
     if (sm != null)
       sm.checkDelete (getName());
 
-    FileDeleter.add (this);
+    DeleteFileHelper.add(this);
   }
 
   private void writeObject(ObjectOutputStream oos) throws IOException
diff --git a/libjava/java/lang/Runtime.java b/libjava/java/lang/Runtime.java
index a30a44b2c229b16ebe1585629ea70ad2274ea93a..519b4ac8c5ef7a081099441abf19706dbf5ab784 100644
--- a/libjava/java/lang/Runtime.java
+++ b/libjava/java/lang/Runtime.java
@@ -1,5 +1,5 @@
 /* Runtime.java -- access to the VM process
-   Copyright (C) 1998, 2002, 2003, 2004, 2005 Free Software Foundation
+   Copyright (C) 1998, 2002, 2003, 2004, 2005, 2006 Free Software Foundation
 
 This file is part of GNU Classpath.
 
@@ -146,6 +146,56 @@ public class Runtime
     SecurityManager sm = SecurityManager.current; // Be thread-safe!
     if (sm != null)
       sm.checkExit(status);
+
+    if (runShutdownHooks())
+      halt(status);
+
+    // Someone else already called runShutdownHooks().
+    // Make sure we are not/no longer in the shutdownHooks set.
+    // And wait till the thread that is calling runShutdownHooks() finishes.
+    synchronized (libpath)
+      {
+        if (shutdownHooks != null)
+          {
+            shutdownHooks.remove(Thread.currentThread());
+            // Interrupt the exit sequence thread, in case it was waiting
+            // inside a join on our thread.
+            exitSequence.interrupt();
+            // Shutdown hooks are still running, so we clear status to
+	    // make sure we don't halt.
+	    status = 0;
+          }
+      }
+
+    // If exit() is called again after the shutdown hooks have run, but
+    // while finalization for exit is going on and the status is non-zero
+    // we halt immediately.
+    if (status != 0)
+      halt(status);
+
+    while (true)
+      try
+        {
+          exitSequence.join();
+        }
+      catch (InterruptedException e)
+        {
+          // Ignore, we've suspended indefinitely to let all shutdown
+          // hooks complete, and to let any non-zero exits through, because
+          // this is a duplicate call to exit(0).
+        }
+  }
+
+  /**
+   * On first invocation, run all the shutdown hooks and return true.
+   * Any subsequent invocations will simply return false.
+   * Note that it is package accessible so that VMRuntime can call it
+   * when VM exit is not triggered by a call to Runtime.exit().
+   * 
+   * @return was the current thread the first one to call this method?
+   */
+  boolean runShutdownHooks()
+  {
     boolean first = false;
     synchronized (libpath) // Synch on libpath, not this, to avoid deadlock.
       {
@@ -177,7 +227,7 @@ public class Runtime
             // itself from the set, then waits indefinitely on the
             // exitSequence thread. Once the set is empty, set it to null to
             // signal all finalizer threads that halt may be called.
-            while (! shutdownHooks.isEmpty())
+            while (true)
               {
                 Thread[] hooks;
                 synchronized (libpath)
@@ -185,19 +235,27 @@ public class Runtime
                     hooks = new Thread[shutdownHooks.size()];
                     shutdownHooks.toArray(hooks);
                   }
-                for (int i = hooks.length; --i >= 0; )
-                  if (! hooks[i].isAlive())
-                    synchronized (libpath)
+                if (hooks.length == 0)
+                  break;
+                for (int i = 0; i < hooks.length; i++)
+                  {
+                    try
                       {
-                        shutdownHooks.remove(hooks[i]);
+                        synchronized (libpath)
+                          {
+                            if (!shutdownHooks.contains(hooks[i]))
+                              continue;
+                          }
+                        hooks[i].join();
+                        synchronized (libpath)
+                          {
+                            shutdownHooks.remove(hooks[i]);
+                          }
+                      }
+                    catch (InterruptedException x)
+                      {
+                        // continue waiting on the next thread
                       }
-                try
-                  {
-                    Thread.sleep(1); // Give other threads a chance.
-                  }
-                catch (InterruptedException e)
-                  {
-                    // Ignore, the next loop just starts sooner.
                   }
               }
             synchronized (libpath)
@@ -205,34 +263,11 @@ public class Runtime
                 shutdownHooks = null;
               }
           }
-        // XXX Right now, it is the VM that knows whether runFinalizersOnExit
-        // is true; so the VM must look at exitSequence to decide whether
-        // this should be run on every object.
-        runFinalization();
+	// Run finalization on all finalizable objects (even if they are
+	// still reachable).
+        runFinalizationForExit();
       }
-    else
-      synchronized (libpath)
-        {
-          if (shutdownHooks != null)
-            {
-              shutdownHooks.remove(Thread.currentThread());
-              status = 0; // Change status to enter indefinite wait.
-            }
-        }
-    
-    if (first || status > 0)
-      halt(status);
-    while (true)
-      try
-        {
-          exitSequence.join();
-        }
-      catch (InterruptedException e)
-        {
-          // Ignore, we've suspended indefinitely to let all shutdown
-          // hooks complete, and to let any non-zero exits through, because
-          // this is a duplicate call to exit(0).
-        }
+    return first;
   }
 
   /**
@@ -667,6 +702,11 @@ public class Runtime
    */
   private static native void init ();
 
+  /**
+   * Run finalizers when exiting.
+   */
+  private native void runFinalizationForExit();
+
   /**
    * Map a system-independent "short name" to the full file name, and append
    * it to the path.
diff --git a/libjava/java/lang/natRuntime.cc b/libjava/java/lang/natRuntime.cc
index 300dc573247accb2131f6cf32dc116fef9ef76a7..5cca1b072a3d157263b0001f889bd041c2ba39ac 100644
--- a/libjava/java/lang/natRuntime.cc
+++ b/libjava/java/lang/natRuntime.cc
@@ -1,6 +1,6 @@
 // natRuntime.cc - Implementation of native side of Runtime class.
 
-/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005  Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -21,7 +21,6 @@ details.  */
 #include <java/lang/Runtime.h>
 #include <java/lang/UnknownError.h>
 #include <java/lang/UnsatisfiedLinkError.h>
-#include <gnu/gcj/runtime/FileDeleter.h>
 #include <gnu/gcj/runtime/FinalizerThread.h>
 #include <java/io/File.h>
 #include <java/util/TimeZone.h>
@@ -90,6 +89,13 @@ _Jv_FindSymbolInExecutable (const char *)
 
 
 
+void
+java::lang::Runtime::runFinalizationForExit ()
+{
+  if (finalizeOnExit)
+    _Jv_RunAllFinalizers ();
+}
+
 void
 java::lang::Runtime::exitInternal (jint status)
 {
@@ -97,12 +103,6 @@ java::lang::Runtime::exitInternal (jint status)
   if (status < 0 || status > 255)
     status = 255;
 
-  if (finalizeOnExit)
-    _Jv_RunAllFinalizers ();
-
-  // Delete all files registered with File.deleteOnExit()
-  gnu::gcj::runtime::FileDeleter::deleteOnExitNow ();
-
   ::exit (status);
 }
 
diff --git a/libjava/java/lang/natThread.cc b/libjava/java/lang/natThread.cc
index 80cdae324ff90472279911339b3f6d67e49e08d0..da9dcbabfd0755de63af0117ae990c2deefb6055 100644
--- a/libjava/java/lang/natThread.cc
+++ b/libjava/java/lang/natThread.cc
@@ -1,6 +1,6 @@
 // natThread.cc - Native part of Thread class.
 
-/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005  Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005, 2006  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -213,8 +213,6 @@ java::lang::Thread::finish_ ()
     }
 #endif
 
-  group = NULL;
-
   // If a method cache was created, free it.
   _Jv_FreeMethodCache();
 
diff --git a/libjava/prims.cc b/libjava/prims.cc
index 490d2b1c127b7a39ebe4faf0607f06cd2396f838..09bca0b6f71f00ad8e737aaae4b60b1817501c26 100644
--- a/libjava/prims.cc
+++ b/libjava/prims.cc
@@ -1,6 +1,6 @@
 // prims.cc - Code for core of runtime environment.
 
-/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005  Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -1387,10 +1387,10 @@ _Jv_RunMain (JvVMInitArgs *vm_args, jclass klass, const char *name, int argc,
 
   _Jv_AttachCurrentThread (main_thread);
   _Jv_ThreadRun (main_thread);
-  _Jv_ThreadWait ();
 
-  int status = (int) java::lang::ThreadGroup::had_uncaught_exception;
-  runtime->exit (status);
+  // If we got here then something went wrong, as MainThread is not
+  // supposed to terminate.
+  ::exit (1);
 }
 
 void
diff --git a/libjava/sources.am b/libjava/sources.am
index d976154598be65e03074fa4198354867cc70492b..043621ad632e81e4f5d6baa0146c29b42081b58e 100644
--- a/libjava/sources.am
+++ b/libjava/sources.am
@@ -555,7 +555,6 @@ gnu/gcj/io.list: $(gnu_gcj_io_source_files)
 gnu_gcj_runtime_source_files = \
 gnu/gcj/runtime/BootClassLoader.java \
 gnu/gcj/runtime/ExtensionClassLoader.java \
-gnu/gcj/runtime/FileDeleter.java \
 gnu/gcj/runtime/FinalizerThread.java \
 gnu/gcj/runtime/HelperClassLoader.java \
 gnu/gcj/runtime/JNIWeakRef.java \