like-thread 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.js +59 -23
  2. package/package.json +1 -1
  3. package/worker.js +15 -10
package/index.js CHANGED
@@ -53,10 +53,12 @@ module.exports = class Thread {
53
53
  this.isGenerator = !!entrypoint[2]
54
54
 
55
55
  this.worker = new Worker(WORKER_SCRIPT, { eval: true, workerData, argv: opts.argv || null })
56
+ this._online = promiseWithResolvers()
56
57
 
57
- this.id = 1
58
+ this.id = 0
58
59
  this.requests = new Map()
59
60
 
61
+ this.worker.on('online', this._onOnline.bind(this))
60
62
  this.worker.on('message', this._onMessage.bind(this))
61
63
 
62
64
  this.promise = new Promise((resolve, reject) => {
@@ -75,6 +77,8 @@ module.exports = class Thread {
75
77
  }
76
78
  }
77
79
 
80
+ this._online.resolve(false)
81
+
78
82
  reject(err)
79
83
  })
80
84
 
@@ -93,6 +97,8 @@ module.exports = class Thread {
93
97
  }
94
98
  }
95
99
 
100
+ this._online.resolve(false)
101
+
96
102
  const success = exitCode === 0 || exitCode === 130 || signal === 'SIGINT' || signal === 'SIGTERM' || signal === 'SIGHUP'
97
103
 
98
104
  if (success) {
@@ -102,6 +108,36 @@ module.exports = class Thread {
102
108
  }
103
109
  })
104
110
  })
111
+
112
+ this.promise.catch(noop)
113
+
114
+ this.closing = null
115
+
116
+ this.opened = false
117
+ this.opening = this.ready()
118
+ this.opening.then(() => {
119
+ this.opened = true
120
+ }).catch(noop)
121
+ }
122
+
123
+ async ready () {
124
+ if (this.opening) return this.opening
125
+
126
+ await this._online.promise
127
+ }
128
+
129
+ async close () {
130
+ if (this.closing) return this.closing
131
+
132
+ this.closing = Promise.resolve()
133
+
134
+ this.worker.postMessage({ command: 'exit' })
135
+
136
+ await this.promise.catch(noop)
137
+ }
138
+
139
+ _onOnline () {
140
+ this._online.resolve(true)
105
141
  }
106
142
 
