--- /dev/null 2016-07-18 08:15:00.740233585 -0700 +++ new/src/jdk.jshell/share/classes/jdk/jshell/execution/LocalExecutionControl.java 2016-07-19 16:57:32.618242945 -0700 @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jshell.execution; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicReference; +import jdk.jshell.spi.ExecutionControl; + +/** + * An implementation of {@link jdk.jshell.spi.ExecutionControl} which executes + * in the same JVM as the JShell-core. + * + * @author Grigory Ptashko + */ +public class LocalExecutionControl extends DirectExecutionControl { + + private final Object STOP_LOCK = new Object(); + private boolean userCodeRunning = false; + private ThreadGroup execThreadGroup; + + /** + * Creates a local ExecutionControl instance. + * + * @return the generator + */ + public static ExecutionControl.Generator create() { + return env -> new LocalExecutionControl(); + } + + /** + * Creates an instance, delegating loader operations to the specified + * delegate. + * + * @param loaderDelegate the delegate to handle loading classes + */ + public LocalExecutionControl(LoaderDelegate loaderDelegate) { + super(loaderDelegate); + } + + /** + * Create an instance using the default class loading. + */ + public LocalExecutionControl() { + } + + @Override + protected String invoke(Method doitMethod) throws Exception { + execThreadGroup = new ThreadGroup("JShell process local execution"); + + AtomicReference iteEx = new AtomicReference<>(); + AtomicReference iaeEx = new AtomicReference<>(); + AtomicReference nmeEx = new AtomicReference<>(); + AtomicReference stopped = new AtomicReference<>(false); + + Thread.setDefaultUncaughtExceptionHandler((t, e) -> { + if (e instanceof InvocationTargetException) { + if (e.getCause() instanceof ThreadDeath) { + stopped.set(true); + } else { + iteEx.set((InvocationTargetException) e); + } + } else if (e instanceof IllegalAccessException) { + iaeEx.set((IllegalAccessException) e); + } else if (e instanceof NoSuchMethodException) { + nmeEx.set((NoSuchMethodException) e); + } else if (e instanceof ThreadDeath) { + stopped.set(true); + } + }); + + final Object[] res = new Object[1]; + Thread snippetThread = new Thread(execThreadGroup, () -> { + try { + res[0] = doitMethod.invoke(null, new Object[0]); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof ThreadDeath) { + stopped.set(true); + } else { + iteEx.set(e); + } + } catch (IllegalAccessException e) { + iaeEx.set(e); + } catch (ThreadDeath e) { + stopped.set(true); + } + }); + + snippetThread.start(); + Thread[] threadList = new Thread[execThreadGroup.activeCount()]; + execThreadGroup.enumerate(threadList); + for (Thread thread : threadList) { + if (thread != null) { + thread.join(); + } + } + + if (stopped.get()) { + throw new StoppedException(); + } + + if (iteEx.get() != null) { + throw iteEx.get(); + } else if (nmeEx.get() != null) { + throw nmeEx.get(); + } else if (iaeEx.get() != null) { + throw iaeEx.get(); + } + + return valueString(res[0]); + } + + @Override + @SuppressWarnings("deprecation") + public void stop() throws EngineTerminationException, InternalException { + synchronized (STOP_LOCK) { + if (!userCodeRunning) { + return; + } + if (execThreadGroup == null) { + throw new InternalException("Process-local code snippets thread group is null. Aborting stop."); + } + + execThreadGroup.stop(); + } + } + + @Override + protected void clientCodeEnter() { + synchronized (STOP_LOCK) { + userCodeRunning = true; + } + } + + @Override + protected void clientCodeLeave() { + synchronized (STOP_LOCK) { + userCodeRunning = false; + } + } + +}