-
Notifications
You must be signed in to change notification settings - Fork 38.9k
Description
I have a small miss-understanding about SimpleIdGenerator, that is a fairly trivial class:
public class SimpleIdGenerator implements IdGenerator {
private final AtomicLong mostSigBits = new AtomicLong(0);
private final AtomicLong leastSigBits = new AtomicLong(0);
@Override
public UUID generateId() {
long leastSigBits = this.leastSigBits.incrementAndGet();
if (leastSigBits == 0) {
this.mostSigBits.incrementAndGet();
}
return new UUID(this.mostSigBits.get(), leastSigBits);
}
}
The presence of AtomicLong hints into the fact that this is a thread-safe continuous incremented UUID, but it's not the case:
long leastSigBits = this.leastSigBits.incrementAndGet();
if (leastSigBits == 0) {
For the sake of the discussion let's suppose that currently leastSigBits holds a -1 (it has been incremented quite a lot, yes).
ThreadA does long leastSigBits = this.leastSigBits.incrementAndGet();, so it puts the value into 0 (-1 + 1 = 0); but before it does the check if (leastSigBits == 0), ThreadB did long leastSigBits = this.leastSigBits.incrementAndGet(); too, now on a value that is 0, so it put the value in 1. ThreadA does the check and sees a value of 1, that if statement is not entered and a such a duplicate UUID.
This is very far stretched and I have doubts it has ever impacted any users as for this to happen they would need to generate all the long range of IDs, which is highly highly improbable. Still, this code is wrong.
If this is suppose to provide thread-safe variant :
- document it as such
- fix the code
if this isn't supposed to be thread safe, simply dropping the un-necessary AtomicLong (with it's volatile overhead) is going to be a big performance gain.
Either way, I would be more than glad to fix this, if someone tells me the path I should be taking. Thank you.