107
143
  _onMessage (msg) {
@@ -111,12 +147,18 @@ module.exports = class Thread {
111
147
  return
112
148
  }
113
149
 
114
- // console.log('_onMessage', msg, !!req.iterator ? 'iterator' : 'promise')
115
-
116
150
  if (msg.error) {
117
151
  this.requests.delete(msg.id)
118
152
 
119
- const err = new Error(msg.error)
153
+ const err = new Error(msg.error.message)
154
+
155
+ if (msg.error.name) {
156
+ err.name = msg.error.name
157
+ }
158
+
159
+ if (typeof msg.error.code !== 'undefined') {
160
+ err.code = msg.error.code
161
+ }
120
162
 
121
163
  if (req.resolver) {
122
164
  req.resolver.reject(err)
@@ -156,11 +198,15 @@ module.exports = class Thread {
156
198
  }
157
199
 
158
200
  call (...args) {
201
+ if (this.closing) {
202
+ return Promise.reject(new Error('Thread is closed'))
203
+ }
204
+
159
205
  if (this.id === 0xffffffff) {
160
- this.id = 1
206
+ this.id = 0
161
207
  }
162
208
 
163
- const id = this.id++
209
+ const id = ++this.id
164
210
 
165
211
  const sab = new SharedArrayBuffer(4)
166
212
  const control = new Int32Array(sab)
@@ -239,19 +285,15 @@ module.exports = class Thread {
239
285
  return item
240
286
  },
241
287
 
242
- return: () => {
243
- iterator.closed = true
244
-
288
+ async return () {
245
289
  Atomics.store(control, 0, 2)
246
290
  Atomics.notify(control, 0, 1)
247
291
 
248
- // this.worker.postMessage({ id, command: 'stop' })
249
- // return
250
-
251
- // iterator.queue.length = 0
252
- // iterator.resolver.resolve()
292
+ try {
293
+ await iterator.resolver.promise
294
+ } catch {}
253
295
 
254
- return Promise.resolve({ done: true })
296
+ return { done: true }
255
297
  }
256
298
  }
257
299
  }
@@ -283,12 +325,6 @@ module.exports = class Thread {
283
325
  finally (onFinally) {
284
326
  return this.promise.finally(onFinally)
285
327
  }
286
-
287
- async close () {
288
- this.worker.postMessage({ command: 'exit' })
289
-
290
- await this.promise
291
-
292
- // await this.worker.terminate()
293
- }
294
328
  }
329
+
330
+ function noop () {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "like-thread",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "files": [
package/worker.js CHANGED
@@ -9,28 +9,28 @@ const functionArgs = entrypoint[4] || ''
9
9
 
10
10
  const fn = workerData.fn.replace(entrypoint[0], (isAsync ? 'async ' : '') + 'function ' + (isGenerator ? '* ' : '') + functionName + ' (' + (functionArgs) + ')')
11
11
 
12
+ // TODO: We can avoid re-creating the func inside the func by using prototypes
13
+ const source = fn + '\n\nreturn ' + functionName + '(...args)'
14
+
15
+ const wrap = new Function('require', '__dirname', '__filename', 'module', 'exports', 'parentPort', 'args', source) // eslint-disable-line no-new-func
16
+
12
17
  parentPort.on('message', async (msg) => {
13
18
  if (msg.command === 'exit') {
14
- process.exit(0)
19
+ process.exit()
15
20
  return
16
21
  }
17
22
 
18
23
  try {
19
- // TODO: Optimize by creating the function only once by fixing the args
20
- const source = fn + '\n\nmodule.exports = ' + functionName + '(...' + JSON.stringify(msg.args || []) + ')'
21
-
22
- const wrap = new Function('require', '__dirname', '__filename', 'module', 'exports', 'parentPort', source) // eslint-disable-line no-new-func
23
-
24
24
  const mod = { exports: {} }
25
25
 
26
- wrap(require, __dirname, __filename, mod, mod.exports, parentPort)
27
-
28
- const out = mod.exports
26
+ const out = wrap(require, __dirname, __filename, mod, mod.exports, parentPort, msg.args || [])
29
27
 
30
28
  if (isGenerator) {
31
29
  const control = new Int32Array(msg.sab)
32
30
  const it = out[Symbol.asyncIterator] ? out[Symbol.asyncIterator]() : out[Symbol.iterator]()
33
31
 
32
+ let end = false
33
+
34
34
  while (true) {
35
35
  while (Atomics.load(control, 0) === 0) {
36
36
  const res = Atomics.waitAsync(control, 0, 0)
@@ -51,6 +51,7 @@ parentPort.on('message', async (msg) => {
51
51
  Atomics.store(control, 0, 0)
52
52
 
53
53
  if (done) {
54
+ end = true
54
55
  parentPort.postMessage({ id: msg.id, done: true })
55
56
  break
56
57
  }
@@ -59,6 +60,10 @@ parentPort.on('message', async (msg) => {
59
60
  parentPort.postMessage({ id: msg.id, value, v: true })
60
61
  }
61
62
 
63
+ if (!end) {
64
+ parentPort.postMessage({ id: msg.id, done: true })
65
+ }
66
+
62
67
  return
63
68
  }
64
69
 
@@ -72,6 +77,6 @@ parentPort.on('message', async (msg) => {
72
77
 
73
78
  parentPort.postMessage({ id: msg.id, value: out, v: true })
74
79
  } catch (err) {
75
- parentPort.postMessage({ id: msg.id, error: err })
80
+ parentPort.postMessage({ id: msg.id, error: { name: err.name, code: err.code, message: err.message } })
76
81
  }
77
82
  })