瀏覽代碼

Refactor: Cross Realm Span

SukkaW 18 小時之前
父節點
當前提交
09183a3cd1
共有 4 個文件被更改,包括 110 次插入76 次删除
  1. 1 1
      Build/constants/reject-data-source.ts
  2. 49 27
      Build/index.ts
  3. 3 3
      Build/lib/parse-filter/domainlists.ts
  4. 57 45
      Build/trace/index.ts

+ 1 - 1
Build/constants/reject-data-source.ts

@@ -147,7 +147,7 @@ export const DOMAIN_LISTS_EXTRA: HostsSource[] = [
     true
     true
   ],
   ],
   [
   [
-    'https://raw.githubusercontent.com/DandelionSprout/adfilt/refs/heads/master/Alternate%20versions%20Anti-Malware%20List/AntiMalwareDomains.txt',
+    'https://raw.githubusercontent.com/DandelionSprout/adfilt/master/Alternate%20versions%20Anti-Malware%20List/AntiMalwareDomains.txt',
     [],
     [],
     true
     true
   ]
   ]

+ 49 - 27
Build/index.ts

@@ -25,7 +25,8 @@ import { downloadMockAssets } from './download-mock-assets';
 
 
 import { buildCloudMounterRules } from './build-cloudmounter-rules';
 import { buildCloudMounterRules } from './build-cloudmounter-rules';
 
 
-import { createSpan, printTraceResult, whyIsNodeRunning } from './trace';
+import { printStats, printTraceResult, whyIsNodeRunning } from './trace';
+import type { TraceResult } from './trace';
 import { buildDeprecateFiles } from './build-deprecate-files';
 import { buildDeprecateFiles } from './build-deprecate-files';
 import path from 'node:path';
 import path from 'node:path';
 import { ROOT_DIR } from './constants/dir';
 import { ROOT_DIR } from './constants/dir';
@@ -66,8 +67,6 @@ const buildFinishedLock = path.join(ROOT_DIR, '.BUILD_FINISHED');
 
 
   console.log(`Memory: ${os.totalmem() / (1024 * 1024)} MiB`);
   console.log(`Memory: ${os.totalmem() / (1024 * 1024)} MiB`);
 
 
-  const rootSpan = createSpan('root');
-
   if (fs.existsSync(buildFinishedLock)) {
   if (fs.existsSync(buildFinishedLock)) {
     fs.unlinkSync(buildFinishedLock);
     fs.unlinkSync(buildFinishedLock);
   }
   }
@@ -78,39 +77,62 @@ const buildFinishedLock = path.join(ROOT_DIR, '.BUILD_FINISHED');
       await import('why-is-node-running');
       await import('why-is-node-running');
     }
     }
 
 
