summaryrefslogtreecommitdiff
path: root/vm
diff options
context:
space:
mode:
authorEric Blake <ebb9@byu.net>2002-03-06 19:44:44 +0000
committerEric Blake <ebb9@byu.net>2002-03-06 19:44:44 +0000
commit17c3f9b21a1503325cafd893332decf6c003a916 (patch)
treeb76fe7e6ed37726c5687bb3d2eacd444561c61e5 /vm
parente2c44b08d79dbbbb6ddd4d1544a90d4d1cc5f8d2 (diff)
downloadclasspath-17c3f9b21a1503325cafd893332decf6c003a916.tar.gz
2002-03-06 Eric Blake <ebb9@email.byu.edu>
* java/lang/RuntimePermission.java: Improve Javadoc. * java/lang/SecurityManager.java: Improve Javadoc and formatting. * java/lang/System.java (setIn, setOut, setErr): Add required security check. (defaultProperties): Add a default, to allow clean resetting of properties back to the VM startup state. (setProperties): Correctly reset properties to default state. * native/jni/java-lang/java_lang_System.c: Update method signatures for changing I/O. * include/java_lang_System.h: Ditto. * vm/reference/java/lang/Runtime.java: Add shutdown hook capability, as well as updating the exec calls. * vm/reference/java/lang/VMSecurityManager.java: Improve Javadoc. * java/util/PropertyPermission.java: Fix implication bugs. * java/util/PropertyPermissionCollection.java: Ditto.
Diffstat (limited to 'vm')
-rwxr-xr-xvm/reference/java/lang/Runtime.java290
-rw-r--r--vm/reference/java/lang/VMSecurityManager.java7
2 files changed, 197 insertions, 100 deletions
diff --git a/vm/reference/java/lang/Runtime.java b/vm/reference/java/lang/Runtime.java
index e125447cc..f58fba092 100755
--- a/vm/reference/java/lang/Runtime.java
+++ b/vm/reference/java/lang/Runtime.java
@@ -37,8 +37,14 @@ exception statement from your version. */
package java.lang;
-import java.util.*;
-import java.io.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.StringTokenizer;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.HashSet;
/**
* Runtime represents the Virtual Machine.
@@ -55,50 +61,50 @@ public class Runtime
private static final Runtime current = new Runtime();
/**
- * The library path, to search when loading libraries.
+ * The library path, to search when loading libraries. We can also safely use
+ * this as a lock for synchronization.
*/
- private String[] libpath;
+ private final String[] libpath;
/**
* The current security manager. This is located here instead of in
* Runtime, to avoid security problems, as well as bootstrap issues.
+ * Make sure to access it in a thread-safe manner.
*/
private static SecurityManager securityManager;
/**
+ * The thread that started the exit sequence. Access to this field must
+ * be thread-safe; lock on libpath to avoid deadlock with user code.
+ * <code>runFinalization()</code> may want to look at this to see if ALL
+ * finalizers should be run, because the virtual machine is about to halt.
+ */
+ private Thread exitSequence;
+
+ /**
+ * All shutdown hooks. This is initialized lazily, and set to null once all
+ * shutdown hooks have run. Access to this field must be thread-safe; lock
+ * on libpath to avoid deadlock with user code.
+ */
+ private Set shutdownHooks;
+
+ /**
* Not instantiable by a user, this should only create one instance.
*/
private Runtime()
{
if (current != null)
throw new InternalError("Attempt to recreate Runtime");
- String path = getLibraryPath();
+ // XXX Does this need special privileges?
+ String path = System.getProperty("java.library.path");
if (path == null)
libpath = new String[0];
else
{
- // XXX Use StringTokenizer to make this nicer.
- int numColons = 0;
- int pathLength = path.length();
- for (int i = 0; i < pathLength; i++)
- // XXX Use path.separator property.
- if (path.charAt(i) == ':')
- numColons++;
- libpath = new String[numColons + 1];
- int current = 0;
- int libpathIndex = 0;
- while (true)
- {
- int next = path.indexOf(File.pathSeparatorChar, current);
- if (next == -1)
- {
- libpath[libpathIndex] = path.substring(current);
- break;
- }
- libpath[libpathIndex] = path.substring(current, next);
- libpathIndex++;
- current = next + 1;
- }
+ StringTokenizer t = new StringTokenizer(path, File.pathSeparator);
+ libpath = new String[t.countTokens()];
+ for (int i = 0; i < libpath.length; i++)
+ libpath[i] = t.nextToken();
}
}
@@ -119,7 +125,7 @@ public class Runtime
* a non-zero status code indicates an abnormal exit. Of course, there is a
* security check, <code>checkExit(status)</code>.
*
- * <p>First, all shutdown hooks are run, in unspecified order, and possibly
+ * <p>First, all shutdown hooks are run, in unspecified order, and
* concurrently. Next, if finalization on exit has been enabled, all pending
* finalizers are run. Finally, the system calls <code>halt</code>.
*
@@ -141,9 +147,87 @@ public class Runtime
SecurityManager sm = securityManager; // Be thread-safe!
if (sm != null)
sm.checkExit(status);
- //XXX Check if we are already finalizing.
- //XXX Don't use exitInternal. Instead, run shutdown hooks, then call halt.
- exitInternal(status);
+ boolean first = false;
+ synchronized (libpath) // Synch on libpath, not this, to avoid deadlock.
+ {
+ if (exitSequence == null)
+ {
+ first = true;
+ exitSequence = Thread.currentThread();
+ Iterator i = shutdownHooks.iterator();
+ while (i.hasNext()) // Start all shutdown hooks.
+ try
+ {
+ ((Thread) i.next()).start();
+ }
+ catch (IllegalThreadStateException e)
+ {
+ i.remove();
+ }
+ }
+ }
+ if (first)
+ {
+ // Check progress of all shutdown hooks. As a hook completes, remove
+ // it from the set. If a hook calls exit, it removes 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())
+ {
+ Thread[] hooks;
+ synchronized (libpath)
+ {
+ hooks = new Thread[shutdownHooks.size()];
+ shutdownHooks.toArray(hooks);
+ }
+ for (int i = hooks.length; --i >= 0; )
+ if (! hooks[i].isAlive())
+ synchronized (libpath)
+ {
+ shutdownHooks.remove(hooks[i]);
+ }
+ try
+ {
+ exitSequence.sleep(1); // Give other threads a chance.
+ }
+ catch (InterruptedException e)
+ {
+ // Ignore, the next loop just starts sooner.
+ }
+ }
+ synchronized (libpath)
+ {
+ 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();
+ }
+ 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).
+ }
}
/**
@@ -179,12 +263,24 @@ public class Runtime
* @see #removeShutdownHook(Thread)
* @see #exit(int)
* @see #halt(int)
- * @XXX Add this method.
+ */
public void addShutdownHook(Thread hook)
{
- //XXX Implement me!
+ SecurityManager sm = securityManager; // Be thread-safe!
+ if (sm != null)
+ sm.checkPermission(new RuntimePermission("shutdownHooks"));
+ if (hook.isAlive())
+ throw new IllegalArgumentException();
+ synchronized (libpath)
+ {
+ if (exitSequence != null)
+ throw new IllegalStateException();
+ if (shutdownHooks == null)
+ shutdownHooks = new HashSet(); // Lazy initialization.
+ if (! shutdownHooks.add(hook))
+ throw new IllegalArgumentException();
+ }
}
- */
/**
* De-register a shutdown hook. As when you registered it, there is a
@@ -201,13 +297,21 @@ public class Runtime
* @see #addShutdownHook(Thread)
* @see #exit(int)
* @see #halt(int)
- * @XXX Add this method.
+ */
public boolean removeShutdownHook(Thread hook)
{
- // Implement me!
+ SecurityManager sm = securityManager; // Be thread-safe!
+ if (sm != null)
+ sm.checkPermission(new RuntimePermission("shutdownHooks"));
+ synchronized (libpath)
+ {
+ if (exitSequence != null)
+ throw new IllegalStateException();
+ if (shutdownHooks != null)
+ return shutdownHooks.remove(hook);
+ }
return false;
}
- */
/**
* Forcibly terminate the virtual machine. This call never returns. It is
@@ -220,7 +324,7 @@ public class Runtime
* @since 1.3
* @see #exit(int)
* @see #addShutdownHook(Thread)
- * XXX Add this method.
+ */
public void halt(int status)
{
SecurityManager sm = securityManager; // Be thread-safe!
@@ -228,14 +332,6 @@ public class Runtime
sm.checkExit(status);
exitInternal(status);
}
- */
-
- /**
- * Native method that actually shuts down the virtual machine.
- *
- * @param status the status to end the process with
- */
- native void exitInternal(int status);
/**
* Tell the VM to run the finalize() method on every single Object before
@@ -275,8 +371,7 @@ public class Runtime
*/
public Process exec(String cmdline) throws IOException
{
- //XXX Use this: return exec(cmdline, null, null);
- return exec(cmdline, null);
+ return exec(cmdline, null, null);
}
/**
@@ -295,14 +390,7 @@ public class Runtime
*/
public Process exec(String cmdline, String[] env) throws IOException
{
- //XXX Use this: return exec(cmdline, env, null);
- StringTokenizer t = new StringTokenizer(cmdline);
- Vector v = new Vector();
- while (t.hasMoreTokens())
- v.addElement(t.nextElement());
- String[] cmd = new String[v.size()];
- v.copyInto(cmd);
- return exec(cmd, env);
+ return exec(cmdline, env, null);
}
/**
@@ -323,7 +411,7 @@ public class Runtime
* @throws NullPointerException if cmdline is null, or env has null entries
* @throws IndexOutOfBoundsException if cmdline is ""
* @since 1.3
- * @XXX Add this method.
+ */
public Process exec(String cmdline, String[] env, File dir)
throws IOException
{
@@ -333,7 +421,6 @@ public class Runtime
cmd[i] = t.nextToken();
return exec(cmd, env, dir);
}
- */
/**
* Create a new subprocess with the specified command line, already
@@ -349,8 +436,7 @@ public class Runtime
*/
public Process exec(String[] cmd) throws IOException
{
- //XXX Use this: return exec(cmd, null, null);
- return exec(cmd, null);
+ return exec(cmd, null, null);
}
/**
@@ -371,11 +457,7 @@ public class Runtime
*/
public Process exec(String[] cmd, String[] env) throws IOException
{
- //XXX Use this: return exec(cmd, env, null);
- SecurityManager sm = securityManager; // Be thread-safe!
- if (sm != null)
- sm.checkExec(cmd[0]);
- return execInternal(cmd, env);
+ return exec(cmd, env, null);
}
/**
@@ -395,7 +477,8 @@ public class Runtime
* entries
* @throws IndexOutOfBoundsException if cmd is length 0
* @since 1.3
- * @XXX Add this method.
+ * @XXX Ignores dir, for now
+ */
public Process exec(String[] cmd, String[] env, File dir)
throws IOException
{
@@ -404,9 +487,9 @@ public class Runtime
sm.checkExec(cmd[0]);
if (env == null)
env = new String[0];
- return execInternal(cmd, env, dir);
+ //XXX Should be: return execInternal(cmd, env, dir);
+ return execInternal(cmd, env);
}
- */
/**
* Returns the number of available processors currently available to the
@@ -414,9 +497,8 @@ public class Runtime
* program want to poll this to determine maximal resource usage.
*
* @return the number of processors available, at least 1
- * @XXX Add this method
- public native int availableProcessors();
*/
+ public native int availableProcessors();
/**
* Find out how much memory is still free for allocating Objects on the heap.
@@ -440,9 +522,8 @@ public class Runtime
*
* @return the maximum number of bytes the virtual machine will attempt
* to allocate
- * @XXX Add this method.
- public native long maxMemory();
*/
+ public native long maxMemory();
/**
* Run the garbage collector. This method is more of a suggestion than
@@ -501,33 +582,45 @@ public class Runtime
/**
* Load a native library using a system-independent "short name" for the
* library. It will be transformed to a correct filename in a
- * system-dependent manner, via <code>System.mapLibraryName</code> (for
- * example, in Windows, "mylib" will be turned into "mylib.dll"), and then
- * passed to load(filename). There may be a security check, of
- * <code>checkLink</code>.
+ * system-dependent manner (for example, in Windows, "mylib" will be turned
+ * into "mylib.dll"). This is done as follows: if the context that called
+ * load has a ClassLoader cl, then <code>cl.findLibrary(libpath)</code> is
+ * used to convert the name. If that result was null, or there was no class
+ * loader, this searches each directory of the system property
+ * <code>java.library.path</code> for a file named
+ * <code>System.mapLibraryName(libname)</code>. There may be a security
+ * check, of <code>checkLink</code>.
*
* @param filename the file to load
* @throws SecurityException if permission is denied
* @throws UnsatisfiedLinkError if the library is not found
+ * @see System#mapLibraryName(String)
+ * @see ClassLoader#findLibrary(String)
*/
public void loadLibrary(String libname)
{
- // XXX First, check ClassLoader.findLibrary(libname)
- for (int i = 0; i < libpath.length; i++)
+ String filename;
+ ClassLoader cl = VMSecurityManager.currentClassLoader();
+ if (cl != null)
{
- try
+ filename = cl.findLibrary(libname);
+ if (filename != null)
{
- // XXX use this:
- // load(libpath[i] + System.mapLibraryName(libname));
- String filename = nativeGetLibname(libpath[i],libname);
load(filename);
return;
}
- catch(UnsatisfiedLinkError e)
- {
- // Try next path element.
- }
}
+ filename = System.mapLibraryName(libname);
+ for (int i = 0; i < libpath.length; i++)
+ try
+ {
+ load(libpath[i] + filename);
+ return;
+ }
+ catch (UnsatisfiedLinkError e)
+ {
+ // Try next path element.
+ }
throw new UnsatisfiedLinkError("Could not find library " + libname + ".");
}
@@ -577,12 +670,11 @@ public class Runtime
* @param manager the new security manager
* @throws SecurityException if permission is denied
*/
- static void setSecurityManager(SecurityManager manager)
+ static synchronized void setSecurityManager(SecurityManager manager)
{
- // XXX Synchronize, for thread safety
if (securityManager != null)
- // XXX Check RuntimePermission("setSecurityManager")
- throw new SecurityException("Security Manager already set");
+ securityManager.checkPermission
+ (new RuntimePermission("setSecurityManager"));
securityManager = manager;
}
@@ -598,6 +690,13 @@ public class Runtime
}
/**
+ * 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.
*
@@ -615,7 +714,7 @@ public class Runtime
* @param libname the short version of the library name
* @return the full filename
*/
- native String nativeGetLibname(String pathname, String libname);
+ static native String nativeGetLibname(String pathname, String libname);
/**
* Execute a process. The command line has already been tokenized, and
@@ -628,15 +727,8 @@ public class Runtime
* @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
*/
// native Process execInternal(String[] cmd, String[] env, File dir);
native Process execInternal(String[] cmd, String[] env);
-
- /**
- * Get the library path.
- *
- * @return the library path
- * XXX Use the java.library.path property
- */
- static native String getLibraryPath();
}
diff --git a/vm/reference/java/lang/VMSecurityManager.java b/vm/reference/java/lang/VMSecurityManager.java
index a70d049d9..3d95c8a96 100644
--- a/vm/reference/java/lang/VMSecurityManager.java
+++ b/vm/reference/java/lang/VMSecurityManager.java
@@ -60,7 +60,12 @@ final class VMSecurityManager
static native Class[] getClassContext();
/**
- * Get the current ClassLoader--the one nearest to the top of the stack.
+ * Get the current ClassLoader. This is the first non-null class loader
+ * on the stack, if one exists, stopping either at the end of the stack
+ * or the first instance of a PrivilegedAction. In other words, this call
+ * automatically unwinds past all classes loaded by the bootstrap loader,
+ * where getClassLoader() returns null, to get to the user class that
+ * really invoked the call that needs a classloader.
*
* @return the current ClassLoader
*/