com.gemstone.gemfire.internal.UniqueIdGenerator Maven / Gradle / Ivy
Show all versions of gemfire-core Show documentation
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.internal;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
/** UniqueIdGenerator is factory that will produce unique ids that fall
* in a range between 0 and numIds-1 inclusive.
* Obtained ids will not be reissued until they are released and until every
* other non-obtained id has been obtained first.
*
* Memory Use: this implementation allocates numIds/64 8-byte longs at construction
* time and keeps them allocated.
*
* Instances of this class are thread safe.
*
* @author Darrel
* @since 5.0.2
*/
public class UniqueIdGenerator {
/*
* BitSets are packed into arrays of "units." Currently a unit is a long,
* which consists of 64 bits, requiring 6 address bits. The choice of unit
* is determined purely by performance concerns.
*/
private final static int ADDRESS_BITS_PER_UNIT = 6;
private final static int BITS_PER_UNIT = 1 << ADDRESS_BITS_PER_UNIT;
private final static int BIT_INDEX_MASK = BITS_PER_UNIT - 1;
/**
* The units in this BitSet. The ith bit is stored in units[i/64] at
* bit position i % 64 (where bit position 0 refers to the least
* significant bit and 63 refers to the most significant bit).
*/
private final long units[];
/**
* The maximum id supported by this generator
*/
private final int MAX_ID;
/**
* The next id to return from obtain if it is available.
*/
private int ctr = 0;
/**
* Given a bit index return unit index containing it.
*/
private static int unitIndex(int bitIndex) {
return bitIndex >> ADDRESS_BITS_PER_UNIT;
}
/**
* Given a bit index, return a unit that masks that bit in its unit.
*/
private static long bit(int bitIndex) {
return 1L << (bitIndex & BIT_INDEX_MASK);
}
/**
* Sets the bit at the specified index to true
if it is not
* already in the set. Otherwise does nothing.
*
* @param bitIndex a bit index.
* @exception IndexOutOfBoundsException if the specified index is negative.
*/
private void setBit(int bitIndex) {
int unitIndex = unitIndex(bitIndex);
long bit = bit(bitIndex);
this.units[unitIndex] |= bit;
}
/**
* Sets the bit specified by the index to false
.
*
* @param bitIndex the index of the bit to be cleared.
*/
private void clearBit(int bitIndex) {
int unitIndex = unitIndex(bitIndex);
long bit = bit(bitIndex);
this.units[unitIndex] &= ~bit;
}
/*
* trailingZeroTable[i] is the number of trailing zero bits in the binary
* representation of i.
*/
private final static byte trailingZeroTable[] = {
-25, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
private static int trailingZeroCnt(long val) {
// Loop unrolled for performance
int byteVal = (int)val & 0xff;
if (byteVal != 0) {
return trailingZeroTable[byteVal];
}
byteVal = (int)(val >>> 8) & 0xff;
if (byteVal != 0) {
return trailingZeroTable[byteVal] + 8;
}
byteVal = (int)(val >>> 16) & 0xff;
if (byteVal != 0) {
return trailingZeroTable[byteVal] + 16;
}
byteVal = (int)(val >>> 24) & 0xff;
if (byteVal != 0) {
return trailingZeroTable[byteVal] + 24;
}
byteVal = (int)(val >>> 32) & 0xff;
if (byteVal != 0) {
return trailingZeroTable[byteVal] + 32;
}
byteVal = (int)(val >>> 40) & 0xff;
if (byteVal != 0) {
return trailingZeroTable[byteVal] + 40;
}
byteVal = (int)(val >>> 48) & 0xff;
if (byteVal != 0) {
return trailingZeroTable[byteVal] + 48;
}
byteVal = (int)(val >>> 56) & 0xff;
return trailingZeroTable[byteVal] + 56;
}
private static final long WORD_MASK = 0xffffffffffffffffL;
/**
* Returns the index of the first bit that is set to false
* that occurs on or after the specified starting index.
*
* @param fromIndex the index to start checking from (inclusive).
* @return the index of the next clear bit.
*/
private int nextClearBit(int fromIndex) {
int u = unitIndex(fromIndex);
int testIndex = (fromIndex & BIT_INDEX_MASK);
long unit = this.units[u] >> testIndex;
if (unit == (WORD_MASK >> testIndex)) {
testIndex = 0;
}
while((unit==WORD_MASK) && (u < this.units.length-1)) {
unit = this.units[++u];
}
if (unit == WORD_MASK) {
return -1; // could not find a clear bit
}
int result;
if (unit == 0) {
result = u * BITS_PER_UNIT + testIndex;
} else {
testIndex += trailingZeroCnt(~unit);
result = ((u * BITS_PER_UNIT) + testIndex);
}
if (result > MAX_ID || result < 0) {
return -1;
} else {
return result;
}
}
/**
* Creates unique id pool that has numIds in it.
*
* @param numIds the maximum number of ids
*/
public UniqueIdGenerator(int numIds) {
if (numIds <= 0) {
throw new IllegalArgumentException(LocalizedStrings.UniqueIdGenerator_NUMIDS_0.toLocalizedString());
}
this.units = new long[(unitIndex(numIds-1) + 1)];
this.MAX_ID = numIds-1;
this.ctr = 0;
}
/**
* Obtain an id form the pool of available ids and return it.
* @throws IllegalStateException if all ids have been obtained
*/
public int obtain() {
synchronized (this) {
int candidate = this.ctr;
int result = nextClearBit(candidate);
if (result == -1 && candidate != 0) {
// The following check will do some additional work by scanning
// the range [candidate..MAX_ID] again if it does not find a clear bit
// in the range [0..candidate-1] but it will only do this when
// we are going to throw an exception.
result = nextClearBit(0);
}
if (result == -1) {
throw new IllegalStateException(LocalizedStrings.UniqueIdGenerator_RAN_OUT_OF_MESSAGE_IDS.toLocalizedString());
} else {
setBit(result);
if (result == MAX_ID) {
this.ctr = 0;
} else {
this.ctr = result+1;
}
return result;
}
}
}
/**
* Release a previously obtained id allowing it to be obtained in the future.
*/
public void release(int id) {
if (id < 0) {
throw new IllegalArgumentException(LocalizedStrings.UniqueIdGenerator_NEGATIVE_ID_0.toLocalizedString(Integer.valueOf(id)));
} else if (id > this.MAX_ID) {
throw new IllegalArgumentException(LocalizedStrings.UniqueIdGenerator_ID_MAX_ID_0.toLocalizedString(Integer.valueOf(id)));
}
synchronized (this) {
clearBit(id);
}
}
}