-    const downloadPreviousBuildPromise = downloadPreviousBuild(rootSpan);
-    const buildCommonPromise = downloadPreviousBuildPromise.then(() => buildCommon(rootSpan));
+    const downloadPreviousBuildPromise = downloadPreviousBuild();
 
 
     await Promise.all([
     await Promise.all([
       downloadPreviousBuildPromise,
       downloadPreviousBuildPromise,
-      buildCommonPromise,
-      downloadPreviousBuildPromise.then(() => buildRejectIPList(rootSpan)),
-      downloadPreviousBuildPromise.then(() => buildAppleCdn(rootSpan)),
-      downloadPreviousBuildPromise.then(() => buildCdnDownloadConf(rootSpan)),
-      downloadPreviousBuildPromise.then(() => buildRejectDomainSet(rootSpan)),
-      downloadPreviousBuildPromise.then(() => buildTelegramCIDR(rootSpan)),
-      downloadPreviousBuildPromise.then(() => buildChnCidr(rootSpan)),
-      downloadPreviousBuildPromise.then(() => buildSpeedtestDomainSet(rootSpan)),
-      downloadPreviousBuildPromise.then(() => buildDomesticRuleset(rootSpan)),
-      downloadPreviousBuildPromise.then(() => buildGlobalRuleset(rootSpan)),
-      downloadPreviousBuildPromise.then(() => buildRedirectModule(rootSpan)),
-      downloadPreviousBuildPromise.then(() => buildAlwaysRealIPModule(rootSpan)),
-      downloadPreviousBuildPromise.then(() => buildStreamService(rootSpan)),
-      downloadPreviousBuildPromise.then(() => buildMicrosoftCdn(rootSpan)),
-      downloadPreviousBuildPromise.then(() => buildCloudMounterRules(rootSpan)),
-      downloadMockAssets(rootSpan)
+      downloadPreviousBuildPromise.then(() => buildCommon()),
+      downloadPreviousBuildPromise.then(() => buildRejectIPList()),
+      downloadPreviousBuildPromise.then(() => buildAppleCdn()),
+      downloadPreviousBuildPromise.then(() => buildCdnDownloadConf()),
+      downloadPreviousBuildPromise.then(() => buildRejectDomainSet()),
+      downloadPreviousBuildPromise.then(() => buildTelegramCIDR()),
+      downloadPreviousBuildPromise.then(() => buildChnCidr()),
+      downloadPreviousBuildPromise.then(() => buildSpeedtestDomainSet()),
+      downloadPreviousBuildPromise.then(() => buildDomesticRuleset()),
+      downloadPreviousBuildPromise.then(() => buildGlobalRuleset()),
+      downloadPreviousBuildPromise.then(() => buildRedirectModule()),
+      downloadPreviousBuildPromise.then(() => buildAlwaysRealIPModule()),
+      downloadPreviousBuildPromise.then(() => buildStreamService()),
+      downloadPreviousBuildPromise.then(() => buildMicrosoftCdn()),
+      downloadPreviousBuildPromise.then(() => buildCloudMounterRules()),
+      downloadMockAssets()
     ]);
     ]);
 
 
-    await buildDeprecateFiles(rootSpan);
-    await buildPublic(rootSpan);
-
-    rootSpan.stop();
-
-    printTraceResult(rootSpan.traceResult);
+    await buildDeprecateFiles();
+    await buildPublic();
 
 
     // write a file to demonstrate that the build is finished
     // write a file to demonstrate that the build is finished
     fs.writeFileSync(buildFinishedLock, 'BUILD_FINISHED\n');
     fs.writeFileSync(buildFinishedLock, 'BUILD_FINISHED\n');
 
 
+    const traces: TraceResult[] = [];
+    [
+      downloadPreviousBuild,
+      downloadMockAssets,
+      buildCommon,
+      buildRejectIPList,
+      buildAppleCdn,
+      buildCdnDownloadConf,
+      buildRejectDomainSet,
+      buildTelegramCIDR,
+      buildChnCidr,
+      buildSpeedtestDomainSet,
+      buildDomesticRuleset,
+      buildGlobalRuleset,
+      buildRedirectModule,
+      buildAlwaysRealIPModule,
+      buildStreamService,
+      buildMicrosoftCdn,
+      buildCloudMounterRules,
+      buildPublic,
+      buildDeprecateFiles
+    ].forEach((fn) => {
+      const trace = fn.getInternalTraceResult();
+      printTraceResult(trace);
+      traces.push(trace);
+    });
+    printStats(traces);
+
     // Finish the build to avoid leaking timer/fetch ref
     // Finish the build to avoid leaking timer/fetch ref
     await whyIsNodeRunning();
     await whyIsNodeRunning();
     process.exit(0);
     process.exit(0);

+ 3 - 3
Build/lib/parse-filter/domainlists.ts

