From 007a3d4c7e0801b0e068d5e63074bea1c973cbf2 Mon Sep 17 00:00:00 2001 From: Leonid <117165489+LeonidPolukhin@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:37:07 +0300 Subject: [PATCH] fix: transfer from worker (#455) Co-authored-by: leonid.polukhin --- examples/webpack5/package-lock.json | 8 ++-- examples/webpack5/package.json | 2 +- examples/webpack5/src/App.tsx | 64 ++++++++++++++++++++------ examples/webpack5/src/worker/worker.ts | 23 ++++++++- src/generated/embeddedWorker.js | 2 +- src/worker.js | 4 +- 6 files changed, 79 insertions(+), 24 deletions(-) diff --git a/examples/webpack5/package-lock.json b/examples/webpack5/package-lock.json index 1427718a..569bcaa3 100644 --- a/examples/webpack5/package-lock.json +++ b/examples/webpack5/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "solid-js": "^1.8.7", - "workerpool": "8.0.0" + "workerpool": "^9.1.2" }, "devDependencies": { "@babel/core": "^7.23.5", @@ -6273,9 +6273,9 @@ } }, "node_modules/workerpool": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-8.0.0.tgz", - "integrity": "sha512-aoLtwqMXoYVA1JV+t8uCLo7sXkF4Q1Ijrn7954X2IVyysk2bv2Il7C9sVJH8xk9xJAL0FNgR+hPOhmvnMk/P5Q==" + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.1.2.tgz", + "integrity": "sha512-5wZwyy5lcqrakQQcjaYQgVCbMR3djwIFWXuD2EGk/o/9bL3bd2kRGNwF74Bhcf1CIkAIwoOMG82EVnA5JmVVNw==" }, "node_modules/wrappy": { "version": "1.0.2", diff --git a/examples/webpack5/package.json b/examples/webpack5/package.json index 0b115a6c..45fd542c 100644 --- a/examples/webpack5/package.json +++ b/examples/webpack5/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "solid-js": "^1.8.7", - "workerpool": "8.0.0" + "workerpool": "^9.1.2" }, "devDependencies": { "@babel/core": "^7.23.5", diff --git a/examples/webpack5/src/App.tsx b/examples/webpack5/src/App.tsx index 2ba7949d..e3095cdb 100644 --- a/examples/webpack5/src/App.tsx +++ b/examples/webpack5/src/App.tsx @@ -31,21 +31,57 @@ function App() { }; result.appendChild(a); }; + + const createArray = () => { + const size = parseInt(inputArraySize()); + const results = document.getElementById('arrayResults')!; + const result = document.createElement('div'); + results.appendChild(result); + const p = document.createElement('p')!; + const promise = pool.exec('createArray', [size], { on: function (array) { + const p = document.createElement('p'); + p.innerHTML = `Array of size ${array.buffer.byteLength} bytes is created in the worker.`; + result.appendChild(p); + }}).then(function (f) { + const p = document.createElement('p'); + if (f) { + p.innerHTML = 'Ok. Array has been transferred.'; + } + else { + p.innerHTML = 'Warning. Array has been cloned.'; + } + result.appendChild(p); + }).catch(function (error) { + result.innerHTML = `${error}`; + }); + + }; + const [inputValue, setInputValue] = createSignal('30') - return
- Calculate fibonacci: - setInputValue(e.target.value)} /> - - -

- Try entering values in the range of 10 to 50. - Verify that the browser stays responsive when working on a large calculation. - We have created 3 workers, so the worker pool will handle a maximum of three - tasks at a time. When exceeding this, tasks will be put in a queue. -

- -
-
+ const [inputArraySize, setArraySize] = createSignal('100') + + return
+
+

Calculate fibonacci:

+ setInputValue(e.target.value)} /> + + +

+ Try entering values in the range of 10 to 50. + Verify that the browser stays responsive when working on a large calculation. + We have created 3 workers, so the worker pool will handle a maximum of three + tasks at a time. When exceeding this, tasks will be put in a queue. +

+
+
+
+

Test transferring array from a worker:

