Comparing version 0.0.7 to 0.0.8
@@ -30,4 +30,5 @@ # Contributing to `worker_map` | ||
Guidelines are enforced using [ESLint](https://www.npmjs.com/package/eslint): | ||
```bash | ||
$ npm run lint | ||
``` | ||
``` |
@@ -14,3 +14,3 @@ const { WorkerMap } = require('worker_map'); | ||
setTimeout(() => { | ||
console.log(map.get('balance')); // 200 | ||
}, 50); | ||
console.log(map.get('balance')); // 200 | ||
}, 50); |
@@ -8,2 +8,2 @@ const { WorkerMap } = require('worker_map'); | ||
// The change will also be reflected in the main process | ||
map.set('balance', 200); | ||
map.set('balance', 200); |
@@ -5,5 +5,5 @@ const { WorkerMap } = require('worker_map'); | ||
const accountMap = new WorkerMap({ | ||
owner: 'Elon', | ||
wallets_usd_bank: 100, | ||
wallets_eth: 0, | ||
owner: 'Elon', | ||
wallets_usd_bank: 100, | ||
wallets_eth: 0, | ||
}); | ||
@@ -15,28 +15,27 @@ | ||
setInterval(() => { | ||
accountMap.set('wallets_usd_bank', accountMap.get('wallets_usd_bank') - 1); | ||
accountMap.set('wallets_eth', accountMap.get('wallets_eth') + 0.6); | ||
accountMap.set('wallets_doge', accountMap.get('wallets_doge') + 0.4); | ||
accountMap.set('wallets_usd_bank', accountMap.get('wallets_usd_bank') - 1); | ||
accountMap.set('wallets_eth', accountMap.get('wallets_eth') + 0.6); | ||
accountMap.set('wallets_doge', accountMap.get('wallets_doge') + 0.4); | ||
}, 50); | ||
new Worker('./worker', { | ||
workerData: { | ||
coin_name: 'doge', | ||
sharedAccount: accountMap.toSharedBuffer(), | ||
}, | ||
workerData: { | ||
coin_name: 'doge', | ||
sharedAccount: accountMap.toSharedBuffer(), | ||
}, | ||
}); | ||
new Worker('./worker', { | ||
workerData: { | ||
coin_name: 'eth', | ||
sharedAccount: accountMap.toSharedBuffer(), | ||
}, | ||
workerData: { | ||
coin_name: 'eth', | ||
sharedAccount: accountMap.toSharedBuffer(), | ||
}, | ||
}); | ||
setInterval(() => { | ||
console.log(accountMap.toObject()); | ||
console.log(accountMap.toObject()); | ||
if (accountMap.get('wallets_usd_bank') <= 1) { | ||
process.exit(0); | ||
} | ||
}, 30); | ||
if (accountMap.get('wallets_usd_bank') <= 1) { | ||
process.exit(0); | ||
} | ||
}, 30); |
{ | ||
"name": "worker_map", | ||
"description": "Tread-safe map structure for worker_threads.", | ||
"version": "0.0.7", | ||
"main": "./src/worker_map.js", | ||
"scripts": { | ||
"test": "node --test ./test", | ||
"lint": "eslint ./**/*.js" | ||
}, | ||
"devDependencies": { | ||
"eslint": "8.49.0", | ||
"eslint-config-airbnb-base": "15.0.0", | ||
"eslint-config-prettier": "9.0.0", | ||
"eslint-plugin-import": "2.28.1", | ||
"eslint-plugin-prettier": "5.0.0", | ||
"prettier": "3.0.3" | ||
}, | ||
"homepage": "https://github.com/nairihar/worker_map", | ||
"keywords": [ | ||
"thread", | ||
"worker", | ||
"worker_threads", | ||
"multithreading", | ||
"data sharing", | ||
"hash map", | ||
"hash table", | ||
"shared map", | ||
"worker share", | ||
"thread safe", | ||
"worker_map" | ||
], | ||
"author": "Nairi Harutyunyan", | ||
"license": "MIT" | ||
"name": "worker_map", | ||
"description": "Tread-safe map structure for worker_threads.", | ||
"version": "0.0.8", | ||
"main": "./src/worker_map.js", | ||
"scripts": { | ||
"test": "node --test ./test", | ||
"lint": "eslint ./**/*.js" | ||
}, | ||
"devDependencies": { | ||
"eslint": "8.49.0", | ||
"eslint-config-airbnb-base": "15.0.0" | ||
}, | ||
"homepage": "https://github.com/nairihar/worker_map", | ||
"keywords": [ | ||
"thread", | ||
"worker", | ||
"worker_threads", | ||
"multithreading", | ||
"data sharing", | ||
"hash map", | ||
"hash table", | ||
"shared map", | ||
"worker share", | ||
"thread safe", | ||
"worker_map" | ||
], | ||
"author": "Nairi Harutyunyan", | ||
"license": "MIT" | ||
} |
@@ -20,2 +20,3 @@ ![](https://img.shields.io/badge/dependencies-none-brightgreen.svg) | ||
## Basic Example | ||
First, let's create a simple hash map structure in main process, then create a worker thread and share the hash. | ||
@@ -25,9 +26,9 @@ | ||
// main.js | ||
const { Worker } = require('worker_threads'); | ||
const { WorkerMap } = require('worker_map'); | ||
const { Worker } = require("worker_threads"); | ||
const { WorkerMap } = require("worker_map"); | ||
const map = new WorkerMap(); | ||
map.set('balance', 100); // sync operation | ||
map.set("balance", 100); // sync operation | ||
new Worker('./worker.js', { | ||
new Worker("./worker.js", { | ||
workerData: { | ||
@@ -39,5 +40,4 @@ mapBuffer: map.toSharedBuffer(), | ||
setTimeout(() => { | ||
console.log(map.get('balance')); // 200 | ||
console.log(map.get("balance")); // 200 | ||
}, 50); | ||
``` | ||
@@ -49,10 +49,10 @@ | ||
// worker.js | ||
const { WorkerMap } = require('worker_map'); | ||
const { workerData } = require('worker_threads'); | ||
const { WorkerMap } = require("worker_map"); | ||
const { workerData } = require("worker_threads"); | ||
const map = new WorkerMap(workerData.mapBuffer); | ||
console.log(map.get('balance')); // 100 | ||
console.log(map.get("balance")); // 100 | ||
// The change will be reflected in the main process as well | ||
map.set('balance', 200); | ||
map.set("balance", 200); | ||
``` | ||
@@ -65,34 +65,72 @@ | ||
### `map.set(key, value)` | ||
``` | ||
```js | ||
map.set('name', 'John'); // true | ||
``` | ||
### `map.get(key):` | ||
``` | ||
```js | ||
const name = map.get('name'); // 'John' | ||
``` | ||
### `map.delete(key):` | ||
``` | ||
```js | ||
map.delete('name'); // true | ||
map.delete('something'); // false because it doesn't exist | ||
``` | ||
### `map.clear():` | ||
```js | ||
map.clear(); | ||
map.size(); // 0 | ||
``` | ||
### `map.has(key)` | ||
``` | ||
```js | ||
map.has('name'); // true | ||
map.has('country'); // false | ||
``` | ||
### `map.size()` | ||
``` | ||
```js | ||
map.has('size'); // 1 | ||
``` | ||
### `map.keys()` | ||
``` | ||
```js | ||
map.keys(); // [ 'name' ] | ||
``` | ||
### `map.entries()` | ||
```js | ||
for (const [ key, value ] of map.entries()) { | ||
console.log(`${key}: ${value}`); // name: 'John' | ||
} | ||
``` | ||
### `map.forEach()` | ||
```js | ||
map.forEach(function(key, value, map) { | ||
console.log(`${key}: ${value}`); // name: 'John' | ||
}); | ||
``` | ||
### `map.toSharedBuffer()` | ||
``` | ||
```js | ||
const buffer = map.toSharedBuffer(); | ||
const sameMap = new WorkerMap(buffer); | ||
``` | ||
### `map.toObject()` | ||
``` | ||
```js | ||
const mapObject = map.toObject(); // { ... } | ||
@@ -103,2 +141,3 @@ mapObject.name; // 'John' | ||
## Contributing | ||
See the [contributing guide](https://github.com/nairihar/worker_map/blob/main/CONTRIBUTING.md) for detailed instructions on how to get started with our project. | ||
@@ -108,5 +147,2 @@ | ||
- `map.clear()` | ||
- `map.entries()` | ||
- `map.forEach()` | ||
- Currently, when performing an action on the map, it temporarily locks the entire map, loads the necessary data, and then unlocks the map, allowing other threads to access it. However, this approach is suboptimal. It would be more efficient if we could lock only the specific portion of memory required for the particular operation. | ||
@@ -117,3 +153,4 @@ | ||
Please be aware of the following limitations when using our library: | ||
1. **Functions:** Function types are not supported. | ||
2. **NaN Values:** NaN values are not supported. |
@@ -16,3 +16,3 @@ const LOCKED = 1; | ||
throw new Error( | ||
"Mutex is in inconsistent state: unlock on unlocked Mutex.", | ||
'Mutex is in inconsistent state: unlock on unlocked Mutex.', | ||
); | ||
@@ -19,0 +19,0 @@ } |
@@ -1,12 +0,14 @@ | ||
const decoder = new TextDecoder("utf8"); | ||
const encoder = new TextEncoder("utf8"); | ||
const decoder = new TextDecoder('utf8'); | ||
const encoder = new TextEncoder('utf8'); | ||
const { lock, unlock, UNLCOKED } = require("./mutex"); | ||
const { lock, unlock, UNLCOKED } = require('./mutex'); | ||
const DEFAULT_OBJECT_BYTE_LENGTH = 4096; // total characters of stringified object (2^12) | ||
const MAX_OBJECT_BYTE_LENGTH = 4294967296; // max characters length for the stringified object (2^32) | ||
// total characters of stringified object (2^12) | ||
const DEFAULT_OBJECT_BYTE_LENGTH = 4096; | ||
// max characters length for the stringified object (2^32) | ||
const MAX_OBJECT_BYTE_LENGTH = 4294967296; | ||
const PLAIN_OBJECT = Symbol("PLAIN_OBJECT"); | ||
const VALUE_BUFFER = Symbol("VALUE_BUFFER"); | ||
const SHARED_BUFFER = Symbol("SHARED_BUFFER"); | ||
const PLAIN_OBJECT = Symbol('PLAIN_OBJECT'); | ||
const VALUE_BUFFER = Symbol('VALUE_BUFFER'); | ||
const SHARED_BUFFER = Symbol('SHARED_BUFFER'); | ||
@@ -74,3 +76,3 @@ function getSharedMemoryBuffer(sharedBuffer) { | ||
throw new Error( | ||
"No more space, create a new bigger shared object or use Node >= v20 to support auto grow.", | ||
'No more space, create a new bigger shared object or use Node >= v20 to support auto grow.', | ||
); | ||
@@ -126,3 +128,3 @@ } | ||
WorkerMap.prototype.set = function (key, value) { | ||
if (typeof key === "function" || typeof value === "function") { | ||
if (typeof key === 'function' || typeof value === 'function') { | ||
return false; | ||
@@ -150,3 +152,3 @@ } | ||
return true; | ||
return this; | ||
}; | ||
@@ -187,2 +189,12 @@ | ||
WorkerMap.prototype.clear = function () { | ||
const valueBuffer = this[VALUE_BUFFER]; | ||
lock(valueBuffer); | ||
saveObjectInBuffer({}, valueBuffer); | ||
unlock(valueBuffer); | ||
}; | ||
WorkerMap.prototype.size = function () { | ||
@@ -209,2 +221,20 @@ const valueBuffer = this[VALUE_BUFFER]; | ||
WorkerMap.prototype.entries = function () { | ||
const valueBuffer = this[VALUE_BUFFER]; | ||
const sharedObject = safeLoadSharedObject(valueBuffer); | ||
return Object.entries(sharedObject); | ||
}; | ||
WorkerMap.prototype.forEach = function (cb) { | ||
const valueBuffer = this[VALUE_BUFFER]; | ||
const sharedObject = safeLoadSharedObject(valueBuffer); | ||
const entries = Object.entries(sharedObject); | ||
entries.forEach(([key, value]) => { | ||
cb(key, value, this); | ||
}); | ||
}; | ||
WorkerMap.prototype.toSharedBuffer = function () { | ||
@@ -211,0 +241,0 @@ return this[SHARED_BUFFER]; |
@@ -1,60 +0,82 @@ | ||
const test = require("node:test"); | ||
const assert = require("node:assert"); | ||
const test = require('node:test'); | ||
const assert = require('node:assert'); | ||
const { WorkerMap } = require("../src/worker_map"); | ||
const { WorkerMap } = require('../src/worker_map'); | ||
let map = null; | ||
test("Create a map structure", () => { | ||
const values = { | ||
balance: 100, | ||
name: 'John', | ||
nameArmenian: 'Ջոն', | ||
lovesSport: true, | ||
numbers: [1, 2, 3, 4], | ||
details: { | ||
citizen: { | ||
country: 'Germany', | ||
passportYear: 1999, | ||
}, | ||
}, | ||
}; | ||
test('Create a map structure', () => { | ||
map = new WorkerMap(); | ||
}); | ||
test(".set()", () => { | ||
map.set("balance", 100); | ||
map.set("name", "John"); | ||
map.set("nameArmenian", "Ջոն"); | ||
map.set("lovesSport", true); | ||
map.set("numbers", [1, 2, 3, 4]); | ||
map.set("details", { | ||
citizen: { | ||
country: "Germany", | ||
passportYear: 1999, | ||
}, | ||
test('.set()', () => { | ||
Object.entries(values).forEach(([key, value]) => { | ||
const sameMap = map.set(key, value); | ||
assert.strictEqual(sameMap, map); | ||
}); | ||
}); | ||
test(".get()", () => { | ||
assert.strictEqual(map.get("balance"), 100); | ||
assert.strictEqual(map.get("name"), "John"); | ||
assert.strictEqual(map.get("nameArmenian"), "Ջոն"); | ||
assert.strictEqual(map.get("lovesSport"), true); | ||
assert.strictEqual(map.get("numbers").length, 4); | ||
assert.strictEqual(map.get("details").citizen.country, "Germany"); | ||
test('.get()', () => { | ||
assert.strictEqual(map.get('balance'), 100); | ||
assert.strictEqual(map.get('name'), 'John'); | ||
assert.strictEqual(map.get('nameArmenian'), 'Ջոն'); | ||
assert.strictEqual(map.get('lovesSport'), true); | ||
assert.strictEqual(map.get('numbers').length, 4); | ||
assert.strictEqual(map.get('details').citizen.country, 'Germany'); | ||
}); | ||
test(".has", () => { | ||
assert.strictEqual(map.has("balance"), true); | ||
assert.strictEqual(map.has("name"), true); | ||
assert.strictEqual(map.has("nameLatin"), false); | ||
assert.strictEqual(map.has("text"), false); | ||
assert.strictEqual(map.has("numbers"), true); | ||
test('.has', () => { | ||
assert.strictEqual(map.has('balance'), true); | ||
assert.strictEqual(map.has('name'), true); | ||
assert.strictEqual(map.has('nameLatin'), false); | ||
assert.strictEqual(map.has('text'), false); | ||
assert.strictEqual(map.has('numbers'), true); | ||
}); | ||
test(".delete", () => { | ||
assert.strictEqual(map.delete("balance"), true); | ||
assert.strictEqual(map.has("balance"), false); | ||
test('.delete', () => { | ||
assert.strictEqual(map.delete('balance'), true); | ||
assert.strictEqual(map.has('balance'), false); | ||
assert.strictEqual(map.delete("account"), false); | ||
assert.strictEqual(map.delete('account'), false); | ||
}); | ||
test(".size", () => { | ||
test('.size', () => { | ||
assert.strictEqual(map.size(), 5); | ||
}); | ||
test(".keys", () => { | ||
test('.keys', () => { | ||
assert.strictEqual(map.keys().length, 5); | ||
assert.strictEqual(map.keys()[0], "name"); | ||
assert.strictEqual(map.keys()[0], 'name'); | ||
}); | ||
test(".toSharedBuffer", () => { | ||
test('.entries', () => { | ||
const entries = map.entries(); | ||
entries.forEach(([key, value]) => { | ||
assert.deepEqual(values[key], value); | ||
}); | ||
}); | ||
test('.forEach', () => { | ||
map.forEach((key, value, sameMap) => { | ||
assert.deepEqual(values[key], value); | ||
assert.strictEqual(sameMap, map); | ||
}); | ||
}); | ||
test('.toSharedBuffer', () => { | ||
const sharedBuffer = map.toSharedBuffer(); | ||
@@ -66,7 +88,13 @@ const sameMap = new WorkerMap(sharedBuffer); | ||
test(".toObject", () => { | ||
test('.toObject', () => { | ||
const mapObject = map.toObject(); | ||
assert.strictEqual(mapObject.details.citizen.passportYear, 1999); | ||
assert.strictEqual(Object.keys(mapObject).length, 5); | ||
Object.entries(mapObject).forEach(([key, value]) => { | ||
assert.deepEqual(values[key], value); | ||
}); | ||
}); | ||
test('.clear', () => { | ||
map.clear(); | ||
assert.strictEqual(map.size(), 0); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
18895
2
331
149
14