From what I read, race conditions occur when different threads try to change a shared variable, which can result in a value that's not possible with any serial order of execution of those threads.
But code in node.js runs in a single thread, so, does that mean code written in node.js is free of race conditions?
Yes, we can have race conditions in Node. js!
Race conditions are sometimes produced by data races, which occur when two threads concurrently target the same memory location and at least one is a write operation. Data races are easier to detect than race conditions because specific conditions are required for them to occur.
A race condition occurs when two threads access a shared variable at the same time. The first thread reads the variable, and the second thread reads the same value from the variable.
Luckily for you, Node. js comes with a garbage collector, and you don't need to manually manage memory allocation.
Yes, race conditions (in the sense of a shared resource having an inconsistent value due to order of events) can still happen anywhere that there's a point of suspension that could lead to other code being run (with threads its at any line), take for example this piece of async code that is entirely single threaded:
var accountBalance = 0; async function getAccountBalance() { // Suppose this was asynchronously from a database or something return accountBalance; }; async function setAccountBalance(value) { // Suppose this was asynchronously from a database or something accountBalance = value; }; async function increment(value, incr) { return value + incr; }; async function add$50() { var balance, newBalance; balance = await getAccountBalance(); newBalance = await increment(balance, 50); await setAccountBalance(newBalance); }; async function main() { var transaction1, transaction2; transaction1 = add$50(); transaction2 = add$50(); await transaction1; await transaction2; console.log('$' + await getAccountBalance()); // Can print either $50 or $100 // which it prints is dependent on what order // things arrived on the message queue, for this very simple // dummy implementation it actually prints $50 because // all values are added to the message queue immediately // so it actually alternates between the two async functions }; main();
This code has suspension points at every single await and as such could context switch between the two functions at a bad time producing "$50" rather than the expected "$100", this is essentially the same example as Wikipedia's example for Race Conditions in threads but with explicit points of suspension/re-entry.
Just like threads though you can solve such race conditions with things like a Lock (aka mutex). So we could prevent the above race condition in the same way as threads:
var accountBalance = 0; class Lock { constructor() { this._locked = false; this._waiting = []; } lock() { var unlock = () => { var nextResolve; if (this._waiting.length > 0) { nextResolve = this._waiting.pop(0); nextResolve(unlock); } else { this._locked = false; } }; if (this._locked) { return new Promise((resolve) => { this._waiting.push(resolve); }); } else { this._locked = true; return new Promise((resolve) => { resolve(unlock); }); } } } var account = new Lock(); async function getAccountBalance() { // Suppose this was asynchronously from a database or something return accountBalance; }; async function setAccountBalance(value) { // Suppose this was asynchronously from a database or something accountBalance = value; }; async function increment(value, incr) { return value + incr; }; async function add$50() { var unlock, balance, newBalance; unlock = await account.lock(); balance = await getAccountBalance(); newBalance = await increment(balance, 50); await setAccountBalance(newBalance); await unlock(); }; async function main() { var transaction1, transaction2; transaction1 = add$50(); transaction2 = add$50(); await transaction1; await transaction2; console.log('$' + await getAccountBalance()); // Now will always be $100 regardless }; main();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With