like-thread 1.0.2 → 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 +55 -21
  2. package/package.json +1 -1
  3. package/worker.js +14 -9
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) {
@@ -104,6 +110,34 @@ module.exports = class Thread {
104
110
  })
105
111
 
106
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)
107
141
  }
108
142
 
109
143
  _onMessage (msg) {
@@ -113,12 +147,18 @@ module.exports = class Thread {
113
147
  return
114
148
  }
115
149
 
116
- // console.log('_onMessage', msg, !!req.iterator ? 'iterator' : 'promise')
117
-
118
150
  if (msg.error) {
119
151
  this.requests.delete(msg.id)
120
152
 
121
- 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
+ }
122
162
 
123
163
  if (req.resolver) {
124
164
  req.resolver.reject(err)
@@ -158,11 +198,15 @@ module.exports = class Thread {
158
198
  }
159
199
 
160
200
  call (...args) {
201
+ if (this.closing) {
202
+ return Promise.reject(new Error('Thread is closed'))
203
+ }
204
+
161
205
  if (this.id === 0xffffffff) {
162
- this.id = 1
206
+ this.id = 0
163
207
  }
164
208
 
165
- const id = this.id++
209
+ const id = ++this.id
166
210
 
167
211
  const sab = new SharedArrayBuffer(4)
168
212
  const control = new Int32Array(sab)
@@ -241,19 +285,15 @@ module.exports = class Thread {
241
285
  return item
242
286
  },
243
287
 
244
- return: () => {
245
- iterator.closed = true
246
-
288
+ async return () {
247
289
  Atomics.store(control, 0, 2)
248
290
  Atomics.notify(control, 0, 1)
249
291
 
250
- // this.worker.postMessage({ id, command: 'stop' })
251
- // return
252
-
253
- // iterator.queue.length = 0
254
- // iterator.resolver.resolve()
292
+ try {
293
+ await iterator.resolver.promise
294
+ } catch {}
255
295
 
256
- return Promise.resolve({ done: true })
296
+ return { done: true }
257
297
  }
258
298
  }
259
299
  }
@@ -285,12 +325,6 @@ module.exports = class Thread {
285
325
  finally (onFinally) {
286
326
  return this.promise.finally(onFinally)
287
327
  }
288
-
289
- async close () {
290
- this.worker.postMessage({ command: 'exit' })
291
-
292
- await this.promise.catch(noop)
293
- }
294
328
  }
295
329
 
296
330
  function noop () {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "like-thread",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "files": [
package/worker.js CHANGED
@@ -9,6 +9,11 @@ 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
19
  process.exit()
@@ -16,21 +21,16 @@ parentPort.on('message', async (msg) => {
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
  })