1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { DbPool, getThreadPoolTraceBufferCacheKey, setThreadPoolTraceBuffer } from './SqlLite';
17
18class ConvertThread {
19  isCancelled: boolean = false;
20  id: number = -1;
21  taskMap: unknown = {};
22  name: string | undefined;
23  worker?: Worker;
24  busy: boolean = false;
25  constructor(worker: Worker) {
26    this.worker = worker;
27  }
28  uuid(): string {
29    // @ts-ignore
30    return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
31      (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
32    );
33  }
34
35  getConvertData(handler: (status: boolean, msg: string, results: Blob) => void): void {
36    this.busy = true;
37    let id = this.uuid();
38    // @ts-ignore
39    this.taskMap[id] = (res: unknown): void => {
40      // @ts-ignore
41      setThreadPoolTraceBuffer('1', res.buffer);
42      // @ts-ignore
43      handler(res.status, res.msg, res.results);
44    };
45    caches.match(getThreadPoolTraceBufferCacheKey('1')).then((resData) => {
46      if (resData) {
47        resData.arrayBuffer().then((buffer) => {
48          this.worker!.postMessage(
49            {
50              id: id,
51              action: 'getConvertData',
52              buffer: buffer!,
53            },
54            [buffer!]
55          );
56        });
57      }
58    });
59  }
60}
61
62class ConvertPool {
63  maxThreadNumber: number = 0;
64  works: Array<ConvertThread> = [];
65  progress: Function | undefined | null;
66  static data: Array<string> = [];
67  num = Math.floor(Math.random() * 10 + 1) + 20;
68  init = async (type: string): Promise<void> => {
69    // server
70    await this.close();
71    if (type === 'convert') {
72      this.maxThreadNumber = 1;
73    }
74    for (let i = 0; i < this.maxThreadNumber; i++) {
75      let thread: ConvertThread;
76      if (type === 'convert') {
77        thread = new ConvertThread(new Worker(new URL('./ConvertTraceWorker', import.meta.url)));
78      }
79      thread!.worker!.onmessage = (event: MessageEvent): void => {
80        thread.busy = false;
81        ConvertPool.data = event.data.results;
82        // @ts-ignore
83        if (Reflect.has(thread.taskMap, event.data.id)) {
84          if (event.data.results) {
85            // @ts-ignore
86            let fun = thread.taskMap[event.data.id];
87            if (fun) {
88              fun(event.data);
89            }
90            // @ts-ignore
91            Reflect.deleteProperty(thread.taskMap, event.data.id);
92          } else {
93            // @ts-ignore
94            let fun = thread.taskMap[event.data.id];
95            if (fun) {
96              fun([]);
97            }
98            // @ts-ignore
99            Reflect.deleteProperty(thread.taskMap, event.data.id);
100          }
101        }
102      };
103      thread!.worker!.onmessageerror = (e): void => {};
104      thread!.worker!.onerror = (e): void => {};
105      thread!.id = i;
106      thread!.busy = false;
107      this.works?.push(thread!);
108    }
109  };
110
111  clearCache = (): void => {
112    for (let i = 0; i < this.works.length; i++) {
113      let thread = this.works[i];
114      thread.getConvertData(() => {});
115    }
116  };
117
118  close = async (): Promise<void> => {
119    for (let i = 0; i < this.works.length; i++) {
120      let thread = this.works[i];
121      thread.worker!.terminate();
122    }
123    this.works.length = 0;
124  };
125
126  // @ts-ignore
127  submitWithName(
128    name: string,
129    handler: (status: boolean, msg: string, results: Blob) => void
130  ): ConvertThread | undefined {
131    let noBusyThreads = this.works;
132    let thread: ConvertThread | undefined;
133    if (noBusyThreads.length > 0) {
134      //取第一个空闲的线程进行任务
135      thread = noBusyThreads[0];
136      thread!.getConvertData(handler);
137    }
138    return thread;
139  }
140
141  isIdle(): boolean {
142    return this.works.every((it) => !it.busy);
143  }
144}
145
146export const convertPool = new ConvertPool();
147