Fix race condition when removing changeId/accountId locks

IIUC, the intention is to remove lock from the Map when the lock count
reaches zero.

There was a small window of vulnerability which could cause a lock to
be removed when the lock count was larger than zero:

After the zero condition check was done:

  if (changeIdLock.decrementAndGet() == 0) {
    removeChangeIdLock(id);
  }

and just before the removeChangeIdLock(id) was called, another thread
could have just finished:

  AtomicInteger changeIdLock = getAndIncrementChangeIdLock(id);

for the same change id and increased the count to one. The first thread
would proceed with the call to removeChangeIdLock and remove the entry
from the Map, although the count was 1.

A third thread, trying to index the same change, would end-up creating a
new entry in the Map with the new value and would afterwards
acquire lock on it:

  synchronized (changeIdLock) {
    ...
  }

during the time another thread is indexing the same change.

To fix the issue, check that the condition for removing the entry still
holds after locking the whole Map.

Change-Id: I2388ce1c9be27ea7f7b10eeb704974f418f1e795
2 files changed
tree: 7e4ded39b13af473e37184f9dd9ba6a19ba83540
  1. .buckconfig
  2. .gitignore
  3. .mailmap
  4. BUCK
  5. lib/
  6. src/