--- /home/cpdev/src/classpath/java/lang/Runtime.java	2005-07-02 21:03:34.000000000 +0000
+++ java/lang/Runtime.java	2005-06-30 05:34:38.000000000 +0000
@@ -39,7 +39,6 @@
 package java.lang;
 
 import gnu.classpath.SystemProperties;
-import gnu.classpath.VMStackWalker;
 
 import java.io.File;
 import java.io.IOException;
@@ -66,6 +65,11 @@
    */
   private final String[] libpath;
 
+  static
+  {
+    init();
+  }
+
   /**
    * The thread that started the exit sequence. Access to this field must
    * be thread-safe; lock on libpath to avoid deadlock with user code.
@@ -81,6 +85,9 @@
    */
   private Set shutdownHooks;
 
+  /** True if we should finalize on exit.  */
+  private boolean finalizeOnExit;
+
   /**
    * The one and only runtime instance.
    */
@@ -94,40 +101,10 @@
     if (current != null)
       throw new InternalError("Attempt to recreate Runtime");
     
-    // If used by underlying VM this contains the directories where Classpath's own
-    // native libraries are located.
-    String bootPath = SystemProperties.getProperty("gnu.classpath.boot.library.path", "");
-    
-    // If properly set by the user this contains the directories where the application's
-    // native libraries are located. On operating systems where a LD_LIBRARY_PATH environment
-    // variable is available a VM should preset java.library.path with value of this
-    // variable.
-    String path = SystemProperties.getProperty("java.library.path", ".");
-    String pathSep = SystemProperties.getProperty("path.separator", ":");
-    String fileSep = SystemProperties.getProperty("file.separator", "/");
-
-    StringTokenizer t1 = new StringTokenizer(bootPath, pathSep);
-    StringTokenizer t2 = new StringTokenizer(path, pathSep);
-    libpath = new String[t1.countTokens() + t2.countTokens()];
-    
-    int i = 0;
-    while(t1.hasMoreTokens()) {
-      String prefix = t1.nextToken();
-      if (! prefix.endsWith(fileSep))
-        prefix += fileSep;
-      
-      libpath[i] = prefix;
-      i++;
-    }
-    
-    while(t2.hasMoreTokens()) {
-      String prefix = t2.nextToken();
-      if (! prefix.endsWith(fileSep))
-        prefix += fileSep;
-  
-      libpath[i] = prefix;
-      i++;
-    }
+    // We don't use libpath in the libgcj implementation.  We still
+    // set it to something to allow the various synchronizations to
+    // work.
+    libpath = new String[0];
   }
 
   /**
@@ -169,56 +146,6 @@
     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.
       {
@@ -250,7 +177,7 @@
             // 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 (true)
+            while (! shutdownHooks.isEmpty())
               {
                 Thread[] hooks;
                 synchronized (libpath)
@@ -258,27 +185,19 @@
                     hooks = new Thread[shutdownHooks.size()];
                     shutdownHooks.toArray(hooks);
                   }
-                if (hooks.length == 0)
-                  break;
-                for (int i = 0; i < hooks.length; i++)
-                  {
-                    try
-                      {
-                        synchronized (libpath)
-                          {
-                            if (!shutdownHooks.contains(hooks[i]))
-                              continue;
-                          }
-                        hooks[i].join();
-                        synchronized (libpath)
-                          {
-                            shutdownHooks.remove(hooks[i]);
-                          }
-                      }
-                    catch (InterruptedException x)
+                for (int i = hooks.length; --i >= 0; )
+                  if (! hooks[i].isAlive())
+                    synchronized (libpath)
                       {
-                        // continue waiting on the next thread
+                        shutdownHooks.remove(hooks[i]);
                       }
+                try
+                  {
+                    Thread.sleep(1); // Give other threads a chance.
+                  }
+                catch (InterruptedException e)
+                  {
+                    // Ignore, the next loop just starts sooner.
                   }
               }
             synchronized (libpath)
@@ -286,11 +205,34 @@
                 shutdownHooks = null;
               }
           }
-	// Run finalization on all finalizable objects (even if they are
-	// still reachable).
-        VMRuntime.runFinalizationForExit();
+        // 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();
       }
-    return first;
+    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).
+        }
   }
 
   /**
@@ -339,10 +281,7 @@
         if (exitSequence != null)
           throw new IllegalStateException("The Virtual Machine is exiting. It is not possible anymore to add any hooks");
         if (shutdownHooks == null)
-          {
-            VMRuntime.enableShutdownHooks();
-            shutdownHooks = new HashSet(); // Lazy initialization.
-          }
+          shutdownHooks = new HashSet(); // Lazy initialization.
         if (! shutdownHooks.add(hook))
           throw new IllegalArgumentException(hook.toString() + " had already been inserted");
       }
@@ -396,7 +335,7 @@
     SecurityManager sm = SecurityManager.current; // Be thread-safe!
     if (sm != null)
       sm.checkExit(status);
-    VMRuntime.exit(status);
+    exitInternal(status);
   }
 
   /**
@@ -420,7 +359,7 @@
     SecurityManager sm = SecurityManager.current; // Be thread-safe!
     if (sm != null)
       sm.checkExit(0);
-    VMRuntime.runFinalizersOnExit(finalizeOnExit);
+    current.finalizeOnExit = finalizeOnExit;
   }
 
   /**
@@ -550,7 +489,7 @@
     SecurityManager sm = SecurityManager.current; // Be thread-safe!
     if (sm != null)
       sm.checkExec(cmd[0]);
-    return VMRuntime.exec(cmd, env, dir);
+    return execInternal(cmd, env, dir);
   }
 
   /**
@@ -560,20 +499,14 @@
    *
    * @return the number of processors available, at least 1
    */
