
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
readwrite-lock
Advanced tools
Read/Write locks on asynchronous code
npm install readwrite-lock
Readwrite lock rules:
Features
Nodejs is single threaded, and the code execution is never get interrupted inside an event loop, so locking is unnecessary? This is true ONLY IF your critical section can be executed inside a single event loop. However, if you have any async code inside your critical section (it can be simply triggered by any I/O operation, or timer), your critical logic will across multiple event loops, therefore it's not concurrency safe!
Consider the following code
redis.get('key', function(err, value){
redis.set('key', value * 2);
});
The above code simply multiply a redis key by 2. However, if two users run concurrency, the execution order may like this
user1: redis.get('key') -> 1
user2: redis.get('key') -> 1
user1: redis.set('key', 1 x 2) -> 2
user2: redis.set('key', 1 x 2) -> 2
Obviously it's not what you expected
With readwriteLock, you can easily write your async critical section
lock.acquireWrite('key', () => {
// Concurrency safe
return new Promise((resolve, reject) => {
redis.get('key', (err, value) => {
redis.set('key', value * 2, (err, value) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
});
}).then((result) => {
}).catch((err) => {
});
Read locks run concurrently, while write locks run exclusively. This is useful when working with multiple async IO calls to modify a resource. Consider the below example of how read write locks can help organize your async nodejs app. This is of course could be fixed using better coding rather than locks, it is only used as an example of what can happen when using asynchronous IO in NodeJS.
function concatHtml() {
return new Promise((resolve, reject) => {
htmlDownload("https://www.google.com", googleHtml => {
fs.writeFile('concat_html.txt', googleHtml, () => {
htmlDownload("https://www.github.com", githubHtml => {
fs.appendFile('concat_html.txt', githubHtml, () => resolve());
});
});
});
});
}
function readHtml() {
return new Promise((resolve, reject) => {
fs.readFile('concat_html.txt', result => resolve(result))
});
}
user1: concatHtml()
user2: readHtml() -> only googleHtml found in file
user3: readHtml() -> googleHtml + githubHtml found in file
With readwriteLock, you can make sure that read locks can run concurrently as long as no writes are queued up. Likewise, writes block all reads until they are completed.
lock.acquireWrite('key', () => {
// no other locks exist when this is running
return concatHtml();
}).then(result => {
}).catch(err => {
});
lock.acquireRead('key', () => {
// runs parallel with other read locks
return readHtml();
}).then(result => {
}).catch(err => {
});
lock.acquireRead('key', () => {
// runs parallel with other read locks
return readHtml();
}).then(result => {
}).catch(err => {
});
var ReadwriteLock = require('readwrite-lock');
var lock = new ReadwriteLock();
/**
* @param {String|Array} key resource key or keys to lock
* @param {function} fn execute function
* @param {Object} opts (optional) options
*/
lock.acquireRead(key, () => {
// critical section
// return value or promise
}, opts).then(() => {
// continue execution outside critical section
// NOTE: LOCK IS RELEASED AS SOON AS CRITICAL SECTION RETURNS
// there is no guaranteed order of this "then()" call
// compared to other recently released locks of same key
});
/**
* @param {String|Array} key resource key or keys to lock
* @param {function} fn execute function
* @param {Object} opts (optional) options
*/
lock.acquireWrite(key, () => {
// critical section
// return value or promise
}, opts).then(() => {
// continue execution outside critical section
// NOTE: LOCK IS RELEASED AS SOON AS CRITICAL SECTION RETURNS
// there is no guaranteed order of this "then()" call
// compared to other recently released locks of same key
});
lock.acquireRead(key, () => {
throw new Error('error');
}).catch(err => {
console.log(err.message); // output: error
});
lock.acquireWrite(key, () => {
throw new Error('error');
}).catch(err => {
console.log(err.message); // output: error
});
lock.acquireRead([key1, key2], fn)
.then(() => {
// no longer in critical section
})
.catch(err => {
console.log(err.message);
});
lock.acquireWrite([key1, key2], fn)
.then(() => {
// no longer in critical section
})
.catch(err => {
console.log(err.message);
});
// Specify timeout
var lock = new ReadwriteLock({timeout : 5000});
lock.acquireRead(key, () => {
// critical section will never be entered if timeout occurs
}).catch(err => {
// timed out error will be returned here if lock not acquired in given time
});
lock.acquireWrite(key, () => {
// critical section will never be entered if timeout occurs
}).catch(err => {
// timed out error will be returned here if lock not acquired in given time
});
// Set max pending tasks
var lock = new ReadwriteLock({maxPending : 1000});
lock.acquireRead(key, () => {
// critical section will never be entered if pending limit reached
}).catch(err => {
// too many pending tasks error will be returned here if lock not acquired in given time
});
lock.acquireWrite(key, () => {
// critical section will never be entered if pending limit reached
}).catch(err => {
// too many pending tasks error will be returned here if lock not acquired in given time
});
// Whether there is any running or pending async function
lock.isBusy();
// Use your own promise library instead of the global Promise variable
var lock = new ReadwriteLock({Promise : require('bluebird')}); // Bluebird
var lock = new ReadwriteLock({Promise : require('q').Promise}); // Q
See isse tracker.
MIT, see LICENSE
FAQs
Read/Write lock on asynchronous code
The npm package readwrite-lock receives a total of 1,002 weekly downloads. As such, readwrite-lock popularity was classified as popular.
We found that readwrite-lock demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.