@@ -28,11 +28,11 @@ export function processDomainListsWithPreload(
   const downloadPromise = fetchAssets(domainListsUrl, mirrors, true, allowEmptyRemote);
   const downloadPromise = fetchAssets(domainListsUrl, mirrors, true, allowEmptyRemote);
   const lineCb = includeAllSubDomain ? domainListLineCbIncludeAllSubdomain : domainListLineCb;
   const lineCb = includeAllSubDomain ? domainListLineCbIncludeAllSubdomain : domainListLineCb;
 
 
-  return (span: Span) => span.traceChildAsync(`process domainlist: ${domainListsUrl}`, async (span) => {
-    const filterRules = await span.traceChildPromise('download', downloadPromise);
+  return (span: Span) => span.traceChildAsync(`process domainlist: ${domainListsUrl}`, async (childSpan) => {
+    const filterRules = await childSpan.traceChildPromise('download', downloadPromise);
     const domainSets: string[] = [];
     const domainSets: string[] = [];
 
 
-    span.traceChildSync('parse domain list', () => {
+    childSpan.traceChildSync('parse domain list', () => {
       for (let i = 0, len = filterRules.length; i < len; i++) {
       for (let i = 0, len = filterRules.length; i < len; i++) {
         lineCb(filterRules[i], domainSets, domainListsUrl, fastNormalizeDomainWithoutWww);
         lineCb(filterRules[i], domainSets, domainListsUrl, fastNormalizeDomainWithoutWww);
       }
       }

+ 57 - 45
Build/trace/index.ts

@@ -4,8 +4,8 @@ import { basename, extname } from 'node:path';
 import process from 'node:process';
 import process from 'node:process';
 import picocolors from 'picocolors';
 import picocolors from 'picocolors';
 
 
-const SPAN_STATUS_START = 0;
-const SPAN_STATUS_END = 1;
+export const SPAN_STATUS_START = 0;
+export const SPAN_STATUS_END = 1;
 
 
 const spanTag = Symbol('span');
 const spanTag = Symbol('span');
 
 
@@ -16,12 +16,11 @@ export interface TraceResult {
   children: TraceResult[]
   children: TraceResult[]
 }
 }
 
 
-const rootTraceResult: TraceResult = {
-  name: 'root',
-  start: 0,
-  end: 0,
-  children: []
-};
+/** Pure data object — safe to transfer across Worker Thread boundaries. */
+export interface RawSpan {
+  traceResult: TraceResult,
+  status: typeof SPAN_STATUS_START | typeof SPAN_STATUS_END
+}
 
 
 export interface Span {
 export interface Span {
   [spanTag]: true,
   [spanTag]: true,
@@ -36,37 +35,23 @@ export interface Span {
   readonly traceResult: TraceResult
   readonly traceResult: TraceResult
 }
 }
 
 
-export function createSpan(name: string, parentTraceResult?: TraceResult): Span {
-  const start = performance.now();
-
-  let curTraceResult: TraceResult;
-
-  if (parentTraceResult == null) {
-    curTraceResult = rootTraceResult;
-  } else {
-    curTraceResult = {
-      name,
-      start,
-      end: 0,
-      children: []
-    };
-    parentTraceResult.children.push(curTraceResult);
-  }
-
-  let status: typeof SPAN_STATUS_START | typeof SPAN_STATUS_END = SPAN_STATUS_START;
+/**
+ * Wraps a serializable {@link RawSpan} with all span methods.
+ * Use this on a worker thread after receiving a {@link RawSpan} (or {@link TraceResult})
+ * transferred from another thread.
+ */
+export function makeSpan(rawSpan: RawSpan): Span {
+  const { traceResult } = rawSpan;
 
 
   const stop = (time?: number) => {
   const stop = (time?: number) => {
-    if (status === SPAN_STATUS_END) {
-      throw new Error(`span already stopped: ${name}`);
+    if (rawSpan.status === SPAN_STATUS_END) {
+      throw new Error(`span already stopped: ${traceResult.name}`);
     }
     }
-    const end = time ?? performance.now();
-
-    curTraceResult.end = end;
-
-    status = SPAN_STATUS_END;
+    traceResult.end = time ?? performance.now();
+    rawSpan.status = SPAN_STATUS_END;
   };
   };
 
 
-  const traceChild = (name: string) => createSpan(name, curTraceResult);
+  const traceChild = (name: string) => createSpan(name, traceResult);
 
 
   const span: Span = {
   const span: Span = {
     [spanTag]: true,
     [spanTag]: true,
@@ -82,7 +67,7 @@ export function createSpan(name: string, parentTraceResult?: TraceResult): Span
       span.stop();
       span.stop();
       return res;
       return res;
     },
     },
-    traceResult: curTraceResult,
+    traceResult,
     async tracePromise<T>(promise: Promise<T>): Promise<T> {
     async tracePromise<T>(promise: Promise<T>): Promise<T> {
       const res = await promise;
       const res = await promise;
       span.stop();
       span.stop();
@@ -97,18 +82,35 @@ export function createSpan(name: string, parentTraceResult?: TraceResult): Span
   return span;
   return span;
 }
 }
 
 
+export function createSpan(name: string, parentTraceResult?: TraceResult): Span {
+  const rawSpan: RawSpan = {
+    traceResult: {
+      name,
+      start: performance.now(),
+      end: 0,
+      children: []
+    },
+    status: SPAN_STATUS_START
+  };
+
+  parentTraceResult?.children.push(rawSpan.traceResult);
+
+  return makeSpan(rawSpan);
+}
+
 export const dummySpan = createSpan('dummy');
 export const dummySpan = createSpan('dummy');
 
 
 export function task(importMetaMain: boolean, importMetaPath: string) {
 export function task(importMetaMain: boolean, importMetaPath: string) {
-  return <T>(fn: (span: Span, onCleanup: (cb: () => Promise<void> | void) => void) => Promise<T>, customName?: string) => {
+  return (fn: (span: Span, onCleanup: (cb: () => Promise<void> | void) => void) => Promise<unknown>, customName?: string) => {
     const taskName = customName ?? basename(importMetaPath, extname(importMetaPath));
     const taskName = customName ?? basename(importMetaPath, extname(importMetaPath));
     let cleanup: () => Promise<void> | void = noop;
     let cleanup: () => Promise<void> | void = noop;
     const onCleanup = (cb: () => void) => {
     const onCleanup = (cb: () => void) => {
       cleanup = cb;
       cleanup = cb;
     };
     };
 
 
-    const innerSpan = createSpan(taskName);
     if (importMetaMain) {
     if (importMetaMain) {
+      const innerSpan = createSpan(taskName);
+
       process.on('uncaughtException', (error) => {
       process.on('uncaughtException', (error) => {
         console.error('Uncaught exception:', error);
         console.error('Uncaught exception:', error);
         process.exit(1);
         process.exit(1);
@@ -126,15 +128,26 @@ export function task(importMetaMain: boolean, importMetaPath: string) {
       });
       });
     }
     }
 
 
-    function run(span?: Span | null): Promise<T> {
-      return fn(span || innerSpan, onCleanup).finally(() => {
-        (span || innerSpan).stop();
+    let runSpan: Span;
+    async function run(parentSpan?: Span | null): Promise<TraceResult> {
+      if (parentSpan) {
+        runSpan = parentSpan.traceChild(taskName);
+      } else {
+        runSpan = createSpan(taskName);
+      }
+
+      try {
+        await fn(runSpan, onCleanup);
+      } finally {
+        runSpan.stop();
         cleanup();
         cleanup();
-      });
+      }
+
+      return runSpan.traceResult;
     }
     }
 
 
     return Object.assign(run, {
     return Object.assign(run, {
-      getInternalTraceResult: () => innerSpan.traceResult
+      getInternalTraceResult: () => runSpan.traceResult
     });
     });
   };
   };
 }
 }
@@ -159,8 +172,7 @@ export async function whyIsNodeRunning() {
 //   };
 //   };
 // };
 // };
 
 
-export function printTraceResult(traceResult: TraceResult = rootTraceResult) {
-  printStats(traceResult.children);
+export function printTraceResult(traceResult: TraceResult) {
   printTree(
   printTree(
     traceResult,
     traceResult,
     node => {
     node => {
@@ -206,7 +218,7 @@ function printTree(initialTree: TraceResult, printNode: (node: TraceResult, bran
   printBranch(initialTree, '', true, false);
   printBranch(initialTree, '', true, false);
 }
 }
 
 
-function printStats(stats: TraceResult[]): void {
+export function printStats(stats: TraceResult[]): void {
   const longestTaskName = Math.max(...stats.map(i => i.name.length));
   const longestTaskName = Math.max(...stats.map(i => i.name.length));
   const realStart = Math.min(...stats.map(i => i.start));
   const realStart = Math.min(...stats.map(i => i.start));
   const realEnd = Math.max(...stats.map(i => i.end));
   const realEnd = Math.max(...stats.map(i => i.end));