{"id":18576653,"url":"https://github.com/mmomtchev/gatedobject","last_synced_at":"2025-10-05T03:29:01.060Z","repository":{"id":104224472,"uuid":"269935832","full_name":"mmomtchev/GatedObject","owner":"mmomtchev","description":"A module for creating a transparent RPC interface for non-thread safe objects to be used in a multi-threaded environment","archived":false,"fork":false,"pushed_at":"2020-09-27T16:25:06.000Z","size":27,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-16T01:08:38.458Z","etag":null,"topics":["multi-threading","transparent-rpc-interface","worker-threads"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mmomtchev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-06-06T09:44:14.000Z","updated_at":"2020-09-27T16:25:09.000Z","dependencies_parsed_at":null,"dependency_job_id":"1b2e004a-0549-49c2-bbcc-537ab4506dc4","html_url":"https://github.com/mmomtchev/GatedObject","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mmomtchev/GatedObject","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmomtchev%2FGatedObject","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmomtchev%2FGatedObject/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmomtchev%2FGatedObject/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmomtchev%2FGatedObject/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mmomtchev","download_url":"https://codeload.github.com/mmomtchev/GatedObject/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmomtchev%2FGatedObject/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278403957,"owners_count":25981189,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["multi-threading","transparent-rpc-interface","worker-threads"],"created_at":"2024-11-06T23:25:54.281Z","updated_at":"2025-10-05T03:29:00.324Z","avatar_url":"https://github.com/mmomtchev.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":" # GatedObject\n\nA module for creating a transparent RPC interface for non-thread safe objects to be used in a multi-threaded environment\n  \nSupports blocking synchronous, asynchronous and non-blocking polling \n  \n**Only compatible with Node.js worker_threads at the moment**\n\n## Introduction\n\nI created this module to be able to use advanced data structures that can not be easily rendered multi-threading-capable. Keep in mind that this works through RPC so it is an order of magnitude slower than [SharedMap](https://github.com/mmomtchev/SharedMap) which was built from the ground-up for multi-threading. Its advantage is that it is completely transparent and supports (almost) all existing JS libraries.\n\nIt supports three modes of operation\n* synchronous, which is the most natural to use but it is slow and wasteful\n* asynchronous, which is much faster and returns Promises which are natural to use in JS\n* polling, which is unusual to use in JS, but can be very versatile and does not require async\n\nRun npm test to see how much ops/s you will get with each method in your case. Typical values on a recent Core i7 for an RPC containing a simple object with a few primitives types are about 40Kops/s for synchronous mode and more than 1Mops/s for asynchronous and polling mode.\n\nThe module supports gating primitive types as Date and String, but if this is what you need [objectbuffer](https://github.com/Bnaya/objectbuffer) which uses shared memory is a much better choice.\n\nThe current version of test.js (as well as the examples), make use of *transferList* in the *Worker* constructor which requires an up-to-date Node.js LTS [#32250](https://github.com/nodejs/node/issues/32250). The module itself should work on older versions.\n\n\n## Installation\n\n*(the 0.0.1 version is not yet published, clone from here if you want it)*\n\n```bash\nnpm install --save gatedobject\n```\n\n## Usage\n\nThese examples use [@mourner's](https://github.com/mourner) excellent [RBush](https://github.com/mourner/rbush) for which this module was initially created\n\n### Synchronous mode\n```js\nconst { IGNORE_RETURN, GatedObjectAsync, GatedObjectSync, GatedObjectPolling } = require('./index');\nconst { Worker, isMainThread, parentPort, workerData } = require('worker_threads');\n\nif (isMainThread) {\n    /**\n     * Create a new GatedObject in sync mode with the CJS module name and the class name\n     * class name should be null when the module itself is the class/constructor\n     */\n    const myRBush = new GatedObjectSync('rbush', null, 16);\n\n    /** \n     * Create a clone for a subthread \n     * Every thread requires a separate clone\n     */\n    const cloneRBush = myRBush.clone();\n\n    /* Create a subthread and pass the clone */\n    new Worker(__filename, {\n        workerData: { myRBush: cloneRBush },\n        /* Ask the JS designers why this must be explicit */\n        transferList: [cloneRBush.port]\n    });\n\n    /* Use like nothing happened */\n    myRBush.insert({ minX: 1, minY: 1, maxX: 10, maxY: 10, data: 'data1' });\n\n    /** \n     * All methods gain an additional optional first argument IGNORE_RETURN\n     * which allows you to avoid copying large return results when you ignore them\n     * RBush.insert() is an excellent example as it will return this (the whole data structure)\n     * so that operations can be chained like this RBush.insert().insert().insert()...\n     * In that particular case this will be recognized and a reference local to the\n     * thread will be returned, but there other cases you might need it\n     */\n    myRBush.insert(IGNORE_RETURN, { minX: 2, minY: 2, maxX: 5, maxY: 5, data: 'data2' });\n} else {\n    /**\n     * Import the clone\n     * You are allowed to import a sync object as an async or polling\n     * or every other possible combination\n     * You are even allowed to clone an object twice and to import\n     * it twice with different strategies\n     */\n    const myRBush = new GatedObjectSync(workerData.myRBush);\n\n    /* Use like nothing happened, all the data is here */\n    myRBush.insert({ minX: 4, minY: 4, maxX: 6, maxY: 6, data: 'data3' });\n    console.log(myRBush.all().length);\n}\n\n```\n\n### Asynchronous mode\n```js\nconst { GatedObjectAsync, GatedObjectSync, GatedObjectPolling } = require('./index');\nconst { Worker, isMainThread, parentPort, workerData } = require('worker_threads');\n\nif (isMainThread) {\n    /**\n     * Create a new GatedObject by passing code which returns\n     * If there was a Guinness entry for ugliest API, this would surely\n     * be a serious contender\n     */\n    const myRBush = new GatedObjectSync('return new(require(\"rbush\"))(16)');\n\n    /** \n     * Create a clone for a subthread \n     * Every thread requires a separate clone\n     */\n    const cloneRBush = myRBush.clone();\n\n    /* Create a subthread and pass the clone */\n    new Worker(__filename, {\n        workerData: { myRBush: cloneRBush },\n        /* Ask the JS designers why this must be explicit */\n        transferList: [cloneRBush.port]\n    });\n\n    /* All methods return a promise now */\n    myRBush.insert({ minX: 1, minY: 1, maxX: 10, maxY: 10, data: 'data1' })\n        .then(() =\u003e myRBush.insert({ minX: 2, minY: 2, maxX: 5, maxY: 5, data: 'data2' }))\n        .catch((e) =\u003e console.error(e));\n} else {\n    /**\n     * Import the clone\n     * You are allowed to import a sync object as an async or polling one\n     * or every other possible combination\n     * You are even allowed to clone an object twice and to import\n     * it twice with different strategies\n     */\n    const myRBush = new GatedObjectSync(workerData.myRBush);\n\n    /* Here nothing has changed, we are still in sync mode */\n    myRBush.insert({ minX: 4, minY: 4, maxX: 6, maxY: 6, data: 'data3' });\n    console.log(myRBush.all().length);\n}\n\n```\n\n\n### Polling mode\n```js\nconst { GatedObjectAsync, GatedObjectSync, GatedObjectPolling } = require('./index');\nconst { Worker, isMainThread, parentPort, workerData } = require('worker_threads');\n\nif (isMainThread) {\n    /**\n     * Create a new GatedObject in sync mode with the CJS module name and the class name\n     * class name should be null when the module itself is the class/constructor\n     */\n    const myRBush = new GatedObjectPolling('rbush', null, 16);\n\n    /** \n     * Create a clone for a subthread \n     * Every thread requires a separate clone\n     */\n    const cloneRBush = myRBush.clone();\n\n    /* Create a subthread and pass the clone */\n    new Worker(__filename, {\n        workerData: { myRBush: cloneRBush },\n        /* Ask the JS designers why this must be explicit */\n        transferList: [cloneRBush.port]\n    });\n\n    /**\n     * Now you must poll for a return value\n     * Everything is serialized, you must poll\n     * the same number of times you called the method\n     **/\n    myRBush.insert({ minX: 1, minY: 1, maxX: 10, maxY: 10, data: 'data1' });\n    /* continue your work */\n    r = myRBush.poll();\n    if (r === undefined)\n    /* continue your work */\n     {}\n    /* ok, now you need it, block waiting */\n    r = myRBush.poll(true)\n} else {\n    /**\n     * Import the clone\n     * You are allowed to import a sync object as an async or polling\n     * or every other possible combination\n     * You are even allowed to clone an object twice and to import\n     * it twice with different strategies\n     */\n    const myRBush = new GatedObjectAsync(workerData.myRBush);\n\n    /* Use with Promises */\n    myRBush.insert({ minX: 4, minY: 4, maxX: 6, maxY: 6, data: 'data3' })\n        .then(() =\u003e console.log(myRBush.all().length));\n        .catch((e) =\u003e console.error(e));\n}\n\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmmomtchev%2Fgatedobject","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmmomtchev%2Fgatedobject","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmmomtchev%2Fgatedobject/lists"}