-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
150 lines (130 loc) · 3.69 KB
/
index.ts
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
/*!
* ThreadLocal in Node.js
* alias "Async Context Bound"
*/
import * as util from "util";
import * as fs from "fs";
import * as async_hooks from "async_hooks";
import * as Tree from "./lib/tree";
import { debugFile, debugConsole } from "./lib/util";
const { AsyncResource } = async_hooks;
const { Node } = Tree;
const SumeruResource = "Sumeru";
// 须弥
class Sumeru extends AsyncResource {
// create Sumeru async resource
public static async run(cb: Function) {
// emit init hook
const instance = new Sumeru();
await instance._run(cb);
instance.close();
}
constructor() {
super("Sumeru");
}
public async run(cb) {
await this._run(cb);
this.close();
}
private _run(callback) {
// call before hook
(this as any).emitBefore();
// exec async function, must return promise
const promise = callback();
// call after hook
(this as any).emitAfter();
return promise;
}
private close() {
// call destroy hook
(this as any).emitDestroy();
}
}
export class ThreadLocal {
public static invokerRoot: any;
public static asyncHooks: any;
private sumerus = new Map<number, Sumeru>();
constructor() {
try {
ThreadLocal.asyncHooks = async_hooks;
ThreadLocal.invokerRoot = new Node(async_hooks.executionAsyncId());
} catch (e) {
return;
}
const self = this;
function init(asyncId, type, triggerId, resource) {
const currentId = async_hooks.executionAsyncId();
// don't want the initial start TCPWRAP
if (currentId === 1 && type === "TCPWRAP") {
return;
}
// executionAsyncId() of 0 means that it is being executed from C++ with no JavaScript stack above it
if (triggerId === 0) {
return;
}
if (type === SumeruResource) {
debugFile("Sumeru", asyncId);
const sumeruNode = new Node(asyncId);
self.sumerus.set(asyncId, sumeruNode);
const parent = ThreadLocal.invokerRoot.search(triggerId);
if (parent) {
parent.appendChild(sumeruNode);
} else {
ThreadLocal.invokerRoot.appendChild(sumeruNode);
}
} else {
debugFile("other", asyncId, triggerId);
const parent = ThreadLocal.invokerRoot.search(triggerId);
if (parent) {
const sumeruNode = new Node(asyncId);
parent.appendChild(sumeruNode);
}
}
}
function destroy(asyncId) {
// debugFile(`del asyncId`, asyncId)
if (self.sumerus.has(asyncId)) {
// let semeruNode = self.sumerus.get(asyncId)
ThreadLocal.invokerRoot.removeChild(asyncId);
self.sumerus.delete(asyncId);
}
}
const hooks = {
destroy,
init,
};
const asyncHook = async_hooks.createHook(hooks);
asyncHook.enable();
}
public setProp(k, v) {
const currentId = ThreadLocal.asyncHooks.executionAsyncId();
let node = ThreadLocal.invokerRoot.search(currentId);
if (!node) {
node = ThreadLocal.invokerRoot.search(ThreadLocal.asyncHooks.triggerAsyncId());
}
if (!node) {
return console.error("[rockerjs/tls] can\'t find node");
}
node.data(k, v);
}
public getProp(k) {
const currentId = ThreadLocal.asyncHooks.executionAsyncId();
let node = ThreadLocal.invokerRoot.search(currentId);
if (!node) {
node = ThreadLocal.invokerRoot.search(ThreadLocal.asyncHooks.triggerAsyncId());
}
if (!node) {
return console.error("[rockerjs/tls] can\'t find node");
}
for (; !!node; node = node.parent) {
const re = node.data(k);
if (re) {
return re;
}
}
}
// entry
public async run(fn: Function) {
await Sumeru.run(fn);
}
}