001: /*
002: * @(#)LogicalVM.java 1.8 06/10/27
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.misc;
029:
030: import java.lang.reflect.Method;
031: import java.lang.reflect.Modifier;
032: import java.lang.reflect.InvocationTargetException;
033: import java.util.Vector;
034:
035: /*
036: * Comments (FIXMEs) on the current Logical VM implementation:
037: *
038: * - Still under development.
039: * - Java isolation API is still under discussion under JSR-121.
040: * Hopefully this implementation will cover major requirements.
041: * - A parent LVM can die before its children, except the main LVM
042: * (the main LVM keeps itself alive until all the child LVMs get
043: * terminated).
044: * - Killing a parent LVM doesn't cause its children to die, except
045: * the main LVM (if we terminate the main LVM, all go die).
046: * - No security checking done yet (no SecurityException).
047: * - No state management (no "IllegalLogicalVMStateException" thrown)
048: * - Uses remote (asynchronous) exception mechanism (ThreadDeath) when
049: * we terminate an LVM. Enabling and disabling remote exceptions
050: * to protect key data structures are not completely revised yet.
051: * - We also need to protect some of important finalizers (like one of
052: * FileOutputStream) so that it doesn't get terminated in the middle
053: * before freeing native resource by remote exceptions.
054: * - Supposed to interrupt blocking operations performed by threads when
055: * we terminate an LVM, but this part is not implemented yet.
056: * We need to interrupt blocking I/O, monitor operations (including
057: * monitor enter), etc.
058: * - Monitors for Class and String object are supposed to be replicated
059: * per LVM, but not implemented yet. CVM code assumes one Class object
060: * per a class, so, instead of having multiple replicated Class objects
061: * per LVM, we replicate its monitor only. There may need some other
062: * consideration so as for a Class object not to leak any objects from
063: * one LVM to another. As for String object, although it is immutable,
064: * the interned string support is making the situation difficult.
065: * Instead of going down the route to replicate the interned string
066: * table in the VM, we just share the object and replicate its monitor
067: * per LVM.
068: * - None of JVMTI, JVMPI, RMI nor Personal[Basis]Profile work has done yet.
069: */
070:
071: /*
072: * Implementation of a handle class of Logical VM
073: */
074: public final class LogicalVM {
075: private Isolate isolate;
076:
077: private LogicalVM(Isolate isolate) {
078: this .isolate = isolate;
079: }
080:
081: public LogicalVM(String className, String[] args, String n) {
082: isolate = new Isolate(className, args, n);
083: }
084:
085: public static LogicalVM getCurrentLogicalVM() {
086: return new LogicalVM(Isolate.getCurrent());
087: }
088:
089: public LogicalVM getParent() {
090: return new LogicalVM(isolate.getParent());
091: }
092:
093: public String getName() {
094: // A String objects is immutable and shared (we use per-LVM
095: // String object lock).
096: return isolate.getName();
097: }
098:
099: public void start() {
100: isolate.start();
101: }
102:
103: public void join() throws InterruptedException {
104: isolate.join();
105: }
106:
107: public void join(long millis) throws InterruptedException {
108: isolate.join(millis);
109: }
110:
111: public void join(long millis, int nanos)
112: throws InterruptedException {
113: isolate.join(millis, nanos);
114: }
115:
116: public void exit(int exitCode) {
117: isolate.exit(exitCode);
118: }
119:
120: public void halt(int exitCode) {
121: isolate.halt(exitCode);
122: }
123:
124: public boolean equals(Object obj) {
125: if (obj instanceof LogicalVM) {
126: LogicalVM other = (LogicalVM) obj;
127: return (this .isolate == other.isolate);
128: }
129: return false;
130: }
131: }
132:
133: /*
134: * Actual implementation of Logical VM
135: */
136: final class Isolate {
137: // Used by the native code to store pointer to per-Logical VM
138: // info data structure.
139: private int context = 0; /* FIXME: What about 64 bit support. */
140:
141: private String name;
142: private Isolate parent;
143: private Isolate main;
144: private Thread launcher;
145: private RequestHandler requestHandler;
146:
147: private String className;
148: private String[] args;
149:
150: // Special attention needs to be paid for use of static fields
151: private static Isolate current;
152:
153: private native void initContext(Thread initThread);
154:
155: private native void destroyContext();
156:
157: private static native void switchContext(Isolate target);
158:
159: // Worm hole to invoke java.lang.Shutdown.waitAllUserThreads...()
160: private static native void waitAllUserThreadsExitAndShutdown();
161:
162: // Worm hole to invoke java.lang.ref.Finalizer.runAllFinalizers()
163: private static native void runAllFinalizersOfSystemClass();
164:
165: /*
166: * Private constructor for the main (primordial) Logical VM.
167: * This constructor will be invoked from native code during the JVM
168: * start up and before completion of the JVM initialization.
169: */
170: private Isolate() {
171: // We are still in the middle of main LVM initialization.
172: // Do not perform anything fancy (like starting a new thread).
173: name = "main";
174: parent = null;
175: main = this ;
176: current = this ;
177: }
178:
179: public Isolate(String className, String[] args, String name) {
180: this .name = name;
181: parent = getCurrent();
182: main = parent.main;
183:
184: this .className = className;
185: // Make duplicates of arguments so as not to share mutable objects
186: // across LVMs. String objects are immutable and shared.
187: this .args = new String[args.length];
188: System.arraycopy(args, 0, this .args, 0, args.length);
189:
190: launcher = new Launcher("LVM " + name + " start-up thread");
191: }
192:
193: public static Isolate getCurrent() {
194: return current;
195: }
196:
197: public Isolate getParent() {
198: return parent;
199: }
200:
201: public String getName() {
202: return name;
203: }
204:
205: public void start() {
206: // Start this LVM form the context of the main LVM.
207: // This effectively keeps the main LVM alive until this LVM
208: // terminates.
209: main.remoteRequest(launcher);
210: }
211:
212: public void join() throws InterruptedException {
213: launcher.join();
214: }
215:
216: public void join(long millis) throws InterruptedException {
217: launcher.join(millis);
218: }
219:
220: public void join(long millis, int nanos)
221: throws InterruptedException {
222: launcher.join(millis, nanos);
223: }
224:
225: public void exit(int exitCode) {
226: remoteHaltOrExit(this , false, exitCode);
227: }
228:
229: public void halt(int exitCode) {
230: remoteHaltOrExit(this , true, exitCode);
231: }
232:
233: private static void remoteHaltOrExit(Isolate target,
234: final boolean isHalt, final int exitCode) {
235:
236: Isolate initiatingLVM = getCurrent();
237: if (target != initiatingLVM) {
238: target.remoteRequest(new Runnable() {
239: public void run() {
240: haltOrExit(isHalt, exitCode);
241: }
242: });
243: } else {
244: haltOrExit(isHalt, exitCode);
245: }
246: }
247:
248: private static void haltOrExit(boolean isHalt, int exitCode) {
249: Runtime rt = Runtime.getRuntime();
250: if (isHalt) {
251: rt.halt(exitCode);
252: } else {
253: rt.exit(exitCode);
254: }
255: }
256:
257: private void startRequestHandler() {
258: requestHandler = new RequestHandler("LVM " + name
259: + " request handler");
260: requestHandler.start();
261: }
262:
263: private void startMainRequestHandler() {
264: startRequestHandler();
265: // The following line is for main LVM to make join()
266: // implementation simpler. Anyway, the main LVM never terminates
267: // while any of Java threads is running. So, it doesn't matter.
268: launcher = requestHandler;
269: }
270:
271: private void remoteRequest(Runnable proc) {
272: requestHandler.request(proc);
273: }
274:
275: /*
276: * Helper class for executing a piece of code in this LVM's context.
277: */
278: private class RequestHandler extends Thread {
279: private Vector queue = new Vector();
280:
281: public RequestHandler(String name) {
282: super (name);
283: setDaemon(true);
284: }
285:
286: public synchronized void run() {
287: setPriority(MAX_PRIORITY - 2);
288: current = Isolate.this ;
289:
290: while (!ThreadRegistry.exitRequested()) {
291: if (queue.size() == 0) {
292: try {
293: wait();
294: } catch (InterruptedException e) {
295: continue;
296: }
297: }
298: Thread handler;
299: Object proc = queue.remove(0);
300: if (proc instanceof Thread) {
301: handler = (Thread) proc;
302: } else {
303: handler = new Thread((Runnable) proc);
304: }
305: handler.setDaemon(false);
306: handler.start();
307: }
308: }
309:
310: public synchronized void request(Runnable proc) {
311: queue.add(proc);
312: notify();
313: }
314: }
315:
316: /*
317: * Helper class for launching an application in a new Logical VM.
318: */
319: private class Launcher extends Thread {
320: Launcher(String name) {
321: super (name);
322: }
323:
324: public void run() {
325: // Don't allow any remote exception all the way
326: CVM.disableRemoteExceptions();
327:
328: // Initializes a new Logical VM context and switch to it.
329: initContext(this );
330: current = Isolate.this ;
331: setPriority(Thread.NORM_PRIORITY);
332:
333: startRequestHandler();
334:
335: Throwable uncaughtException = null;
336: try {
337: // Obtain the main ThreadGroup. There should only be
338: // one sub-group yet because of initContext().
339: ThreadGroup sys = Thread.currentThread()
340: .getThreadGroup();
341: ThreadGroup[] list = new ThreadGroup[1];
342: sys.enumerate(list, false);
343: ThreadGroup mainGroup = list[0];
344:
345: // Start-up the main thread of this LVM
346: Thread main = new Thread(mainGroup, new MainExec(),
347: "main");
348: main.start();
349:
350: // Shutdown procedure for this LVM
351: waitAllUserThreadsExitAndShutdown();
352: ThreadRegistry.waitAllSystemThreadsExit();
353: runAllFinalizersOfSystemClass();
354: } catch (Throwable t) {
355: uncaughtException = t;
356: }
357: // Switch the context back to the main LVM so that this
358: // thread gets removed from the main LVM's ThreadRegistry.
359: // In this way, we keep the main LVM alive while any of
360: // child LVMs is running.
361: switchContext(main);
362:
363: // This is the last thread of this LVM
364: Isolate.this .destroyContext();
365:
366: // Rethrow the uncaughtException and let the caller,
367: // Thread.startup(), deal with it.
368: if (uncaughtException != null) {
369: CVM.throwLocalException(uncaughtException);
370: }
371: }
372: }
373:
374: private class MainExec implements Runnable {
375: // Use Runnable so that a plain Thread object is used to run
376: // the main method of the new LVM.
377: public void run() {
378: Throwable uncaughtException = null;
379: try {
380: // Load the target class using the system class loader.
381: // This makes the main class loading procedure same
382: // as the ordinary JVM start-up.
383: ClassLoader scl = ClassLoader.getSystemClassLoader();
384: Class cls = scl.loadClass(className);
385:
386: Class[] argClses = { String[].class };
387: // Returns public method only
388: Method mainMethod = cls.getMethod("main", argClses);
389: if (mainMethod.getReturnType() != Void.TYPE) {
390: throw new NoSuchMethodException(
391: "return type is not void");
392: }
393: if (!Modifier.isStatic(mainMethod.getModifiers())) {
394: throw new IllegalAccessException(
395: "main is not static");
396: }
397: Object[] argObjs = { args };
398: mainMethod.invoke(null, argObjs);
399: } catch (InvocationTargetException ite) {
400: uncaughtException = ite.getTargetException();
401: } catch (Throwable t) {
402: uncaughtException = t;
403: }
404: // Rethrow the uncaughtException and let the caller,
405: // Thread.startup(), deal with it.
406: if (uncaughtException != null) {
407: CVM.throwLocalException(uncaughtException);
408: }
409: }
410: }
411:
412: private static void dprintln(String s) {
413: if (CVM.checkDebugFlags(CVM.DEBUGFLAG_TRACE_LVM) != 0) {
414: System.err.println(s);
415: }
416: }
417: }
|