Browse Source

Restores the stopped threads after a restoreState request

Johan Mabille 5 years ago
parent
commit
d41943e4ad
4 changed files with 46 additions and 25 deletions
  1. 15 0
      src/debugger.ts
  2. 5 5
      src/index.ts
  3. 24 18
      src/service.ts
  4. 2 2
      src/tokens.ts

+ 15 - 0
src/debugger.ts

@@ -158,6 +158,20 @@ export namespace Debugger {
       this._modeChanged.emit(mode);
     }
 
+    /**
+     * The set of threads in stopped state.
+     */
+    get stoppedThreads(): Set<number> {
+      return this._stoppedThreads;
+    }
+
+    /**
+     * Assigns the parameters to the set of threads in stopped state.
+     */
+    set stoppedThreads(threads: Set<number>) {
+      this._stoppedThreads = threads;
+    }
+
     get modeChanged(): ISignal<this, IDebugger.Mode> {
       return this._modeChanged;
     }
@@ -181,6 +195,7 @@ export namespace Debugger {
     private _codeValue: IObservableString;
     private _isDisposed = false;
     private _mode: IDebugger.Mode;
+    private _stoppedThreads = new Set<number>();
     private _modeChanged = new Signal<this, IDebugger.Mode>(this);
     private _disposed = new Signal<this, void>(this);
   }

+ 5 - 5
src/index.ts

@@ -320,7 +320,7 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
       caption: 'Continue',
       iconClass: 'jp-MaterialIcon jp-RunIcon',
       isEnabled: () => {
-        return service.isThreadStopped();
+        return service.hasStoppedThreads();
       },
       execute: async () => {
         await service.continue();
@@ -333,7 +333,7 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
       caption: 'Terminate',
       iconClass: 'jp-MaterialIcon jp-StopIcon',
       isEnabled: () => {
-        return service.isThreadStopped();
+        return service.hasStoppedThreads();
       },
       execute: async () => {
         await service.restart();
@@ -346,7 +346,7 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
       caption: 'Next',
       iconClass: 'jp-MaterialIcon jp-StepOverIcon',
       isEnabled: () => {
-        return service.isThreadStopped();
+        return service.hasStoppedThreads();
       },
       execute: async () => {
         await service.next();
@@ -358,7 +358,7 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
       caption: 'Step In',
       iconClass: 'jp-MaterialIcon jp-StepInIcon',
       isEnabled: () => {
-        return service.isThreadStopped();
+        return service.hasStoppedThreads();
       },
       execute: async () => {
         await service.stepIn();
@@ -370,7 +370,7 @@ const main: JupyterFrontEndPlugin<IDebugger> = {
       caption: 'Step Out',
       iconClass: 'jp-MaterialIcon jp-StepOutIcon',
       isEnabled: () => {
-        return service.isThreadStopped();
+        return service.hasStoppedThreads();
       },
       execute: async () => {
         await service.stepOut();

+ 24 - 18
src/service.ts

@@ -91,10 +91,10 @@ export class DebugService implements IDebugger {
 
     this._session.eventMessage.connect((_, event) => {
       if (event.event === 'stopped') {
-        this._stoppedThreads.add(event.body.threadId);
+        this.model.stoppedThreads.add(event.body.threadId);
         void this.getAllFrames();
       } else if (event.event === 'continued') {
-        this._stoppedThreads.delete(event.body.threadId);
+        this.model.stoppedThreads.delete(event.body.threadId);
         this.clearModel();
         this.clearSignals();
       }
@@ -166,10 +166,10 @@ export class DebugService implements IDebugger {
   }
 
   /**
-   * Whether the current thread is stopped.
+   * Whether there exists a thread in stopped state.
    */
-  isThreadStopped(): boolean {
-    return this._stoppedThreads.has(this.currentThread());
+  hasStoppedThreads(): boolean {
+    return this._model && this._model.stoppedThreads.size !== 0;
   }
 
   /**
@@ -186,12 +186,14 @@ export class DebugService implements IDebugger {
    */
   async stop(): Promise<void> {
     await this.session.stop();
-    this._stoppedThreads.clear();
+    if (this.model) {
+      this.model.stoppedThreads.clear();
+    }
   }
 
   /**
    * Restarts the debugger.
-   * Precondition: isStarted() and stopped.
+   * Precondition: isStarted().
    */
   async restart(): Promise<void> {
     const breakpoints = this.model.breakpointsModel.breakpoints;
@@ -213,7 +215,6 @@ export class DebugService implements IDebugger {
    * @param autoStart - when true, starts the debugger
    * if it has not been started yet.
    */
-
   async restoreState(autoStart: boolean): Promise<void> {
     if (!this.model || !this.session) {
       return;
@@ -242,13 +243,21 @@ export class DebugService implements IDebugger {
         );
       });
     }
+    this._model.breakpointsModel.restoreBreakpoints(bpMap);
 
-    if (this._model) {
-      this._model.breakpointsModel.restoreBreakpoints(bpMap);
-    }
-    if (!this.isStarted() && autoStart) {
+    const stoppedThreads = new Set(reply.body.stoppedThreads);
+    this._model.stoppedThreads = stoppedThreads;
+
+    if (!this.isStarted() && (autoStart || stoppedThreads.size !== 0)) {
       await this.start();
     }
+
+    if (stoppedThreads.size !== 0) {
+      await this.getAllFrames();
+    } else {
+      this.clearModel();
+      this.clearSignals();
+    }
   }
 
   /**
@@ -259,7 +268,7 @@ export class DebugService implements IDebugger {
       await this.session.sendRequest('continue', {
         threadId: this.currentThread()
       });
-      this._stoppedThreads.delete(this.currentThread());
+      this.model.stoppedThreads.delete(this.currentThread());
     } catch (err) {
       console.error('Error:', err.message);
     }
@@ -370,13 +379,13 @@ export class DebugService implements IDebugger {
     return reply;
   }
 
-  getAllFrames = async () => {
+  async getAllFrames() {
     this._model.callstackModel.currentFrameChanged.connect(this.onChangeFrame);
     this._model.variablesModel.variableExpanded.connect(this.getVariable);
 
     const stackFrames = await this.getFrames(this.currentThread());
     this._model.callstackModel.frames = stackFrames;
-  };
+  }
 
   onChangeFrame = async (_: Callstack.Model, frame: Callstack.IFrame) => {
     if (!frame) {
@@ -521,9 +530,6 @@ export class DebugService implements IDebugger {
   private _hashMethod: (code: string) => string;
   private _tmpFilePrefix: string;
   private _tmpFileSuffix: string;
-
-  // TODO: move this in model
-  private _stoppedThreads = new Set();
 }
 
 namespace Private {

+ 2 - 2
src/tokens.ts

@@ -72,9 +72,9 @@ export interface IDebugger extends IDisposable {
   isStarted(): boolean;
 
   /**
-   * Whether the current thread is stopped.
+   * Whether there exist a thread in stopped state.
    */
-  isThreadStopped(): boolean;
+  hasStoppedThreads(): boolean;
 
   /**
    * Starts a debugger.