-  public int availableProcessors()
-  {
-    return VMRuntime.availableProcessors();
-  }
+  public native int availableProcessors();
 
   /**
    * Find out how much memory is still free for allocating Objects on the heap.
    *
    * @return the number of bytes of free memory for more Objects
    */
-  public long freeMemory()
-  {
-    return VMRuntime.freeMemory();
-  }
+  public native long freeMemory();
 
   /**
    * Find out how much memory total is available on the heap for allocating
@@ -581,10 +514,7 @@
    *
    * @return the total number of bytes of memory for Objects
    */
-  public long totalMemory()
-  {
-    return VMRuntime.totalMemory();
-  }
+  public native long totalMemory();
 
   /**
    * Returns the maximum amount of memory the virtual machine can attempt to
@@ -594,10 +524,7 @@
    * @return the maximum number of bytes the virtual machine will attempt
    *         to allocate
    */
-  public long maxMemory()
-  {
-    return VMRuntime.maxMemory();
-  }
+  public native long maxMemory();
 
   /**
    * Run the garbage collector. This method is more of a suggestion than
@@ -605,10 +532,7 @@
    * have "done its best" by the time it returns. Notice that garbage
    * collection takes place even without calling this method.
    */
-  public void gc()
-  {
-    VMRuntime.gc();
-  }
+  public native void gc();
 
   /**
    * Run finalization on all Objects that are waiting to be finalized. Again,
@@ -617,10 +541,7 @@
    *
    * @see #finalize()
    */
-  public void runFinalization()
-  {
-    VMRuntime.runFinalization();
-  }
+  public native void runFinalization();
 
   /**
    * Tell the VM to trace every bytecode instruction that executes (print out
@@ -629,10 +550,7 @@
    *
    * @param on whether to turn instruction tracing on
    */
-  public void traceInstructions(boolean on)
-  {
-    VMRuntime.traceInstructions(on);
-  }
+  public native void traceInstructions(boolean on);
 
   /**
    * Tell the VM to trace every method call that executes (print out a trace
@@ -641,10 +559,7 @@
    *
    * @param on whether to turn method tracing on
    */
