From: <ls...@us...> - 2008-11-09 12:24:56
|
Revision: 4691 http://jnode.svn.sourceforge.net/jnode/?rev=4691&view=rev Author: lsantha Date: 2008-11-09 12:24:49 +0000 (Sun, 09 Nov 2008) Log Message: ----------- Support for releasing owned monitors on thread stop. Internal execution methods on behalf of an isolate. Improved isolate termination. Modified Paths: -------------- trunk/core/src/classpath/vm/java/lang/Thread.java trunk/core/src/core/org/jnode/vm/isolate/VmIsolate.java trunk/core/src/core/org/jnode/vm/scheduler/Monitor.java trunk/core/src/core/org/jnode/vm/scheduler/MonitorManager.java trunk/core/src/core/org/jnode/vm/scheduler/VmScheduler.java trunk/core/src/core/org/jnode/vm/scheduler/VmThread.java trunk/core/src/test/org/jnode/test/core/StatusLinkTest.java Added Paths: ----------- trunk/core/src/core/org/jnode/vm/isolate/IsolateThreadFactory.java Modified: trunk/core/src/classpath/vm/java/lang/Thread.java =================================================================== --- trunk/core/src/classpath/vm/java/lang/Thread.java 2008-11-09 10:31:21 UTC (rev 4690) +++ trunk/core/src/classpath/vm/java/lang/Thread.java 2008-11-09 12:24:49 UTC (rev 4691) @@ -408,9 +408,14 @@ * @param name */ protected Thread(ThreadGroup group, Runnable target, String name, VmIsolatedStatics isolatedStatics) { + /* if (!(this instanceof IsolateThread)) { throw new SecurityException("Constructor can only be called from IsolateThread"); } + */ + if (!(this.getClass().getName().startsWith("org.jnode.vm.isolate"))) { + throw new SecurityException("Constructor can only be called from IsolateThread"); + } if (group == null) { throw new InternalError("Isolate thread has invalid group: " + name); } Added: trunk/core/src/core/org/jnode/vm/isolate/IsolateThreadFactory.java =================================================================== --- trunk/core/src/core/org/jnode/vm/isolate/IsolateThreadFactory.java (rev 0) +++ trunk/core/src/core/org/jnode/vm/isolate/IsolateThreadFactory.java 2008-11-09 12:24:49 UTC (rev 4691) @@ -0,0 +1,212 @@ +package org.jnode.vm.isolate; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; +import org.jnode.vm.classmgr.VmIsolatedStatics; +import org.jnode.vm.Vm; +import org.jnode.vm.VmSystem; +import org.jnode.plugin.PluginManager; +import org.jnode.naming.InitialNaming; +import javax.naming.NameNotFoundException; +import javax.isolate.IsolateStartupException; + +class IsolateThreadFactory implements ThreadFactory { + final ThreadGroup group; + final AtomicInteger threadNumber = new AtomicInteger(1); + final VmIsolatedStatics isolatedStatics; + final String namePrefix; + + IsolateThreadFactory(final VmIsolate isolate) { + group = isolate.getThreadGroup(); + namePrefix = "isolate-" + isolate.getId() + "-executor-"; + isolatedStatics = isolate.getIsolatedStaticsTable(); + } + + public Thread newThread(final Runnable r) { + class IsolateFactoryThread extends Thread { + IsolateFactoryThread(ThreadGroup group, String name, VmIsolatedStatics isolatedStatics) { + super(group, r, name, isolatedStatics); + } + } + + Thread t = new IsolateFactoryThread(group, namePrefix + threadNumber.getAndIncrement(), isolatedStatics){ + public void start() { + org.jnode.vm.Unsafe.debug("factory 1 thread start() " + this.getName() +"\n"); +// getVmThread().switchToIsolate(isolatedStatics); + super.start(); + } + }; + /* + PluginManager piManager; + try { + piManager = InitialNaming.lookup(PluginManager.NAME); + } catch (NameNotFoundException ex) { + throw new RuntimeException("Cannot find PluginManager", ex); + } + */ + //t.setContextClassLoader(piManager.getRegistry().getPluginsClassLoader()); +// if (t.isDaemon()) + // t.setDaemon(false); + // if (t.getPriority() != Thread.NORM_PRIORITY) + // t.setPriority(Thread.NORM_PRIORITY + 2); + return t; + } +} + + + + +class IsolateThreadFactory2 implements ThreadFactory { + final ThreadGroup group; + final AtomicInteger threadNumber = new AtomicInteger(1); + final VmIsolatedStatics isolatedStatics; + final String namePrefix; + private final Thread factoryThread; + private Thread newThread; + private final Object lock = new Object(); + private Runnable runnable; + + IsolateThreadFactory2(final VmIsolate isolate) { + group = isolate.getThreadGroup(); + namePrefix = "isolate-" + isolate.getId() + "-executor-"; + isolatedStatics = isolate.getIsolatedStaticsTable(); + factoryThread = new Thread(group, new Runnable(){ + public void run() { + while(true) { + synchronized (lock) { + try { + while(runnable == null) { + lock.wait(); + } + + newThread = new Thread(group, runnable, namePrefix + threadNumber.getAndIncrement()){ + public void start() { + org.jnode.vm.Unsafe.debug("factory thread start() " + this.getName() +"\n"); + super.start(); + } + }; + runnable = null; + lock.notifyAll(); + } catch (InterruptedException x) { + break; + } + } + } + } + },"isolate-" + isolate.getId() + "-thread-factory-"); + factoryThread.start(); + } + + public synchronized Thread newThread(final Runnable r) { + Thread ret; + org.jnode.vm.Unsafe.debug("IsolateThreadFactory2.newThread() called\n"); + org.jnode.vm.Unsafe.debugStackTrace(); + synchronized (lock) { + newThread = null; + runnable = r; + lock.notifyAll(); + while(newThread == null) { + try { + lock.wait(); + } catch (InterruptedException x) { + break; + } + } + ret = newThread; + newThread = null; + lock.notifyAll(); + } + org.jnode.vm.Unsafe.debug("IsolateThreadFactory2.newThread() returned\n"); + + return ret; + } +} + +/* ` +package org.jnode.vm.isolate; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; +import org.jnode.vm.classmgr.VmIsolatedStatics; + +class IsolateThreadFactory implements ThreadFactory { + final ThreadGroup group; + final AtomicInteger threadNumber = new AtomicInteger(1); + final VmIsolatedStatics isolatedStatics; + final String namePrefix; + + IsolateThreadFactory(final VmIsolate isolate) { + group = isolate.getThreadGroup(); + namePrefix = "isolate-" + isolate.getId() + "-executor-"; + isolatedStatics = isolate.getIsolatedStaticsTable(); + } + + public Thread newThread(final Runnable r) { + + org.jnode.vm.Unsafe.debug("newThread Called - 0\n"); + Thread t = new IsolateFactoryThread(group, r, namePrefix + threadNumber.getAndIncrement(), null); + org.jnode.vm.Unsafe.debug("newThread thread created - 0\n"); +// if (t.isDaemon()) + // t.setDaemon(false); + // if (t.getPriority() != Thread.NORM_PRIORITY) + // t.setPriority(Thread.NORM_PRIORITY + 2); + return t; + } +} + +class IsolateThreadFactory2 implements ThreadFactory { + final ThreadGroup group; + final AtomicInteger threadNumber = new AtomicInteger(1); + final VmIsolatedStatics isolatedStatics; + final String namePrefix; + private final Thread factoryThread; + private Thread newThread; + private final Object lock = new Object(); + private boolean flag = false; + + IsolateThreadFactory2(final VmIsolate isolate) { + group = isolate.getThreadGroup(); + namePrefix = "isolate-" + isolate.getId() + "-executor-"; + isolatedStatics = isolate.getIsolatedStaticsTable(); + factoryThread = new Thread(new Runnable(){ + public void run() { + while(true) { + synchronized (lock) { + try { + while(!flag) { + lock.wait(); + + } + + newThread = new IsolateFactoryThread(group, null, namePrefix + threadNumber.getAndIncrement(), null); + } catch (Exception x) { + x.printStackTrace(); + } + } + } + } + },"isolate-" + isolate.getId() + "-thread-factory-"); + factoryThread.start(); + } + + public Thread newThread(final Runnable r) { + + org.jnode.vm.Unsafe.debug("newThread Called - 0\n"); + Thread t = new IsolateFactoryThread(group, r, namePrefix + threadNumber.getAndIncrement(), null); + org.jnode.vm.Unsafe.debug("newThread thread created - 0\n"); +// if (t.isDaemon()) + // t.setDaemon(false); + // if (t.getPriority() != Thread.NORM_PRIORITY) + // t.setPriority(Thread.NORM_PRIORITY + 2); + return t; + } +} + + +class IsolateFactoryThread extends Thread { + IsolateFactoryThread(ThreadGroup group, Runnable r, String name, VmIsolatedStatics isolatedStatics) { + super(group, r, name, isolatedStatics); + } + } + +*/ \ No newline at end of file Modified: trunk/core/src/core/org/jnode/vm/isolate/VmIsolate.java =================================================================== --- trunk/core/src/core/org/jnode/vm/isolate/VmIsolate.java 2008-11-09 10:31:21 UTC (rev 4690) +++ trunk/core/src/core/org/jnode/vm/isolate/VmIsolate.java 2008-11-09 12:24:49 UTC (rev 4691) @@ -48,6 +48,7 @@ import org.jnode.vm.VmIOContext; import org.jnode.vm.VmMagic; import org.jnode.vm.VmSystem; +import org.jnode.vm.scheduler.VmThread; import org.jnode.vm.annotation.MagicPermission; import org.jnode.vm.annotation.PrivilegedActionPragma; import org.jnode.vm.annotation.SharedStatics; @@ -153,7 +154,7 @@ */ private BootableHashMap<VmIsolateLocal<?>, ?> isolateLocalMap = new BootableHashMap<VmIsolateLocal<?>, Object>(); - private List<VmIsolate> children = new LinkedList<VmIsolate>(); + private final List<VmIsolate> children = new LinkedList<VmIsolate>(); /** * Isolate states. @@ -235,7 +236,9 @@ static final VmIsolate getRoot() { if (rootIsolate == null) { - rootIsolate = new VmIsolate(); + rootIsolate = new VmIsolate(null/*Thread.currentThread().getVmThread().getIsolatedStatics()*/); +// org.jnode.vm.Unsafe.debug("getRoot() istatics: " + rootIsolate.isolatedStaticsTable + "\n"); +// org.jnode.vm.Unsafe.debugStackTrace(); } return rootIsolate; } @@ -265,7 +268,7 @@ /** * Constructor for the root isolate. */ - private VmIsolate() { + private VmIsolate(VmIsolatedStatics isolatedStatics) { this.id = StaticData.nextId(); this.isolate = new Isolate(this); this.mainClass = null; @@ -274,7 +277,7 @@ this.state = State.STARTED; this.threadGroup = getRootThreadGroup(); this.creator = null; - this.isolatedStaticsTable = null; + this.isolatedStaticsTable = isolatedStatics; // Initialize currentHolder IsolatedStaticData.current = this; @@ -301,6 +304,14 @@ this.isolatedStaticsTable = new VmIsolatedStatics(VmMagic.currentProcessor().getIsolatedStatics(), arch, new Unsafe.UnsafeObjectResolver()); this.creator = currentIsolate(); + if(getRoot().executor == null && isRoot()) { + //initialize the root executor on the creation of the first child + getRoot().invokeAndWait(new Runnable(){ + public void run() { + //org.jnode.vm.Unsafe.debug("Root executor ready\n"); + } + }); + } StaticData.isolates.add(this); } @@ -381,7 +392,9 @@ this.exitReason = IsolateStatus.ExitReason.OTHER_EXIT; } - stopAllThreads(); + disposeAppContext(this.exitReason == IsolateStatus.ExitReason.SELF_EXIT); + + //stopAllThreads(); } public final void isolateHalt(int status) { @@ -394,7 +407,9 @@ this.exitReason = IsolateStatus.ExitReason.OTHER_HALT; } - stopAllThreads(); + disposeAppContext(this.exitReason == IsolateStatus.ExitReason.SELF_HALT); + + //stopAllThreads(); } public final void systemExit(Isolate isolate, int status) { @@ -406,7 +421,9 @@ this.exitReason = IsolateStatus.ExitReason.SELF_EXIT; this.exitCode = status; - stopAllThreads(); + disposeAppContext(true); + + //stopAllThreads(); } public final void systemHalt(Isolate isolate, int status) { @@ -418,11 +435,14 @@ this.exitReason = IsolateStatus.ExitReason.SELF_HALT; this.exitCode = status; - stopAllThreads(); + disposeAppContext(true); + + //stopAllThreads(); } private void stopAllThreads() { - // FIXME - this is probably unsafe because any of the threads being killed could + // TODO - investigate it + // TODO - this is probably unsafe because any of the threads being killed could // be in the middle of updating a critical system data structure. I'm also // unsure of the order in which we are killing the threads here. It might be // better to kill the isolate's main thread first to give it the chance to @@ -431,8 +451,6 @@ if (ac > 0) { Thread[] ta = new Thread[ac]; int rc = threadGroup.enumerate(ta); - // FIXME - notwithstanding the above comments, is the 'current' thread the - // same one as the isolate's main thread? (Stephen Crawley - 2008-11-08) Thread current = Thread.currentThread(); boolean found = false; for (int i = 0; i < rc; i++) { @@ -459,11 +477,11 @@ * * @param status */ - public final void implicitExit(int status) { + public final void implicitExit(VmThread vmThread, int status) { //on this isolate may call this method testIsolate(currentIsolate().isolate); - // FIXME - handle demon threads + // TODO - handle demon threads if (threadGroup.activeCount() > 0 || threadGroup.activeGroupCount() > 0) return; @@ -473,7 +491,14 @@ this.exitCode = status; } - doExit(); + if(vmThread.getName().indexOf("-AWT-stopper") > - 1) { + doExit(); + } else { + disposeAppContext(true); + } + + //doExit(); + ///stopAllThreads(); } /** @@ -483,7 +508,7 @@ //on this isolate may call this method testIsolate(currentIsolate().isolate); - // FIXME - handle demon threads + // TODO - handle demon threads if (threadGroup.activeCount() > 0 || threadGroup.activeGroupCount() > 0) return; @@ -492,13 +517,18 @@ exitReason = IsolateStatus.ExitReason.UNCAUGHT_EXCEPTION; this.exitCode = -1; - doExit(); + disposeAppContext(true); + + //doExit(); + + //stopAllThreads(); } /** * Force termination of this isolate. * * @param status + * @deprecated */ @SuppressWarnings("deprecation") public final void halt(int status) { @@ -522,7 +552,8 @@ private void doExit() { try { - threadGroup.destroy(); + if(!threadGroup.isDestroyed()) + threadGroup.destroy(); } catch (Throwable t) { t.printStackTrace(); } @@ -639,8 +670,7 @@ } // Create a new ThreadGroup - this.threadGroup = new ThreadGroup(StaticData.getRoot().threadGroup, - mainClass); + this.threadGroup = new ThreadGroup(StaticData.getRoot().threadGroup, mainClass); // Find plugin manager PluginManager piManager; @@ -677,7 +707,7 @@ private Thread executorThread; */ - public void invokeAndWait(final Runnable task) { +// public void invokeAndWait(final Runnable task) { //TODO implement VmIsolate.invokeAndWait(Runnable) /* if(this == StaticData.rootIsolate){ @@ -700,7 +730,7 @@ } } */ - } +// } /* private class TaskExecutor implements Runnable{ public void run() { @@ -735,7 +765,161 @@ } */ + + private java.util.concurrent.ExecutorService executor; + /** + * Execute a task within this isolate and wait for completion. + * + * @param task the task as a Runnable object + */ + public synchronized void invokeAndWait(final Runnable task) { + if(executor == null) { + executor = java.util.concurrent.Executors.newSingleThreadExecutor(new IsolateThreadFactory2(this)); + } + if(task == null) + return; + + try { + if(executor.submit(task).get() != null) { + throw new RuntimeException("Execution failed!"); + } + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + /** + * Execute a task asynchronously within this isolate. + * + * @param task the task as a Runnable object + */ + public synchronized void invokeLater(final Runnable task) { + org.jnode.vm.Unsafe.debug("invokeLater Called - 0\n"); + if(executor == null) { + executor = java.util.concurrent.Executors.newSingleThreadExecutor(new IsolateThreadFactory(this)); + org.jnode.vm.Unsafe.debug("invokeAndWait executor created - 0\n"); + } + if(task == null) + return; + + try { + org.jnode.vm.Unsafe.debug("invokeAndWait submitting task - 0\n"); + executor.submit(task); + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + boolean isEDT(){ + if(appContext == null) + return false; + + + try { + Object eq = appContext.getClass().getMethod("get", Object.class). + invoke(appContext, appContext.getClass().getField("EVENT_QUEUE_KEY").get(null)); + if(eq == null) + return false; + + org.jnode.vm.Unsafe.debug("isEDT - 1\n"); + + Object t = eq.getClass().getField("dispatchThread").get(eq); + if(t == null) + return false; + + org.jnode.vm.Unsafe.debug("isEDT edt=" + t + "\n"); + org.jnode.vm.Unsafe.debug("isEDT currenThread=" + Thread.currentThread() + "\n"); + + return t == Thread.currentThread(); + }catch (Exception x) { + throw new RuntimeException(x); + } + /* + try { + return (Boolean) Class.forName("java.awt.EventQueue"). + getMethod("isDispatchThread").invoke(null); + } catch (Exception x) { + throw new RuntimeException(x); + + } + */ + // return false; + } + + private Object appContext; + + private void disposeAppContext(boolean intraIsolate) { + if(appSupport != null) { + appSupport.stop(intraIsolate); + } else { + stopAllThreads(); + } + } + + /** + * + * @param intraIsolate + * @deprecated + */ + private void disposeAppContext_old(boolean intraIsolate) { + final Object appContext; + final boolean is_edt; + synchronized (this) { + is_edt = isEDT(); + appContext = this.appContext; + this.appContext = null; + } + org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 000\n"); + org.jnode.vm.Unsafe.debugStackTrace(); + org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 000 " + intraIsolate + "\n"); + if(appContext != null) { + org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 0001\n"); + org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 0002\n"); + if(intraIsolate && is_edt) { + org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 0003\n"); + Thread t = new Thread(new Runnable() { + public void run() { + org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 00\n"); + getRoot().invokeAndWait(new Runnable() { + public void run() { + try { + org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 01\n"); + appContext.getClass().getMethod("dispose").invoke(appContext); + org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 02\n"); + } catch (Exception x) { + x.printStackTrace(); + } + } + }); + stopAllThreads(); + doExit(); + } + }, "isolate-" + getId() + "-AWT-stopper"); + t.start(); + } else { + org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 0004\n"); + org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 0\n"); + getRoot().invokeAndWait(new Runnable(){ + public void run() { + try { + org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 1\n"); + org.jnode.vm.Unsafe.debug("disposeAppContextCalled appcontext: " + appContext + "\n"); + org.jnode.vm.Unsafe.debug("disposeAppContextCalled appcontext.getClass(): " + appContext.getClass() + "\n"); + org.jnode.vm.Unsafe.debug("disposeAppContextCalled appcontext.getClass().dispose: " + appContext.getClass().getMethod("dispose") + "\n"); + appContext.getClass().getMethod("dispose").invoke(appContext); + org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 2\n"); + }catch (Exception x) { + x.printStackTrace(); + } + } + }); + stopAllThreads(); + } + } + } + + /** * Run this isolate. This method is called from IsolateThread. */ @PrivilegedActionPragma @@ -781,7 +965,9 @@ //create the appcontext for this isolate // TODO - improve this - Class.forName("sun.awt.SunToolkit").getMethod("createNewAppContext").invoke(null); + //appContext = Class.forName("sun.awt.SunToolkit").getMethod("createNewAppContext").invoke(null); + this.appSupport = new AppSupport(this); + this.appSupport.start(); // Update the state of this isolate. changeState(State.STARTED); @@ -812,6 +998,8 @@ } catch (Throwable ex2) { Unsafe.debug("Exception in catch block.. giving up: "); Unsafe.debug(ex2.getMessage()); + } finally { + systemHalt(isolate, -1); } } } @@ -958,15 +1146,158 @@ } } - private synchronized void addChild(VmIsolate child) { - this.children.add(child); + private void addChild(VmIsolate child) { + synchronized (children) { + children.add(child); + } } - private synchronized void removeChild(VmIsolate child) { - this.children.remove(child); + private void removeChild(VmIsolate child) { + synchronized (children) { + children.remove(child); + } } - public synchronized VmIsolate[] getChildren() { - return this.children.toArray(new VmIsolate[children.size()]); + public VmIsolate[] getChildren() { + synchronized (children) { + return children.toArray(new VmIsolate[children.size()]); + } } + + public ThreadGroup getThreadGroup() { + if(threadGroup == null) { + throw new IllegalStateException("Isolate not available"); + } + return threadGroup; + } + + private AppSupport appSupport; + + @SharedStatics + private static class AppSupport { + private static boolean awtSupport; + static { + try { + Class.forName("java.awt.Toolkit"); + awtSupport = true; + } catch (ClassNotFoundException x) { + awtSupport = false; + } + } + + private final VmIsolate vmIsolate; + private Object appContext; + + AppSupport(VmIsolate vmIsolate) { + this.vmIsolate = vmIsolate; + } + + boolean isAWTReady() { + if(!awtSupport) + return false; + + try { + return Class.forName("java.awt.Toolkit").getField("toolkit").get(null) != null; + } catch (Exception x) { + return false; + } + } + + void start() throws Exception { + if(isAWTReady()) { + synchronized (this) { + appContext = Class.forName("sun.awt.SunToolkit").getMethod("createNewAppContext").invoke(null); + } + } + } + + void stop(boolean intraIsolate) { + boolean done = false; + if(awtSupport) { + synchronized (this) { + if(appContext != null) { + disposeAppContext(intraIsolate); + done = true; + } + } + } + + if(!done) { + vmIsolate.stopAllThreads(); + vmIsolate.doExit(); + } + } + + boolean isEDT(){ + if(appContext == null) + return false; + + + try { + Object eq = appContext.getClass().getMethod("get", Object.class). + invoke(appContext, appContext.getClass().getField("EVENT_QUEUE_KEY").get(null)); + if(eq == null) + return false; + + Object t = eq.getClass().getField("dispatchThread").get(eq); + if(t == null) + return false; + + return t == Thread.currentThread(); + }catch (Exception x) { + throw new RuntimeException(x); + } + /* + try { + return (Boolean) Class.forName("java.awt.EventQueue"). + getMethod("isDispatchThread").invoke(null); + } catch (Exception x) { + throw new RuntimeException(x); + + } + */ + // return false; + } + + private void disposeAppContext(boolean intraIsolate) { + final Object appContext; + final boolean is_edt; + synchronized (this) { + is_edt = isEDT(); + appContext = this.appContext; + this.appContext = null; + } + if(appContext != null) { + if(intraIsolate && is_edt) { + Thread t = new Thread(new Runnable() { + public void run() { + getRoot().invokeAndWait(new Runnable() { + public void run() { + try { + appContext.getClass().getMethod("dispose").invoke(appContext); + } catch (Exception x) { + x.printStackTrace(); + } + } + }); + vmIsolate.stopAllThreads(); + vmIsolate.doExit(); + } + }, "isolate-" + vmIsolate.getId() + "-AWT-stopper"); + t.start(); + } else { + getRoot().invokeAndWait(new Runnable(){ + public void run() { + try { + appContext.getClass().getMethod("dispose").invoke(appContext); + }catch (Exception x) { + x.printStackTrace(); + } + } + }); + vmIsolate.stopAllThreads(); + } + } + } + } } Modified: trunk/core/src/core/org/jnode/vm/scheduler/Monitor.java =================================================================== --- trunk/core/src/core/org/jnode/vm/scheduler/Monitor.java 2008-11-09 10:31:21 UTC (rev 4690) +++ trunk/core/src/core/org/jnode/vm/scheduler/Monitor.java 2008-11-09 12:24:49 UTC (rev 4691) @@ -67,6 +67,11 @@ private final VmThreadQueue.ScheduleQueue notifyQueue; /** + * The previous monitor in a thread bound monitor chain + */ + private Monitor previous; + + /** * Create a new instance */ public Monitor() { @@ -86,6 +91,7 @@ Monitor(VmThread owner, int lockCount) { this.monitorLock = 0; this.owner = owner; + addToOwner(); this.lockCount = lockCount; if (lockCount < 1) { throw new IllegalArgumentException("LockCount must be >= 1"); @@ -101,7 +107,9 @@ * @param lockcount */ final void initialize(VmThread owner, int lockcount) { + dropFromOwner(); this.owner = owner; + addToOwner(); this.lockCount = lockcount; } @@ -139,7 +147,9 @@ // Try to claim this monitor if (lcAddr.attempt(0, 1)) { loop = false; + dropFromOwner(); this.owner = current; + addToOwner(); } else { // Claim the lock for this monitor lock(); @@ -178,6 +188,7 @@ lock(); try { wakeupWaitingThreads(enterQueue, true); + dropFromOwner(); owner = null; lockCount = 0; } finally { @@ -191,6 +202,37 @@ } /** + * Giveup this monitor. + * Called from VmThread on thread stop. + * + * @throws org.vmmagic.pragma.UninterruptiblePragma + * + */ + public final void release(VmThread thread) { + if (owner != thread) { + Unsafe.debug("Current thread is not the owner of this monitor\n"); + return; + } + + if (lockCount <= 0) { + Unsafe.debug("Monitor is not locked\n"); + return; + } + + // Monitor is locked by current thread, decrement lockcount + lockCount = 0; + lock(); + try { + wakeupWaitingThreads(enterQueue, true); + dropFromOwner(); + owner = null; + lockCount = 0; + } finally { + unlock(); + } + } + + /** * Causes current thread to wait until another thread invokes the notify() * method or the notifyAll() method for this monitor. In other words, this * method behaves exactly as if it simply performs the call wait(0). The @@ -232,6 +274,7 @@ current.wakeupTime = VmSystem.currentKernelMillis() + timeout; VmMagic.currentProcessor().getScheduler().addToSleepQueue(current); } + dropFromOwner(); owner = null; lockCount = 0; wakeupWaitingThreads(enterQueue, true); @@ -498,4 +541,56 @@ private final void unlock() { monitorLock = 0; } + + //monitor chaining to handle thread stop + + /** + * Returns the monitor previously owned by the owner thread of this monitor. + * + * @return the previous monitor + */ + Monitor getPrevious() { + return previous; + } + + @Inline + private void addToOwner() { + Monitor lom = owner.getLastOwnedMonitor(); + if(lom == null) { + //the first monitor + owner.setLastOwnedMonitor(this); + } else { + if(lom.owner != this.owner) { + //todo error + return; + } else { + if(lom == this) { + //no need to add it + return; + } else { + //add it + this.previous = lom; + owner.setLastOwnedMonitor(this); + } + } + } + } + + @Inline + private void dropFromOwner() { + if(owner == null) { + //error + return; + } + + Monitor lom = owner.getLastOwnedMonitor(); + if(lom == null) + return; + + if(lom != this) + return; + + owner.setLastOwnedMonitor(lom.previous); + lom.previous = null; + } } Modified: trunk/core/src/core/org/jnode/vm/scheduler/MonitorManager.java =================================================================== --- trunk/core/src/core/org/jnode/vm/scheduler/MonitorManager.java 2008-11-09 10:31:21 UTC (rev 4690) +++ trunk/core/src/core/org/jnode/vm/scheduler/MonitorManager.java 2008-11-09 12:24:49 UTC (rev 4691) @@ -26,6 +26,7 @@ import org.jnode.vm.VmMagic; import org.jnode.vm.annotation.Internal; import org.jnode.vm.annotation.MagicPermission; +import org.jnode.vm.annotation.Uninterruptible; import org.jnode.vm.classmgr.ObjectFlags; import org.jnode.vm.classmgr.ObjectLayout; import org.vmmagic.unboxed.Address; @@ -36,6 +37,7 @@ * @author epr */ @MagicPermission +@Uninterruptible public final class MonitorManager { /** @@ -78,7 +80,7 @@ if (counter.EQ(Word.fromIntZeroExtend(ObjectFlags.LOCK_COUNT_MASK))) { // thin lock entry counter == max, so we need to inflate // ourselves. - installInflatedLock(object).enter(); + installInflatedLock(object, null).enter(); return; } else { // not-quite-so-fast path: locked by current thread. @@ -98,8 +100,17 @@ } else { // Another thread owns the lock // thin lock owned by another thread. - // install an inflated lock. - installInflatedLock(object).enter(); + int ownerId = oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.THREAD_ID_MASK)).toInt(); + VmThread thread = VmMagic.currentProcessor().getScheduler().getThreadById(ownerId); + if(thread == null) { + //the owner of the lock was destroyed + //aquire the lock in fast fashion + statusPtr.store(statusFlags.or(tid)); + return; + } else { + // install an inflated lock. + installInflatedLock(object, thread).enter(); + } return; } @@ -217,7 +228,7 @@ */ private static Monitor getOwnedInflatedMonitor(Object object) throws IllegalMonitorStateException { - final Monitor m = installInflatedLock(object); + final Monitor m = installInflatedLock(object, null); if (!m.isOwner(VmMagic.currentProcessor().getCurrentThread())) { // lock not owned by us! String exMsg = "Object not locked by current thread"; @@ -233,7 +244,7 @@ * @param k */ static void setupInflatedLock(Object k) { - installInflatedLock(k); + installInflatedLock(k, null); } /** @@ -241,9 +252,10 @@ * until the object is unlocked or inflated. * * @param k the object for which the inflated lock is installed + * @param thread * @return the Monitor object representing the lock */ - private static Monitor installInflatedLock(Object k) { + private static Monitor installInflatedLock(Object k, VmThread thread) { Monitor m = null; Word monAddr = null; @@ -271,7 +283,10 @@ int lockcount = 1 + oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.LOCK_COUNT_MASK)). rshl(ObjectFlags.LOCK_COUNT_SHIFT).toInt(); int ownerId = oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.THREAD_ID_MASK)).toInt(); - m.initialize(VmMagic.currentProcessor().getScheduler().getThreadById(ownerId), lockcount); + if(thread == null) { + thread = VmMagic.currentProcessor().getScheduler().getThreadById(ownerId); + } + m.initialize(thread, lockcount); final Word statusFlags = oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.STATUS_FLAGS_MASK)); final Word newlockword = monAddr.or(statusFlags).or(Word.fromIntZeroExtend(ObjectFlags.LOCK_EXPANDED)); Modified: trunk/core/src/core/org/jnode/vm/scheduler/VmScheduler.java =================================================================== --- trunk/core/src/core/org/jnode/vm/scheduler/VmScheduler.java 2008-11-09 10:31:21 UTC (rev 4690) +++ trunk/core/src/core/org/jnode/vm/scheduler/VmScheduler.java 2008-11-09 12:24:49 UTC (rev 4691) @@ -73,6 +73,7 @@ * * @param visitor */ + @Uninterruptible final VmThread getThreadById(int id) { // final SpinLock lock = vm.allThreadsLock; VmThreadQueueEntry e = this.allThreadsQueue.first; Modified: trunk/core/src/core/org/jnode/vm/scheduler/VmThread.java =================================================================== --- trunk/core/src/core/org/jnode/vm/scheduler/VmThread.java 2008-11-09 10:31:21 UTC (rev 4690) +++ trunk/core/src/core/org/jnode/vm/scheduler/VmThread.java 2008-11-09 12:24:49 UTC (rev 4691) @@ -143,6 +143,11 @@ private Monitor waitForMonitor; /** + * The most recently owned monitor. + */ + private Monitor lastOwnedMonitor; + + /** * My priority */ protected int priority = Thread.NORM_PRIORITY; @@ -391,7 +396,7 @@ javaThread.onExit(); //exit the current isolate if needed if (ex instanceof ThreadDeath) { - VmIsolate.currentIsolate().implicitExit(0); + VmIsolate.currentIsolate().implicitExit(this, 0); } else { VmIsolate.currentIsolate().uncaughtExceptionExit(); } @@ -433,6 +438,18 @@ */ @Uninterruptible private final void doStop() { + //release monitors + Monitor lom = lastOwnedMonitor; + while(lom != null) { + Monitor prev = lom.getPrevious(); + lom.release(this); + if (prev == lom) + break; + lom = prev; + } + lastOwnedMonitor = null; + + final VmProcessor proc = VmMagic.currentProcessor(); final VmThread current = proc.getCurrentThread(); proc.getScheduler().unregisterThread(this); @@ -914,6 +931,7 @@ * @see ObjectFlags#THREAD_ID_SHIFT */ @KernelSpace + @Uninterruptible public final int getId() { return id; } @@ -1387,4 +1405,18 @@ + state); } } + + public VmIsolatedStatics getIsolatedStatics() { + return isolatedStatics; + } + + @Uninterruptible + final Monitor getLastOwnedMonitor() { + return lastOwnedMonitor; + } + + @Uninterruptible + final void setLastOwnedMonitor(Monitor lastOwnedMonitor) { + this.lastOwnedMonitor = lastOwnedMonitor; + } } Modified: trunk/core/src/test/org/jnode/test/core/StatusLinkTest.java =================================================================== --- trunk/core/src/test/org/jnode/test/core/StatusLinkTest.java 2008-11-09 10:31:21 UTC (rev 4690) +++ trunk/core/src/test/org/jnode/test/core/StatusLinkTest.java 2008-11-09 12:24:49 UTC (rev 4691) @@ -213,7 +213,7 @@ Math.sin(i); if (i % 100 == 0) { org.jnode.vm.Unsafe.debug("i=" + i + "\n"); - //System.out.println("i=" + i); + System.out.println("i=" + i); } } System.out.println("Child thread: exiting"); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |