/******************************************************************************* * Copyright (c) 2006, 2016 Wind River Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Wind River Systems - initial API and implementation * Navid Mehregani (TI) - Bug 289526 - Migrate the Restart feature to the new one, as supported by the platform * Patrick Chuong (Texas Instruments) - Add support for icon overlay in the debug view (Bug 334566) * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) * Marc Khouzam (Ericsson) - Extracted GdbSessionAdapters to allow overriding *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import org.eclipse.cdt.debug.core.model.IConnectHandler; import org.eclipse.cdt.debug.core.model.IDebugNewExecutableHandler; import org.eclipse.cdt.dsf.concurrent.ThreadSafe; import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.core.runtime.IAdapterFactory; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchesListener2; import org.eclipse.debug.core.commands.IDisconnectHandler; import org.eclipse.debug.core.commands.ITerminateHandler; import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; import org.eclipse.debug.ui.contexts.ISuspendTrigger; /** * This implementation of platform adapter factory only retrieves the adapters * for the launch object. But it also manages the creation and destruction * of the session-based adapters which are returned by the * IDMContext.getAdapter() methods. * * When extending the GdbAdapterFactory, it is important to register all the * types declaratively (in the plugin.xml) that the factory can adapt the * extended launch to. * * See the plugin.xml that references GdbAdapterFactory for the current list, * and it should match {@link #getAdapterList()}. */ @ThreadSafe public class GdbAdapterFactory implements IAdapterFactory, ILaunchesListener2 { /** * Active adapter sets. They are accessed using the launch instance * which owns the debug services session. */ private static Map<GdbLaunch, GdbSessionAdapters> fgLaunchAdapterSets = Collections.synchronizedMap(new HashMap<>()); /** * Map of launches for which adapter sets have already been disposed. * This map (used as a set) is maintained in order to avoid re-creating an * adapter set after the launch was removed from the launch manager, but * while the launch is still being held by other classes which may * request its adapters. A weak map is used to avoid leaking * memory once the launches are no longer referenced. * <p> * Access to this map is synchronized using the fgLaunchAdapterSets * instance. * </p> */ private static Map<ILaunch, GdbSessionAdapters> fgDisposedLaunchAdapterSets = new WeakHashMap<>(); static void disposeAdapterSet(ILaunch launch) { synchronized(fgLaunchAdapterSets) { if (fgLaunchAdapterSets.containsKey(launch)) { fgLaunchAdapterSets.remove(launch).dispose(); fgDisposedLaunchAdapterSets.put(launch, null); } } } public GdbAdapterFactory() { DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this); } /** * This method only actually returns adapters for the launch object. */ @Override public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) { if (!(adaptableObject instanceof GdbLaunch)) return null; GdbLaunch launch = (GdbLaunch)adaptableObject; // Check for valid session. // Note: even if the session is no longer active, the adapter set // should still be returned. This is because the view model may still // need to show elements representing a terminated process/thread/etc. DsfSession session = launch.getSession(); if (session == null) return null; // Find the correct set of adapters based on the launch session-ID. If not found // it means that we have a new launch and new session, and we have to create a // new set of adapters. GdbSessionAdapters adapterSet; synchronized(fgLaunchAdapterSets) { // The adapter set for the given launch was already disposed. // Return a null adapter. if (fgDisposedLaunchAdapterSets.containsKey(launch)) { return null; } adapterSet = fgLaunchAdapterSets.get(launch); if (adapterSet == null) { // If the first time we attempt to create an adapterSet is once the session is // already inactive, we should not create it and return null. // This can happen, for example, when we run JUnit tests and we don't actually // have a need for any adapters until the launch is actually being removed. // Note that we must do this here because fgDisposedLaunchAdapterSets // may not already know that the launch has been removed because of a race // condition with the caller which is also processing a launchRemoved method. // Bug 334687 if (session.isActive() == false) { return null; } adapterSet = createGdbSessionAdapters(launch, session); fgLaunchAdapterSets.put(launch, adapterSet); } } // Returns the adapter type for the launch object. return adapterSet.getLaunchAdapter(adapterType); } /** * This list must match the list in the plugin.xml. See class comment. */ @Override public Class<?>[] getAdapterList() { return new Class<?>[] { IElementContentProvider.class, IModelProxyFactory.class, ISuspendTrigger.class, IColumnPresentationFactory.class, ITerminateHandler.class, IConnectHandler.class, IDisconnectHandler.class, IDebugNewExecutableHandler.class, }; } @Override public void launchesRemoved(ILaunch[] launches) { // Dispose the set of adapters for a launch only after the launch is // removed. for (ILaunch launch : launches) { if (launch instanceof GdbLaunch) { disposeAdapterSet(launch); } } } @Override public void launchesTerminated(ILaunch[] launches) { } @Override public void launchesAdded(ILaunch[] launches) { } @Override public void launchesChanged(ILaunch[] launches) { } protected GdbSessionAdapters createGdbSessionAdapters(ILaunch launch, DsfSession session) { return new GdbSessionAdapters(launch, session, getAdapterList()); } }