1

I'm using a Nodejs Api server and facing a special situation where a bunch of users notify me with a boolean indication, and only when all users sent me the indication, I'm then calling a method to do some job.

So for the example, I create a group of 5 connected users, and wait for their indications, which is sent using an Http Post message with an input boolean. so, on the server I hold an object as follows -

Group = {
   actionPerformed: false,
   userOne: false,
   userTwo: false,
   userThree: false,
   userFour: false,
   userFive: false
}

Upon receiveing a message from any of the following users, I update the relevant property, lets say, for userOne I set Group.userOne property to true. I then check if all other users have already send thier indication, so I perform the following test -

if (!Group.actionPerformed && 
    Group.userOne && 
    Group.userTwo && 
    Group.userOne && 
    Group.userThree && 
    Group.userFour && 
    Group.userFive) {
       Group.actionPerformed = true;
       //do something only once
}

Of course, I want to perform the above code in brackets only once therefore I want to avoid a race condition situation where the 2 last users send their indications on the exactly same time, and both will set their propery to true, and afterwards as checking the condition, the first user might check the condition - which will result with true, and before setting the actionPerformed to true, a thread switch might happen, and a second user will test the condition which will also result with true, then both user will enter the brackets.

So my question is, Is the described situation can only be solved with an atomic operation on the condition and Group.actionPerformed = true or, is there another solution, maybe more elegant ?

UPDATE - the above code is performed within a route async method -

router.route('/')
    .get(passport.authenticate('jwt', { session: false }), async (req, res, next) => {
   ....
   if (!Group.actionPerformed && 
        Group.userOne && 
        Group.userTwo && 
        Group.userOne && 
        Group.userThree && 
        Group.userFour && 
        Group.userFive) {
           Group.actionPerformed = true;
           //do something only once
    }
});

1 Answer 1

1

If you are only using a single NodeJS process, it is mono-threaded, so race conditions cannot occur in a single frame.

Another way to say it: When code executes to respond to an event, it is not interrupted.

You can see that if you enter an infinite loop in your server, the process will not respond to any other queries (there are no threads in JS).

Here are some references:

However, you can have race conditions when

  • Running multiple NodeJS processes (in different machines, or with NodeJS cluster module). => In that case, you cannot store the state in the NodeJS process memory

  • Performing any asynchronous work between setting the boolean and checking if they are all set (reading files, async/await, network, ...). => Change that behaviour

// This will always work, as js frames run to completion
async function toggle(userName) {
  Group[userName] = true;

  [...all the SYNCHRONOUS work you want...]

  if (!Group.actionPerformed && Group.userOne && ... && Group.userFive) {
       Group.actionPerformed = true;
       //do something only once
  }
}
// This may not work. A race condition is possible
async function toggle(userName) {
  Group[userName] = true;

  await database.get(somedocument); // this is asynchronous
  // the code below this line will not run in the same frame
  // so other javascript code may run in between

  if (!Group.actionPerformed && Group.userOne && ... && Group.userFive) {
       Group.actionPerformed = true;
       //do something only once
  }
}
// This may not work. A race condition is possible
async function toggle(userName) {
  Group[userName] = true;

  setTimeout(() => {
    if (!Group.actionPerformed && Group.userOne && ... && Group.userFive) {
       Group.actionPerformed = true;
       //do something only once
    }
  }, <any value>);
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for answering @Eloims! I updated my question to clarify the code is running inside an async route. I'm not sure I fully understand, are you saying that the condition and the next line - Group.actionPerformed = true, are guaranteed to run in a single frame although they are written inside an async route method ?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.