Fix a bug where AbstractConstant.compareTo() returns 0 for different constants
Related issue: #2354 Motivation: AbstractConstant.compareTo() can return 0 even if the specified constant object is not the same instance with 'this'. Modifications: - Compare the identityHashCode of constant first. If that fails, allocate a small direct buffer and use its memory address as a unique value. If the platform does not provide a way to get the memory address of a direct buffer, use a thread-local random value. - Signal cannot extend AbstractConstant. Use delegation. Result: It is practically impossible for AbstractConstant.compareTo() to return 0 for different constant objects.
This commit is contained in:
parent
410b5623ff
commit
e64644b21c
@ -15,6 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.util;
|
package io.netty.util;
|
||||||
|
|
||||||
|
import io.netty.util.internal.PlatformDependent;
|
||||||
|
import io.netty.util.internal.ThreadLocalRandom;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base implementation of {@link Constant}.
|
* Base implementation of {@link Constant}.
|
||||||
*/
|
*/
|
||||||
@ -22,6 +27,8 @@ public abstract class AbstractConstant<T extends AbstractConstant<T>> implements
|
|||||||
|
|
||||||
private final int id;
|
private final int id;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
private volatile long uniquifier;
|
||||||
|
private ByteBuffer directBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
@ -41,32 +48,64 @@ public abstract class AbstractConstant<T extends AbstractConstant<T>> implements
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String toString() {
|
||||||
|
return name();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int hashCode() {
|
public final int hashCode() {
|
||||||
return super.hashCode();
|
return super.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean equals(Object o) {
|
public final boolean equals(Object obj) {
|
||||||
return super.equals(o);
|
return super.equals(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int compareTo(T other) {
|
public final int compareTo(T o) {
|
||||||
if (this == other) {
|
if (this == o) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int returnCode = name.compareTo(other.name());
|
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||||
|
AbstractConstant<T> other = o;
|
||||||
|
int returnCode;
|
||||||
|
|
||||||
|
returnCode = hashCode() - other.hashCode();
|
||||||
if (returnCode != 0) {
|
if (returnCode != 0) {
|
||||||
return returnCode;
|
return returnCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ((Integer) id).compareTo(other.id());
|
long thisUV = uniquifier();
|
||||||
|
long otherUV = other.uniquifier();
|
||||||
|
if (thisUV < otherUV) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (thisUV > otherUV) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("failed to compare two different constants");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private long uniquifier() {
|
||||||
public final String toString() {
|
long uniquifier;
|
||||||
return name();
|
if ((uniquifier = this.uniquifier) == 0) {
|
||||||
|
synchronized (this) {
|
||||||
|
while ((uniquifier = this.uniquifier) == 0) {
|
||||||
|
if (PlatformDependent.hasUnsafe()) {
|
||||||
|
directBuffer = ByteBuffer.allocateDirect(1);
|
||||||
|
this.uniquifier = PlatformDependent.directBufferAddress(directBuffer);
|
||||||
|
} else {
|
||||||
|
directBuffer = null;
|
||||||
|
this.uniquifier = ThreadLocalRandom.current().nextLong();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniquifier;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,15 +45,13 @@ public final class Signal extends Error implements Constant<Signal> {
|
|||||||
return pool.valueOf(firstNameComponent, secondNameComponent);
|
return pool.valueOf(firstNameComponent, secondNameComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int id;
|
private final SignalConstant constant;
|
||||||
private final String name;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link Signal} with the specified {@code name}.
|
* Creates a new {@link Signal} with the specified {@code name}.
|
||||||
*/
|
*/
|
||||||
private Signal(int id, String name) {
|
private Signal(int id, String name) {
|
||||||
this.id = id;
|
constant = new SignalConstant(id, name);
|
||||||
this.name = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,12 +76,12 @@ public final class Signal extends Error implements Constant<Signal> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int id() {
|
public int id() {
|
||||||
return id;
|
return constant.id();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
return name;
|
return constant.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -102,16 +100,17 @@ public final class Signal extends Error implements Constant<Signal> {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int returnCode = name.compareTo(other.name());
|
return constant.compareTo(other.constant);
|
||||||
if (returnCode != 0) {
|
|
||||||
return returnCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ((Integer) id).compareTo(other.id());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name();
|
return name();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class SignalConstant extends AbstractConstant<SignalConstant> {
|
||||||
|
SignalConstant(int id, String name) {
|
||||||
|
super(id, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user