Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

await async - race condition error in ESLint require-atomic-updates

The code below is causing a race condition when it is checked with ESLint:

  let match = false

  try {
    match = await something()
  } catch (err) {
    // do something
  }
  if (match === false) {
    // do something
  }

What is the better way of writing this block of code?

EDIT:

  let request = ctx.request.body || {}
  let password = request.password
  let match = false

  try {
    match = await bcrypt.compare(password, 'xxxxx')
  } catch (err) {
    ctx.throw(401, err)
  }
  if (match === false) {
    ctx.throw(401, 'invalid password')
  }

  ctx.body = {
    message: 'logged in ok'
  }

Error from ESLint:

Possible race condition: ctx.body might be reassigned based on an outdated value of ctx.body require-atomic-updates

like image 775
Run Avatar asked Jul 04 '19 18:07

Run


3 Answers

You can safely ignore the warning :)

That ESLint is meant to catch things like this:

 let value = 0;

async function race() {
  value += await Promise.resolve(1);
  console.log(value);
}

race(); race();

In this case, race memoizes value on the stack, awaits a tick, then writes back to value. As other code runs in the meantime, value could've been changed, and then the update might be off ... it is not atomic.

In your case however, you read from ctx.request.body and write to ctx.body, so there is no non atomic update. Also, there is probably no other middleware acccessing the same ctx at the same time, so there cant be any concurrent modifications. So in your case it is a false positive, it is even questionable that this in any way positive (it might be a bug in ESLint).

like image 173
Jonas Wilms Avatar answered Nov 20 '22 09:11

Jonas Wilms


I do not think this a bug. Let's assume your code snippet is included in an async function, namely doRequest.

As long as the ctx variable is defined outside of doRequest, the ctx.body assignment is in race condition. Because it is not possible to assure that the last value assigned to ctx.body belongs to the last call of doRequest.

I wrote a blog post about this race condition. There are 2 ways to avoid this warning

Method 1: move ctx into doRequest body, then return the ctx value at the end of the function.

Method 2: use promise-base-semaphore pattern

let ctxPromise

whenever you fire a request, do a call ctxPromise = doRequest().

like image 37
transang Avatar answered Nov 20 '22 09:11

transang


I realise it is a little bit late for this answer but just for any future users coming across this question, to disable this rule, in your .eslintrc.json or whichever relevant config you use, just specify:

"require-atomic-updates": "off"

like image 5
kyapwc Avatar answered Nov 20 '22 11:11

kyapwc