Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: b4491f09d87f3c205b2dd756ca547578213f8b30 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*******************************************************************************
 * Copyright (c) 2008, 2014 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.examples.dsf.pda.launch;

import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
import org.eclipse.cdt.dsf.concurrent.ThreadSafe;
import org.eclipse.cdt.dsf.debug.model.DsfLaunch;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.examples.dsf.pda.PDAPlugin;
import org.eclipse.cdt.examples.dsf.pda.service.PDATerminatedEvent;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.Launch;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.debug.core.model.ITerminate;

/**
 * The PDA launch object. In general, a DSF-based debugger has to override 
 * the base launch class in order to supply its own content providers for the 
 * debug view.  Additionally, the PDA launch is used to monitor the state of the
 * PDA debugger and to shutdown the DSF services and session belonging to the 
 * launch.
 * <p>
 * The PDA launch class mostly contains methods and fields that can be accessed
 * on any thread.  However, some fields and methods used for managing the DSF
 * session need to be synchronized using the DSF executor.
 * </p>
 */
@ThreadSafe
public class PDALaunch extends DsfLaunch
implements ITerminate
{   
    // DSF executor and session.  Both are created and shutdown by the launch. 
    private final DefaultDsfExecutor fExecutor;
    private final DsfSession fSession;

    // Objects used to track the status of the DSF session.
    private boolean fInitialized = false;
    private boolean fShutDown = false;
    
    @ConfinedToDsfExecutor("getSession().getExecutor()")
    private Sequence fInitializationSequence = null;

    /**
     * Launch constructor creates the launch for given parameters.  The
     * constructor also creates a DSF session and an executor, so that 
     * {@link #getSession()} returns a valid value, however no services 
     * are initialized yet. 
     * 
     * @see Launch
     */
    public PDALaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) {
        super(launchConfiguration, mode, locator);

        // Create the dispatch queue to be used by debugger control and services 
        // that belong to this launch
        final DefaultDsfExecutor dsfExecutor = new DefaultDsfExecutor(PDAPlugin.ID_PDA_DEBUG_MODEL);
        dsfExecutor.prestartCoreThread();
        fExecutor = dsfExecutor;
        fSession = DsfSession.startSession(fExecutor, PDAPlugin.ID_PDA_DEBUG_MODEL);

        // Register the launch as an adapter This ensures that the launch,
        // and debug model ID will be associated with all DMContexts from this
        // session.
        fSession.registerModelAdapter(ILaunch.class, this);
    }

    /**
     * Returns the DSF services session that belongs to this launch.  This 
     * method will always return a DsfSession object, however if the debugger 
     * is shut down, the session will no longer active.
     */
    public DsfSession getSession() { return fSession; }

    /**
     * Initializes the DSF services using the specified parameters.  This 
     * method has to be called on the executor thread in order to avoid 
     * synchronization issues.  
     */
    @ConfinedToDsfExecutor("getSession().getExecutor()")
    public void initializeServices(String program, final RequestMonitor rm)
    {
        // Double-check that we're being called in the correct thread.
        assert fExecutor.isInExecutorThread();

        // Check if shutdownServices() was called already, which would be 
        // highly unusual, but if so we don't need to do anything except set 
        // the initialized flag.
        synchronized(this) {
            if (fShutDown) {
                fInitialized = true;
                return;
            }
        }

        // Register the launch as listener for services events.
        fSession.addServiceEventListener(PDALaunch.this, null);

        // The initialization sequence is stored in a field to allow it to be 
        // canceled if shutdownServices() is called before the sequence 
        // completes.
        fInitializationSequence = new PDAServicesInitSequence(
            getSession(), this, program, 
            new RequestMonitor(ImmediateExecutor.getInstance(), rm) {
                @Override
                protected void handleCompleted() {
                    // Set the initialized flag and check whether the 
                    // shutdown flag is set.  Access the flags in a 
                    // synchronized section as these flags can be accessed
                    // on any thread.
                    boolean doShutdown = false;
                    synchronized (this) { 
                        fInitialized = true;
                        fInitializationSequence = null;
                        if (fShutDown) {
                            doShutdown = true;
                        }
                    }

                    if (doShutdown) {
                        // If shutdownServices() was already called, start the 
                        // shutdown sequence now.
                        doShutdown(rm);
                    } else {
                        // If there was an error in the startup sequence, 
                        // report the error to the client.
                        if (getStatus().getSeverity() == IStatus.ERROR) {
                            rm.setStatus(getStatus());
                        }
                        rm.done();
                    }
                    fireChanged();
                }
            });

        // Finally, execute the sequence. 
        getSession().getExecutor().execute(fInitializationSequence);
    }

    /**
     * Event handler for a debugger terminated event.    
     */
    @DsfServiceEventHandler 
    public void eventDispatched(PDATerminatedEvent event) {
        shutdownServices(new RequestMonitor(ImmediateExecutor.getInstance(), null));
    }

    /**
     * Returns whether the DSF service initialization sequence has completed yet.
     */
    public synchronized boolean isInitialized() {
        return fInitialized;
    }

    /**
     * Returns whether the DSF services have been set to shut down.
     * @return
     */
    public synchronized boolean isShutDown() {
        return fShutDown;
    }

    @Override
    public boolean canTerminate() {
        return super.canTerminate() && isInitialized() && !isShutDown();
    }

    @Override
    public boolean isTerminated() {
        return super.isTerminated() || isShutDown();
    }


    @Override
    public void terminate() throws DebugException {
        if (isShutDown()) return;
        super.terminate();
    }

    /**
     * Shuts down the services, the session and the executor associated with 
     * this launch.  
     * <p>
     * Note: The argument request monitor to this method should NOT use the
     * executor that belongs to this launch.  By the time the shutdown is 
     * complete, this executor will not be dispatching anymore and the 
     * request monitor will never be invoked.  Instead callers should use
     * the {@link ImmediateExecutor}.
     * </p>
     * @param rm The request monitor invoked when the shutdown is complete.    
     */
    @ConfinedToDsfExecutor("getSession().getExecutor()")
    public void shutdownServices(final RequestMonitor rm) {
        // Check initialize and shutdown flags to determine if the shutdown
        // sequence can be called yet.
        boolean doShutdown = false;
        synchronized (this) {
            if (!fInitialized && fInitializationSequence != null) {
                // Launch has not yet initialized, try to cancel the 
                // shutdown sequence.
                fInitializationSequence.cancel(false);
            } else {
                doShutdown = !fShutDown && fInitialized;
            }
            fShutDown = true;
        }

        if (doShutdown) {
            doShutdown(rm);
        } else {
            rm.done();
        }
    }

    @ConfinedToDsfExecutor("getSession().getExecutor()")
    private void doShutdown(final RequestMonitor rm) {
        fExecutor.execute( new PDAServicesShutdownSequence(
            fExecutor, fSession.getId(),
            new RequestMonitor(fSession.getExecutor(), rm) { 
                @Override
                public void handleCompleted() {
                    fSession.removeServiceEventListener(PDALaunch.this);
                    if (!isSuccess()) {
                        PDAPlugin.getDefault().getLog().log(new MultiStatus(
                            PDAPlugin.PLUGIN_ID, -1, new IStatus[]{getStatus()}, "Session shutdown failed", null)); //$NON-NLS-1$
                    }
                    // Last order of business, shutdown the dispatch queue.
                    DsfSession.endSession(fSession);
                    // endSession takes a full dispatch to distribute the 
                    // session-ended event, finish step only after the dispatch.
                    fireTerminate();

                    rm.setStatus(getStatus());
                    rm.done();
                }
            }) );
    }

    @SuppressWarnings("unchecked")
    @Override
    public Object getAdapter(Class adapter) {
        // Force adapters to be loaded.  Otherwise the adapter manager may not find
        // the model proxy adapter for DSF-based debug elements.
        Platform.getAdapterManager().loadAdapter(this, adapter.getName());
        return super.getAdapter(adapter);
    }
    
    @Override
	public void launchRemoved(ILaunch launch) {
		if (this.equals(launch)) {
    		fExecutor.shutdown();
    	}
    	super.launchRemoved(launch);
    }
}

Back to the top