/************************************************************************** * Parts copyright (c) 2001, 2002, 2003 by Punch Telematix. All rights * * reserved. * * Parts copyright (c) 2004, 2005, 2007, 2008, 2009, 2010 by /k/ Embedded * * Java Solutions. All rights reserved. * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * 1. Redistributions of source code must retain the above copyright * * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * 3. Neither the name of Punch Telematix or of /k/ Embedded Java Solutions* * nor the names of other contributors may be used to endorse or promote* * products derived from this software without specific prior written * * permission. * * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * * IN NO EVENT SHALL PUNCH TELEMATIX, /K/ EMBEDDED JAVA SOLUTIONS OR OTHER * * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **************************************************************************/ package java.lang; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.StringTokenizer; import java.util.Vector; import wonka.vm.GarbageCollector; import wonka.vm.NativeLibrary; import wonka.vm.NativeProcess; /** ** A bunch of methods used to manipulate the runtime environment. */ public class Runtime { /** ** The amount of time we wait before checking again whether all finalizers have run. */ private static int FINA_DONNA_PATIENZA = 1000; /** ** Value of the verbose property (if verboseSet is true). */ private static boolean verboseVal; /** ** true iff verbose property has been read */ private static boolean verboseSet; /** ** The single unique instance of Runtime. */ private static Runtime theRuntime; /** ** Mapping from names of libraries which have already been loaded to ** WeakReference's to the corresponding NativeLibrary object. */ private static HashMap loadedLibraries = new HashMap(); /** ** Set of Threads which have called runFinalization and are still waiting. */ static HashSet runners = new HashSet(); /** ** The Threads which should be started on shutdown. */ private Vector shutdownHooks; /** ** Set to true iff a shutdown is in progress. */ private boolean shuttingDown; /** ** Inner class used to implement runFinalization() */ private class FinaDonna { Thread waitingThread; public FinaDonna(Thread thread) { waitingThread = thread; synchronized (runners) { runners.add(thread); } } protected void finalize() { synchronized (runners) { runners.remove(waitingThread); runners.notifyAll(); } } } /** * Implement verbosity. * The first time the method is called we read the mika.verbose property * For every subsequent call we just return the value read * We need this method because trying the read the verbose property in the * constructor causes a seg fault */ private void debug(String s) { if (!verboseSet) { String verboseProperty = System.systemProperties.getProperty("mika.verbose", ""); verboseVal = (verboseProperty.indexOf("shutdown") >= 0); verboseSet = true; } if (verboseVal) { System.err.println(s); } } /** ** Private constructor to block attempts to create a new instance. */ private Runtime() { } private void permissionCheck(String permission) { if (wonka.vm.SecurityConfiguration.ENABLE_SECURITY_CHECKS) { SecurityManager sm = System.theSecurityManager; if (sm != null) { sm.checkPermission(new RuntimePermission(permission)); } } } /** ** Add a Thread to the list of threads to be started when we decide ** to shut down the system. */ public void addShutdownHook(Thread t) { permissionCheck("shutdownHooks"); synchronized(this) { if (shuttingDown) { throw new IllegalStateException(); } if (shutdownHooks == null) { shutdownHooks = new Vector(); } if (shutdownHooks.contains(t)) { throw new IllegalArgumentException(); } shutdownHooks.add(t); } } /** ** Remove a Thread from the list of threads to be started when we decide ** to shut down the system. */ public boolean removeShutdownHook(Thread t) { permissionCheck("shutdownHooks"); synchronized(this) { if (shutdownHooks == null) { return false; } if (shuttingDown) { throw new IllegalStateException(); } return shutdownHooks.remove(t); } } /* ** Method which should be called to run all the shutdown hooks. ** The LIFO behaviour is not mandated by the specification. */ private void runShutdownHooks() { synchronized(this) { if (shuttingDown || shutdownHooks == null) { return; } shuttingDown = true; } int n = shutdownHooks.size(); while (n > 0) { Thread t = (Thread)shutdownHooks.elementAt(--n); debug("Runtime: starting shutdown hook "+t); t.start(); } n = shutdownHooks.size(); while (n > 0) { Thread t = (Thread)shutdownHooks.elementAt(n-1); try { t.join(); --n; } catch (InterruptedException ie) {} debug("Runtime: joined shutdown hook "+t); shutdownHooks.removeElementAt(n); } shutdownHooks = null; debug("Runtime: all shutdown hooks have completed"); } /** ** Return the solitary instance of Runtime. */ public static synchronized Runtime getRuntime() { // CG 20030625 // synchronized (Runtime.class) { if (theRuntime == null) { theRuntime = new Runtime(); } // } return theRuntime; } /** ** Native method called by exit() after performing all checks. */ private native void exit0(int status); /** ** Exit the VM, after running all shutdown hooks. */ public void exit(int status) throws SecurityException { permissionCheck("VMexit"); debug("Runtime: call to exit(" + status + "), running shutdown hooks."); runShutdownHooks(); debug("Runtime: Finished running shutdown hooks, goodnight."); try { System.in.close(); System.out.close(); System.err.close(); } catch (Throwable t) {} exit0(status); } public void halt(int status) throws SecurityException { permissionCheck("VMexit"); debug("Runtime: call to halt(" + status + "), skip running shutdown hooks."); exit0(status); } public Process exec(String command) throws IOException, SecurityException { return exec(command, null, null); } public Process exec(String command, String envp[]) throws IOException, SecurityException { return exec(command, envp, null); } public Process exec(String command, String envp[], File path) throws IOException, SecurityException { if(command.length() == 0) { throw new IllegalArgumentException(); } StringTokenizer st = new StringTokenizer(command); String cmd[] = new String[st.countTokens()]; int i = 0; while(st.hasMoreTokens()) { cmd[i++] = st.nextToken(); } return exec(cmd, envp, path); } public Process exec(String cmdarray[]) throws IOException, SecurityException{ return exec(cmdarray, null, null); } public Process exec(String cmdarray[], String envp[]) throws IOException, SecurityException{ return exec(cmdarray, envp, null); } public Process exec(String cmdarray[], String envp[], File path) throws IOException, SecurityException{ return new NativeProcess(cmdarray, envp, (path != null ? path.getAbsolutePath() : null)); } /** ** Tell the garbage collector to do some work. */ public void gc(){ GarbageCollector theGC = GarbageCollector.getInstance(); theGC.request(Integer.MAX_VALUE); // theGC.request((int)(totalMemory() - freeMemory())); } /** ** Create an instance of FinaDonna and then throw it away. */ private void generateFinaDonna(Thread t) { new FinaDonna(t); } /** ** Ask the garbage collector to run finalizers. Actually what we really ** do is to create an instance of FinaDonna and then make it unreachable, ** kick the Garbage Collector, and wait for the finalizer to be run. */ public void runFinalization(){ debug("Runtime: runFinalization() invoked"); GarbageCollector theGC = GarbageCollector.getInstance(); Thread currentThread = Thread.currentThread(); synchronized(runners) { generateFinaDonna(currentThread); try { theGC.kick(); runners.wait(FINA_DONNA_PATIENZA); while (runners.contains(currentThread)) { debug("Runtime: runFinalization() still waiting"); theGC.kick(); runners.wait(FINA_DONNA_PATIENZA); } } catch (InterruptedException ie) { debug("Runtime: runFinalization() interrupted, baling out"); } debug("Runtime: runFinalization() completed"); } } /** ** @status not implemented */ public void traceInstructions(boolean on){} /** ** @status not implemented */ public void traceMethodCalls(boolean on){} /** ** @deprecated ** @remark always throws a runtime exception */ public InputStream getLocalizedInputStream(InputStream in){ return new BufferedInputStream(in); } /** ** @deprecated ** @remark always throws a runtime exception */ public OutputStream getLocalizedOutputStream(OutputStream out){ return out; } public native long totalMemory(); public native long freeMemory(); private static native ClassLoader getCallingClassLoader(); private static native ClassLoader getCallingCallingClassLoader(); /** ** Clean out any entries in <code>loadedLibraries</code> which have ** become stale because the class loader which created them has become ** unreachable (and hence the NativeLibrary object is also unreachable). */ private static void doLoadedLibrariesHousekeeping() { Iterator iter = loadedLibraries.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next(); Object value = entry.getValue(); try { WeakReference wr = (WeakReference)value; if (wr.get() == null) { iter.remove(); } } catch (ClassCastException cce) { // no hassle, it's just a placeholder String } } } public void load(String libname) throws SecurityException, UnsatisfiedLinkError { SecurityManager sm = System.theSecurityManager; if (sm != null) { // sm.checkLink(new RuntimePermission(libname)); } synchronized (loadedLibraries) { doLoadedLibrariesHousekeeping(); if (loadedLibraries.get(libname) == null) { loadedLibraries.put(libname, "loading"); } else { // HACK HACK HACK // Maybe the existing library is unreachable but we didn't notice, // do GC and try once more before throwing an error. gc(); doLoadedLibrariesHousekeeping(); if (loadedLibraries.get(libname) == null) { loadedLibraries.put(libname, "loading"); } else { throw new UnsatisfiedLinkError("Library '" + libname + "' already loaded"); } } } ClassLoader cl = getCallingClassLoader(); if (cl == null || cl == ClassLoader.systemClassLoader) { cl = getCallingCallingClassLoader(); } int handle = load0(libname); if (handle != 0) { NativeLibrary nl = new NativeLibrary(handle); cl.registerLibrary(nl); synchronized (loadedLibraries) { loadedLibraries.put(libname, new WeakReference(nl)); } } else { synchronized (loadedLibraries) { loadedLibraries.remove(libname); } } } public void loadLibrary(String libname) throws SecurityException, UnsatisfiedLinkError { SecurityManager sm = System.theSecurityManager; if (sm != null) { // sm.checkLink(new RuntimePermission(libname)); } String path = null; /* ** First try to look it up with the findLibrary method of ClassLoader. */ ClassLoader cl = getCallingClassLoader(); if (cl == null || cl == ClassLoader.systemClassLoader) { cl = getCallingCallingClassLoader(); } if(cl != null) { path = cl.findLibrary(libname); } String key = path != null ? path : libname; synchronized (loadedLibraries) { doLoadedLibrariesHousekeeping(); if (loadedLibraries.get(key) == null) { loadedLibraries.put(key, "loading"); } else { // HACK HACK HACK // Maybe the existing library is unreachable but we didn't notice, // do GC and try once more before throwing an error. gc(); doLoadedLibrariesHousekeeping(); if (loadedLibraries.get(key) == null) { loadedLibraries.put(key, "loading"); } else { throw new UnsatisfiedLinkError("Library '" + key + "' already loaded"); } } } if(cl != null && path == null) { path = cl.findLibrary("lib" + libname + ".so"); } int handle; if(path != null) { /* ** Found it with the classloader. */ handle = loadLibrary0(null, path); } else { /* ** No luck -> get the path from java.library.path and look it up. */ path = System.systemProperties.getProperty("java.library.path", null); handle = loadLibrary0(libname, path); } if (handle != 0) { NativeLibrary nl = new NativeLibrary(handle); cl.registerLibrary(nl); synchronized (loadedLibraries) { loadedLibraries.put(key, new WeakReference(nl)); } } else { synchronized (loadedLibraries) { loadedLibraries.remove(key); } } } /** ** Currently a synonym for totalMemory(). Could be coupled to -Xmx. */ public long maxMemory () { return totalMemory(); } /** ** Always returns 1. */ public int availableProcessors() { return 1; } private native int load0(String libname) throws UnsatisfiedLinkError; private native int loadLibrary0(String libname, String libpath) throws UnsatisfiedLinkError; public static void runFinalizersOnExit(boolean run) { //ignore } }