Sfoglia il codice sorgente

Preserve telemetry during aborted worker cleanup

Stefan Stidl 1 settimana fa
parent
commit
9f8826fb76
3 ha cambiato i file con 55 aggiunte e 9 eliminazioni
  1. 13 5
      speedtest.js
  2. 11 4
      speedtest_worker.js
  3. 31 0
      tests/e2e/worker-lifecycle.spec.js

+ 13 - 5
speedtest.js

@@ -336,6 +336,13 @@ Speedtest.prototype = {
     const isCurrentRun = function() {
       return this._activeRun === run && this.worker === worker;
     }.bind(this);
+    const armAbortTimeout = function() {
+      if (run.abortTimeout !== null) clearTimeout(run.abortTimeout);
+      run.abortTimeout = setTimeout(function() {
+        if (this._activeRun === run && this.worker === run.worker)
+          run.finish(true);
+      }.bind(this), 1000);
+    }.bind(this);
     const finish = function(aborted) {
       if (run.ended) return;
       run.ended = true;
@@ -362,14 +369,18 @@ Speedtest.prototype = {
       }
     }.bind(this);
 
+    run.armAbortTimeout = armAbortTimeout;
     run.finish = finish;
     this._activeRun = run;
     this.worker = worker;
     worker.onmessage = function(e) {
       if (!isCurrentRun() || run.ended) return;
+      const data = JSON.parse(e.data);
+      // While an aborted run is submitting telemetry, each status response
+      // proves that the worker is still responsive, so keep its watchdog alive.
+      if (data.abortPending && run.abortTimeout !== null) armAbortTimeout();
       if (e.data === run.prevData) return;
       else run.prevData = e.data;
-      const data = JSON.parse(e.data);
       try {
         if (this.onupdate) this.onupdate(data);
       } catch (e) {
@@ -415,10 +426,7 @@ Speedtest.prototype = {
     if (this._state < 4 && run && this.worker === run.worker) {
       run.worker.postMessage("abort");
       if (!run.ended && run.abortTimeout === null) {
-        run.abortTimeout = setTimeout(function() {
-          if (this._activeRun === run && this.worker === run.worker)
-            run.finish(true);
-        }.bind(this), 1000);
+        run.armAbortTimeout();
       }
     }
   }

+ 11 - 4
speedtest_worker.js

@@ -16,6 +16,7 @@ let dlProgress = 0; //progress of download test 0-1
 let ulProgress = 0; //progress of upload test 0-1
 let pingProgress = 0; //progress of ping+jitter test 0-1
 let testId = null; //test ID (sent back by telemetry if used, null otherwise)
+let abortPending = false; // true while aborted-run telemetry is being submitted
 
 let log = ""; //telemetry log
 function tlog(s) {
@@ -102,7 +103,8 @@ this.addEventListener("message", function(e) {
 				dlProgress: dlProgress,
 				ulProgress: ulProgress,
 				pingProgress: pingProgress,
-				testId: testId
+				testId: testId,
+				abortPending: abortPending
 			})
 		);
 	}
@@ -246,13 +248,12 @@ this.addEventListener("message", function(e) {
 	}
 	if (params[0] === "abort") {
 		// abort command
-        if (testState >= 4) return;
+		if (testState >= 4 || abortPending) return;
 		tlog("manually aborted");
 		clearRequests(); // stop all xhr activity
 		runNextTest = null;
 		if (interval) clearInterval(interval); // clear timer if present
-		if (settings.telemetry_level > 1) sendTelemetry(function() {});
-		testState = 5; //set test as aborted
+		abortPending = settings.telemetry_level > 1;
 		dlStatus = "";
 		ulStatus = "";
 		pingStatus = "";
@@ -261,6 +262,12 @@ this.addEventListener("message", function(e) {
 		dlProgress = 0;
 		ulProgress = 0;
 		pingProgress = 0;
+		const completeAbort = function() {
+			abortPending = false;
+			testState = 5; //set test as aborted
+		};
+		if (abortPending) sendTelemetry(completeAbort);
+		else completeAbort();
 	}
 });
 // stops all XHR activity, aggressively

+ 31 - 0
tests/e2e/worker-lifecycle.spec.js

@@ -182,6 +182,37 @@ test.describe("Speedtest worker lifecycle", () => {
     expect(await page.evaluate(() => window.__endCalls)).toBe(1);
   });
 
+  test("keeps a responsive worker alive while aborted-run telemetry is pending", async ({ page }) => {
+    await page.evaluate(() => {
+      window.__speedtest = new Speedtest();
+      window.__speedtest.start();
+      window.__speedtest.abort();
+      window.__worker = window.__workers[0];
+      window.__telemetryStatus = setInterval(
+        () => window.__worker.emit({ testState: 3, abortPending: true }),
+        100
+      );
+    });
+
+    await page.waitForTimeout(1100);
+    await page.evaluate(() => clearInterval(window.__telemetryStatus));
+    expect(
+      await page.evaluate(() => ({
+        state: window.__speedtest.getState(),
+        terminated: window.__worker.terminated
+      }))
+    ).toEqual({ state: 3, terminated: false });
+
+    const result = await page.evaluate((data) => {
+      window.__worker.emit(data);
+      return {
+        state: window.__speedtest.getState(),
+        terminated: window.__worker.terminated
+      };
+    }, terminalState(5));
+    expect(result).toEqual({ state: 4, terminated: true });
+  });
+
   test("ignores delayed events and an old abort timeout after a new run starts", async ({ page }) => {
     await page.evaluate((data) => {
       window.__speedtest = new Speedtest();