| /******************************************************************************* |
| * Copyright (c) 2007, 2011 Wind River Systems and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Wind River Systems - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.cdt.dsf.mi.service.command; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.RejectedExecutionException; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; |
| import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.DsfRunnable; |
| import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; |
| import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.Query; |
| import org.eclipse.cdt.dsf.concurrent.RequestMonitor; |
| import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; |
| import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; |
| import org.eclipse.cdt.dsf.mi.service.IMIBackend; |
| import org.eclipse.cdt.dsf.mi.service.IMIBackend.BackendStateChangedEvent; |
| import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; |
| import org.eclipse.cdt.dsf.service.DsfSession; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| |
| /** |
| * CLI Process object implementation which uses the {@link IMIBackend} service |
| * to monitor and control the underlying process. |
| * |
| * Note that starting with GDB 7.12, as long as a PTY is available, |
| * this process is no longer used. Instead, the real GDB process, |
| * along with its console will be used directly. A second PTY |
| * will be used to communicate using MI. |
| * |
| * @since 1.1 |
| */ |
| public class MIBackendCLIProcess extends AbstractCLIProcess { |
| |
| private IMIBackend fMIBackend; |
| private AtomicInteger fExitCode = new AtomicInteger(-1); |
| private BackedExitedEventListener fExitedEventListener; |
| |
| @ConfinedToDsfExecutor("getSession()#getExecutor") |
| public MIBackendCLIProcess(ICommandControlService commandControl, IMIBackend backend) throws IOException { |
| super(commandControl); |
| fMIBackend = backend; |
| if (fMIBackend.getState() == IMIBackend.State.TERMINATED) { |
| fExitCode.set(fMIBackend.getExitCode()); |
| } |
| fExitedEventListener = new BackedExitedEventListener(); |
| getSession().addServiceEventListener(fExitedEventListener, null); |
| } |
| |
| public class BackedExitedEventListener { |
| private final List<RequestMonitor> fWaitForRMs = new ArrayList<>(); |
| |
| @DsfServiceEventHandler |
| public void eventDispatched(BackendStateChangedEvent event) { |
| if (event.getState() == IMIBackend.State.TERMINATED && event.getBackendId().equals(fMIBackend.getId())) { |
| fExitCode.set(fMIBackend.getExitCode()); |
| for (RequestMonitor rm : fWaitForRMs) { |
| rm.done(); |
| } |
| fWaitForRMs.clear(); |
| } |
| } |
| |
| void dispose() { |
| for (RequestMonitor rm : fWaitForRMs) { |
| rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, |
| "Backend terminate event never received", null)); //$NON-NLS-1$ |
| rm.done(); |
| } |
| fWaitForRMs.clear(); |
| } |
| } |
| |
| /** |
| * @see java.lang.Process#waitFor() |
| */ |
| @Override |
| public int waitFor() throws InterruptedException { |
| if (!DsfSession.isSessionActive(getSession().getId())) { |
| return fExitCode.get(); |
| } |
| |
| try { |
| Query<Object> query = new Query<Object>() { |
| @Override |
| protected void execute(final DataRequestMonitor<Object> rm) { |
| if (!DsfSession.isSessionActive(getSession().getId()) || isDisposed() |
| || fMIBackend.getState() == IMIBackend.State.TERMINATED) { |
| rm.setData(new Object()); |
| rm.done(); |
| } else { |
| fExitedEventListener.fWaitForRMs.add(new ImmediateRequestMonitor(rm) { |
| @Override |
| protected void handleSuccess() { |
| rm.setData(new Object()); |
| rm.done(); |
| } |
| }); |
| } |
| } |
| }; |
| getSession().getExecutor().execute(query); |
| query.get(); |
| } catch (RejectedExecutionException e) { |
| } catch (ExecutionException e) { |
| } |
| return fExitCode.get(); |
| } |
| |
| /** |
| * @see java.lang.Process#exitValue() |
| */ |
| @Override |
| public int exitValue() { |
| if (!DsfSession.isSessionActive(getSession().getId())) { |
| return fExitCode.get(); |
| } |
| try { |
| getSession().getExecutor().submit(new Callable<Object>() { |
| @Override |
| public Object call() throws Exception { |
| if (fMIBackend.getState() != IMIBackend.State.TERMINATED) { |
| throw new IllegalThreadStateException("Backend Process has not exited"); //$NON-NLS-1$ |
| } |
| return null; |
| } |
| }).get(); |
| } catch (RejectedExecutionException e) { |
| } catch (InterruptedException e) { |
| } catch (ExecutionException e) { |
| if (e.getCause() instanceof RuntimeException) { |
| throw (RuntimeException) e.getCause(); |
| } |
| } |
| return fExitCode.get(); |
| } |
| |
| /** |
| * @see java.lang.Process#destroy() |
| */ |
| @Override |
| public void destroy() { |
| try { |
| getSession().getExecutor().execute(new DsfRunnable() { |
| @Override |
| public void run() { |
| if (!DsfSession.isSessionActive(getSession().getId())) |
| return; |
| if (isDisposed()) |
| return; |
| |
| fMIBackend.destroy(); |
| } |
| }); |
| } catch (RejectedExecutionException e) { |
| // Session disposed. |
| } |
| } |
| |
| @Override |
| public void dispose() { |
| fExitedEventListener.dispose(); |
| getSession().removeServiceEventListener(fExitedEventListener); |
| super.dispose(); |
| } |
| } |