-  public void traceMethodCalls(boolean on)
-  {
-    VMRuntime.traceMethodCalls(on);
-  }
+  public native void traceMethodCalls(boolean on);
 
   /**
    * Load a native library using the system-dependent filename. This is similar
@@ -652,50 +567,16 @@
    * before the final ".so" if the VM was invoked by the name "java_g". There
    * may be a security check, of <code>checkLink</code>.
    *
-   * <p>
-   * The library is loaded using the class loader associated with the
-   * class associated with the invoking method.
-   *
    * @param filename the file to load
    * @throws SecurityException if permission is denied
    * @throws UnsatisfiedLinkError if the library is not found
    */
   public void load(String filename)
   {
-    load(filename, VMStackWalker.getCallingClassLoader());
-  }
-
-  /**
-   * Same as <code>load(String)</code> but using the given loader.
-   *
-   * @param filename the file to load
-   * @param loader class loader, or <code>null</code> for the boot loader
-   * @throws SecurityException if permission is denied
-   * @throws UnsatisfiedLinkError if the library is not found
-   */
-  void load(String filename, ClassLoader loader)
-  {
     SecurityManager sm = SecurityManager.current; // Be thread-safe!
     if (sm != null)
       sm.checkLink(filename);
-    if (loadLib(filename, loader) == 0)
-      throw new UnsatisfiedLinkError("Could not load library " + filename);
-  }
-
-  /**
-   * Do a security check on the filename and then load the native library.
-   *
-   * @param filename the file to load
-   * @param loader class loader, or <code>null</code> for the boot loader
-   * @return 0 on failure, nonzero on success
-   * @throws SecurityException if file read permission is denied
-   */
-  private static int loadLib(String filename, ClassLoader loader)
-  {
-    SecurityManager sm = SecurityManager.current; // Be thread-safe!
-    if (sm != null)
-      sm.checkRead(filename);
-    return VMRuntime.nativeLoad(filename, loader);
+    _load(filename, false);
   }
 
   /**
@@ -710,17 +591,6 @@
    * <code>System.mapLibraryName(libname)</code>. There may be a security
    * check, of <code>checkLink</code>.
    *
-   * <p>Note: Besides <code>java.library.path</code> a VM may chose to search
-   * for native libraries in a path that is specified by the
-   * <code>gnu.classpath.boot.library.path</code> system property. However
-   * this is for internal usage or development of GNU Classpath only.
-   * <b>A Java application must not load a non-system library by changing
-   * this property otherwise it will break compatibility.</b></p>
-   *
-   * <p>
-   * The library is loaded using the class loader associated with the
-   * class associated with the invoking method.
-   *
    * @param libname the library to load
    *
    * @throws SecurityException if permission is denied
@@ -731,37 +601,12 @@
    */
   public void loadLibrary(String libname)
   {
-    loadLibrary(libname, VMStackWalker.getCallingClassLoader());
-  }
-
-  /**
-   * Same as <code>loadLibrary(String)</code> but using the given loader.
-   *
-   * @param libname the library to load
-   * @param loader class loader, or <code>null</code> for the boot loader
-   * @throws SecurityException if permission is denied
-   * @throws UnsatisfiedLinkError if the library is not found
-   */
-  void loadLibrary(String libname, ClassLoader loader)
-  {
+    // This is different from the Classpath implementation, but I
+    // believe it is more correct.
     SecurityManager sm = SecurityManager.current; // Be thread-safe!
     if (sm != null)
       sm.checkLink(libname);
-    String filename;
-    if (loader != null && (filename = loader.findLibrary(libname)) != null)
-      {
-	if (loadLib(filename, loader) != 0)
-	  return;
-      }
-    else
-      {
-	filename = VMRuntime.mapLibraryName(libname);
-	for (int i = 0; i < libpath.length; i++)
-	  if (loadLib(libpath[i] + filename, loader) != 0)
-	    return;
-      }
-    throw new UnsatisfiedLinkError("Native library `" + libname
-      + "' not found (as file `" + filename + "') in gnu.classpath.boot.library.path and java.library.path");
+    _load(libname, true);
   }
 
   /**
@@ -772,7 +617,6 @@
    * @return the localized stream
    * @deprecated <code>InputStreamReader</code> is the preferred way to read
    *             local encodings
-   * @XXX This implementation does not localize, yet.
    */
   public InputStream getLocalizedInputStream(InputStream in)
   {
@@ -787,10 +631,66 @@
    * @return the localized stream
    * @deprecated <code>OutputStreamWriter</code> is the preferred way to write
    *             local encodings
-   * @XXX This implementation does not localize, yet.
    */
   public OutputStream getLocalizedOutputStream(OutputStream out)
   {
     return out;
   }
+
+  /**
+   * Native method that actually shuts down the virtual machine.
+   *
+   * @param status the status to end the process with
+   */
+  native void exitInternal(int status);
+
+  /**
+   * Load a file. If it has already been loaded, do nothing. The name has
+   * already been mapped to a true filename.
+   *
+   * @param filename the file to load
+   * @param do_search True if we should search the load path for the file
+   */
+  native void _load(String filename, boolean do_search);
+
+  /**
+   *This is a helper function for the ClassLoader which can load
+   * compiled libraries.  Returns true if library (which is just the
+   * base name -- path searching is done by this function) was loaded,
+   * false otherwise.
+   */
+  native boolean loadLibraryInternal(String libname);
+
+  /**
+   * A helper for Runtime static initializer which does some internal native
+   * initialization.
+   */
+  private static native void init ();
+
+  /**
+   * Map a system-independent "short name" to the full file name, and append
+   * it to the path.
+   * XXX This method is being replaced by System.mapLibraryName.
+   *
+   * @param pathname the path
+   * @param libname the short version of the library name
+   * @return the full filename
+   */
+  static native String nativeGetLibname(String pathname, String libname);
+
+  /**
+   * Execute a process. The command line has already been tokenized, and
+   * the environment should contain name=value mappings. If directory is null,
+   * use the current working directory; otherwise start the process in that
+   * directory.
+   *
+   * @param cmd the non-null command tokens
+   * @param env the non-null environment setup
+   * @param dir the directory to use, may be null
+   * @return the newly created process
+   * @throws NullPointerException if cmd or env have null elements
+   * @throws IOException if the exec fails
+   */
+  native Process execInternal(String[] cmd, String[] env, File dir)
+    throws IOException;
 } // class Runtime