+ Input array size: + setArraySize(e.target.value)} /> + +
+
+
} export default App diff --git a/examples/webpack5/src/worker/worker.ts b/examples/webpack5/src/worker/worker.ts index e5c7c69b..651ad4d6 100644 --- a/examples/webpack5/src/worker/worker.ts +++ b/examples/webpack5/src/worker/worker.ts @@ -1,4 +1,5 @@ -import workerpool from 'workerpool' +import { arrayBuffer } from 'stream/consumers'; +import workerpool, { worker } from 'workerpool' // a deliberately inefficient implementation of the fibonacci sequence function fibonacci(n: number): number { @@ -6,7 +7,25 @@ function fibonacci(n: number): number { return fibonacci(n - 2) + fibonacci(n - 1); } +// As ArrayBuffer.prototype.detached is a rather recent feature it is not used here. +function isDetached (buffer: ArrayBuffer) : boolean { + try { + const array = new Uint8Array (buffer); + return false; + } + catch (error) { + return true; + } +} + +function createArray(size: number) : boolean { + const array = size > 0 ? new Uint8Array(size) : new Uint8Array(); + workerpool.workerEmit(new workerpool.Transfer(array, [array.buffer])); + return isDetached(array.buffer); +} + // create a worker and register public functions workerpool.worker({ - fibonacci: fibonacci + fibonacci: fibonacci, + createArray: createArray }); diff --git a/src/generated/embeddedWorker.js b/src/generated/embeddedWorker.js index 3585848a..c124fdad 100644 --- a/src/generated/embeddedWorker.js +++ b/src/generated/embeddedWorker.js @@ -3,4 +3,4 @@ * This file is automatically generated, * changes made in this file will be overwritten. */ -module.exports = "!function(e,n){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=n():\"function\"==typeof define&&define.amd?define(n):(e=\"undefined\"!=typeof globalThis?globalThis:e||self).worker=n()}(this,(function(){\"use strict\";function e(n){return e=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e},e(n)}function n(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,\"default\")?e.default:e}var t={};var r=function(e,n){this.message=e,this.transfer=n};return function(n){var t=r,o={exit:function(){}};if(\"undefined\"!=typeof self&&\"function\"==typeof postMessage&&\"function\"==typeof addEventListener)o.on=function(e,n){addEventListener(e,(function(e){n(e.data)}))},o.send=function(e){postMessage(e)};else{if(\"undefined\"==typeof process)throw new Error(\"Script must be executed as a worker\");var i;try{i=require(\"worker_threads\")}catch(n){if(\"object\"!==e(n)||null===n||\"MODULE_NOT_FOUND\"!==n.code)throw n}if(i&&null!==i.parentPort){var s=i.parentPort;o.send=s.postMessage.bind(s),o.on=s.on.bind(s),o.exit=process.exit.bind(process)}else o.on=process.on.bind(process),o.send=function(e){process.send(e)},o.on(\"disconnect\",(function(){process.exit(1)})),o.exit=process.exit.bind(process)}function u(e){return Object.getOwnPropertyNames(e).reduce((function(n,t){return Object.defineProperty(n,t,{value:e[t],enumerable:!0})}),{})}function d(e){return e&&\"function\"==typeof e.then&&\"function\"==typeof e.catch}o.methods={},o.methods.run=function(e,n){var t=new Function(\"return (\"+e+\").apply(null, arguments);\");return t.apply(t,n)},o.methods.methods=function(){return Object.keys(o.methods)},o.terminationHandler=void 0,o.cleanupAndExit=function(e){var n=function(){o.exit(e)};if(!o.terminationHandler)return n();var t=o.terminationHandler(e);d(t)?t.then(n,n):n()};var f=null;o.on(\"message\",(function(e){if(\"__workerpool-terminate__\"===e)return o.cleanupAndExit(0);try{var n=o.methods[e.method];if(!n)throw new Error('Unknown method \"'+e.method+'\"');f=e.id;var r=n.apply(n,e.params);d(r)?r.then((function(n){n instanceof t?o.send({id:e.id,result:n.message,error:null},n.transfer):o.send({id:e.id,result:n,error:null}),f=null})).catch((function(n){o.send({id:e.id,result:null,error:u(n)}),f=null})):(r instanceof t?o.send({id:e.id,result:r.message,error:null},r.transfer):o.send({id:e.id,result:r,error:null}),f=null)}catch(n){o.send({id:e.id,result:null,error:u(n)})}})),o.register=function(e,n){if(e)for(var t in e)e.hasOwnProperty(t)&&(o.methods[t]=e[t]);n&&(o.terminationHandler=n.onTerminate),o.send(\"ready\")},o.emit=function(e){if(f){if(e instanceof t)return void o.send({id:f,isEvent:!0,payload:e.message},e.transfer);o.send({id:f,isEvent:!0,payload:e})}},n.add=o.register,n.emit=o.emit}(t),n(t)}));\n//# sourceMappingURL=worker.min.js.map\n"; +module.exports = "!function(e,n){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=n():\"function\"==typeof define&&define.amd?define(n):(e=\"undefined\"!=typeof globalThis?globalThis:e||self).worker=n()}(this,(function(){\"use strict\";function e(n){return e=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e},e(n)}function n(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,\"default\")?e.default:e}var t={};var r=function(e,n){this.message=e,this.transfer=n};return function(n){var t=r,o={exit:function(){}};if(\"undefined\"!=typeof self&&\"function\"==typeof postMessage&&\"function\"==typeof addEventListener)o.on=function(e,n){addEventListener(e,(function(e){n(e.data)}))},o.send=function(e,n){n?postMessage(e,n):postMessage(e)};else{if(\"undefined\"==typeof process)throw new Error(\"Script must be executed as a worker\");var i;try{i=require(\"worker_threads\")}catch(n){if(\"object\"!==e(n)||null===n||\"MODULE_NOT_FOUND\"!==n.code)throw n}if(i&&null!==i.parentPort){var s=i.parentPort;o.send=s.postMessage.bind(s),o.on=s.on.bind(s),o.exit=process.exit.bind(process)}else o.on=process.on.bind(process),o.send=function(e){process.send(e)},o.on(\"disconnect\",(function(){process.exit(1)})),o.exit=process.exit.bind(process)}function u(e){return Object.getOwnPropertyNames(e).reduce((function(n,t){return Object.defineProperty(n,t,{value:e[t],enumerable:!0})}),{})}function d(e){return e&&\"function\"==typeof e.then&&\"function\"==typeof e.catch}o.methods={},o.methods.run=function(e,n){var t=new Function(\"return (\"+e+\").apply(null, arguments);\");return t.apply(t,n)},o.methods.methods=function(){return Object.keys(o.methods)},o.terminationHandler=void 0,o.cleanupAndExit=function(e){var n=function(){o.exit(e)};if(!o.terminationHandler)return n();var t=o.terminationHandler(e);d(t)?t.then(n,n):n()};var f=null;o.on(\"message\",(function(e){if(\"__workerpool-terminate__\"===e)return o.cleanupAndExit(0);try{var n=o.methods[e.method];if(!n)throw new Error('Unknown method \"'+e.method+'\"');f=e.id;var r=n.apply(n,e.params);d(r)?r.then((function(n){n instanceof t?o.send({id:e.id,result:n.message,error:null},n.transfer):o.send({id:e.id,result:n,error:null}),f=null})).catch((function(n){o.send({id:e.id,result:null,error:u(n)}),f=null})):(r instanceof t?o.send({id:e.id,result:r.message,error:null},r.transfer):o.send({id:e.id,result:r,error:null}),f=null)}catch(n){o.send({id:e.id,result:null,error:u(n)})}})),o.register=function(e,n){if(e)for(var t in e)e.hasOwnProperty(t)&&(o.methods[t]=e[t]);n&&(o.terminationHandler=n.onTerminate),o.send(\"ready\")},o.emit=function(e){if(f){if(e instanceof t)return void o.send({id:f,isEvent:!0,payload:e.message},e.transfer);o.send({id:f,isEvent:!0,payload:e})}},n.add=o.register,n.emit=o.emit}(t),n(t)}));\n//# sourceMappingURL=worker.min.js.map\n"; diff --git a/src/worker.js b/src/worker.js index f7659216..c0a025e7 100644 --- a/src/worker.js +++ b/src/worker.js @@ -24,8 +24,8 @@ if (typeof self !== 'undefined' && typeof postMessage === 'function' && typeof a callback(message.data); }) }; - worker.send = function (message) { - postMessage(message); + worker.send = function (message, transfer) { + transfer ? postMessage(message, transfer) : postMessage (message); }; } else if (typeof process !== 'undefined') {