This project demonstrates race conditions in Node.js when using child processes and shared state.
.
├── race.js # Demonstration of race conditions with child processes
└── README.md # This file
A race condition occurs when multiple processes or threads access shared data concurrently, and the final outcome depends on the timing of these accesses. In Node.js, this can happen when:
Node.js provides the child_process module to create child processes:
fork(): Creates a new Node.js processspawn(): Launches external commandsexec(): Executes commands in a shellUnderstanding race conditions is crucial for:
The current race.js file demonstrates a race condition where:
const { fork } = require("child_process");
let total = 0;
for (let i = 0; i < 2; i++) {
const child = fork("./race.js");
child.on("message", n => total += n);
}
setTimeout(() => {
console.log("Final total:", total);
}, 1000);
This code has several issues:
total variable is shared between parent and child processessetTimeout for synchronization is unreliableWhen run correctly, this should demonstrate:
node race.js
let counter = 0;
function increment() {
counter++; // Not atomic - race condition possible
}
let result;
asyncOperation1().then(() => result = "first");
asyncOperation2().then(() => result = "second");
// result could be either "first" or "second"
// Reading and writing files concurrently
fs.readFile("file.txt", (err, data) => {
// Process data
fs.writeFile("output.txt", processedData); // Race with other processes
});
Use atomic operations when available:
const { Mutex } = require('async-mutex');
const mutex = new Mutex();
async function safeIncrement() {
const release = await mutex.acquire();
try {
counter++;
} finally {
release();
}
}
Use proper inter-process communication:
// Parent process
const child = fork("child.js");
child.send({ type: "increment", value: 1 });
// Child process
process.on("message", (msg) => {
if (msg.type === "increment") {
// Handle increment safely
process.send(result);
}
});
const { Semaphore } = require('async-mutex');
const sem = new Semaphore(1);
async function criticalSection() {
const release = await sem.acquire();
try {
// Critical section code
} finally {
release();
}
}