X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.api.uuid%2Fsrc%2Forg%2Fargeo%2Fapi%2Fuuid%2FConcurrentTimeUuidState.java;h=8131df8eebbd0bfdb9dad16e9210b64b84a1ea88;hb=ee0a9f240a5da3a1437dda5abe7a1c46c5a3a8e9;hp=61f5b8304d3710713a500fa2fc7347ec81ae07c6;hpb=e846ef84146b66ae543c29c5f17b2991ff0f5973;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.api.uuid/src/org/argeo/api/uuid/ConcurrentTimeUuidState.java b/org.argeo.api.uuid/src/org/argeo/api/uuid/ConcurrentTimeUuidState.java index 61f5b8304..8131df8ee 100644 --- a/org.argeo.api.uuid/src/org/argeo/api/uuid/ConcurrentTimeUuidState.java +++ b/org.argeo.api.uuid/src/org/argeo/api/uuid/ConcurrentTimeUuidState.java @@ -1,9 +1,5 @@ package org.argeo.api.uuid; -import static java.lang.System.Logger.Level.DEBUG; -import static java.lang.System.Logger.Level.WARNING; - -import java.lang.System.Logger; import java.security.SecureRandom; import java.time.Clock; import java.time.Duration; @@ -15,15 +11,24 @@ import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicLong; +import org.argeo.api.uuid.UuidFactory.TimeUuidState; + /** * A simple base implementation of {@link TimeUuidState}, which maintains - * different clock sequences for each thread. + * different clock sequences for each thread, based on a specified range. This + * range is defined as clock_seq_hi (cf. RFC4122) and only clock_seq_low is + * dynamically allocated. It means that there can be at most 256 parallel clock + * sequences. If that limit is reached, the clock sequence which has not be used + * for the most time is reallocated to the new thread. It is assumed that the + * context where time uUIDs will be generated will often be using thread pools + * (e.g. {@link ForkJoinPool#commonPool(), http server, database access, etc.) + * and that such reallocation won't have to happen too often. */ -public class ConcurrentTimeUuidState implements TimeUuidState { - private final static Logger logger = System.getLogger(ConcurrentTimeUuidState.class.getName()); +public class ConcurrentTimeUuidState implements UuidFactory.TimeUuidState { +// private final static Logger logger = System.getLogger(ConcurrentTimeUuidState.class.getName()); /** The maximum possible value of the clocksequence. */ - private final static int MAX_CLOCKSEQUENCE = 16384; + private final static int MAX_CLOCKSEQUENCE = 0x3F00; private final ClockSequenceProvider clockSequenceProvider; private final ThreadLocal currentHolder; @@ -46,8 +51,8 @@ public class ConcurrentTimeUuidState implements TimeUuidState { // compute the start reference startInstant = Instant.now(this.clock); long nowVm = nowVm(); - Duration duration = Duration.between(TimeUuidState.GREGORIAN_START, startInstant); - startTimeStamp = durationToUuidTimestamp(duration) - nowVm; + Duration duration = Duration.between(TimeUuid.TIMESTAMP_ZERO, startInstant); + startTimeStamp = TimeUuid.durationToTimestamp(duration) - nowVm; clockSequenceProvider = new ClockSequenceProvider(secureRandom); @@ -100,8 +105,8 @@ public class ConcurrentTimeUuidState implements TimeUuidState { private long computeNow() { if (useClockForMeasurement) { - Duration duration = Duration.between(TimeUuidState.GREGORIAN_START, Instant.now(clock)); - return durationToUuidTimestamp(duration); + Duration duration = Duration.between(TimeUuid.TIMESTAMP_ZERO, Instant.now(clock)); + return TimeUuid.durationToTimestamp(duration); } else { return startTimeStamp + nowVm(); } @@ -111,10 +116,6 @@ public class ConcurrentTimeUuidState implements TimeUuidState { return System.nanoTime() / 100; } - private long durationToUuidTimestamp(Duration duration) { - return (duration.getSeconds() * 10000000 + duration.getNano() / 100); - } - @Override public long getClockSequence() { return currentHolder.get().clockSequence; @@ -125,10 +126,10 @@ public class ConcurrentTimeUuidState implements TimeUuidState { return currentHolder.get().lastTimestamp; } - public void reset(long nodeIdBase) { + protected void reset(long nodeIdBase, long range) { synchronized (clockSequenceProvider) { this.nodeIdBase = nodeIdBase; - clockSequenceProvider.reset(); + clockSequenceProvider.reset(range); clockSequenceProvider.notifyAll(); } } @@ -141,8 +142,7 @@ public class ConcurrentTimeUuidState implements TimeUuidState { @Override public long getMostSignificantBits() { long timestamp = useTimestamp(); - long mostSig = MOST_SIG_VERSION1 // base for version 1 UUID - | ((timestamp & 0xFFFFFFFFL) << 32) // time_low + long mostSig = UuidFactory.MOST_SIG_VERSION1 | ((timestamp & 0xFFFFFFFFL) << 32) // time_low | (((timestamp >> 32) & 0xFFFFL) << 16) // time_mid | ((timestamp >> 48) & 0x0FFFL);// time_hi_and_version return mostSig; @@ -151,7 +151,6 @@ public class ConcurrentTimeUuidState implements TimeUuidState { /* * INTERNAL CLASSSES */ - private class Holder { private long lastTimestamp; private long clockSequence; @@ -169,8 +168,6 @@ public class ConcurrentTimeUuidState implements TimeUuidState { private void setClockSequence(long clockSequence) { this.clockSequence = clockSequence; -// if (nodeIdBase == null) -// throw new IllegalStateException("Node id base is not initialised"); this.leastSig = nodeIdBase // already computed node base | (((clockSequence & 0x3F00) >> 8) << 56) // clk_seq_hi_res | ((clockSequence & 0xFF) << 48); // clk_seq_low @@ -184,9 +181,11 @@ public class ConcurrentTimeUuidState implements TimeUuidState { } private static class ClockSequenceProvider { - private int rangeSize = 256; - private volatile int min; - private volatile int max; + /** Set to an illegal value. */ + private long range = MAX_CLOCKSEQUENCE;// this is actually clk_seq_hi +// private int rangeSize = 256; + private volatile long min; + private volatile long max; private final AtomicLong counter = new AtomicLong(-1); private final SecureRandom secureRandom; @@ -195,12 +194,37 @@ public class ConcurrentTimeUuidState implements TimeUuidState { ClockSequenceProvider(SecureRandom secureRandom) { this.secureRandom = secureRandom; - reset(); +// reset(range); } - synchronized void reset() { - int min = secureRandom.nextInt(ConcurrentTimeUuidState.MAX_CLOCKSEQUENCE - rangeSize); - int max = min + rangeSize; + synchronized void reset(long range) { + // long min = secureRandom.nextInt(ConcurrentTimeUuidState.MAX_CLOCKSEQUENCE - + // rangeSize); + // long max = min + rangeSize; + + long min, max; + if (range >= 0) { + if (range > MAX_CLOCKSEQUENCE) + throw new IllegalArgumentException("Range " + Long.toHexString(range) + " is too big"); + long previousRange = this.range; + this.range = range & 0x3F00; + if (this.range != range) { + this.range = previousRange; + throw new IllegalArgumentException( + "Range is not properly formatted: " + range + " (0x" + Long.toHexString(range) + ")"); + } + + min = this.range; + max = min | 0xFF; + } else {// full range + this.range = range; + min = 0; + max = MAX_CLOCKSEQUENCE; + } + assert min == (int) min; + assert max == (int) max; + + // TODO rather use assertions if (min >= max) throw new IllegalArgumentException("Minimum " + min + " is bigger than maximum " + max); if (min < 0 || min > MAX_CLOCKSEQUENCE) @@ -215,21 +239,21 @@ public class ConcurrentTimeUuidState implements TimeUuidState { if (activeCount > getRangeSize()) throw new IllegalStateException( "There are too many holders for range [" + min + "," + max + "] : " + activeCount); - // reset the counter - counter.set(min); + + // reset the counter with a random value in range + long firstCount = min + secureRandom.nextInt(getRangeSize()); + counter.set(firstCount); + + // reset holders for (Holder holder : active) { // save old clocksequence? newClockSequence(holder); } } - private synchronized int getRangeSize() { - return rangeSize; - } - private synchronized void newClockSequence(Holder holder) { // Too many holders, we will remove the oldes ones - while (activeHolders.size() > rangeSize) { + while (activeHolders.size() > getRangeSize()) { long oldestTimeStamp = -1; Holder holderToRemove = null; holders: for (Holder h : activeHolders.keySet()) { @@ -247,18 +271,18 @@ public class ConcurrentTimeUuidState implements TimeUuidState { } assert holderToRemove != null; - long oldClockSequence = holderToRemove.clockSequence; +// long oldClockSequence = holderToRemove.clockSequence; holderToRemove.clockSequence = -1; activeHolders.remove(holderToRemove); - if (logger.isLoggable(WARNING)) - logger.log(WARNING, "Removed " + holderToRemove + ", oldClockSequence=" + oldClockSequence); +// if (logger.isLoggable(WARNING)) +// logger.log(WARNING, "Removed " + holderToRemove + ", oldClockSequence=" + oldClockSequence); } long newClockSequence = -1; int tryCount = 0;// an explicit exit condition do { tryCount++; - if (tryCount >= rangeSize) + if (tryCount >= getRangeSize()) throw new IllegalStateException("No more clock sequence available"); newClockSequence = counter.incrementAndGet(); @@ -272,9 +296,17 @@ public class ConcurrentTimeUuidState implements TimeUuidState { // TODO use an iterator to check the values holder.setClockSequence(newClockSequence); activeHolders.put(holder, newClockSequence); - if (logger.isLoggable(DEBUG)) - logger.log(DEBUG, - "New clocksequence " + newClockSequence + " for thread " + Thread.currentThread().getId()); +// if (logger.isLoggable(DEBUG)) { +// String clockDesc = range >= 0 ? Long.toHexString(newClockSequence & 0x00FF) +// : Long.toHexString(newClockSequence | 0x8000); +// String rangeDesc = Long.toHexString(min | 0x8000) + "-" + Long.toHexString(max | 0x8000); +// logger.log(DEBUG, "New clocksequence " + clockDesc + " for thread " + Thread.currentThread().getId() +// + " (in range " + rangeDesc + ")"); +// } + } + + private synchronized int getRangeSize() { + return (int) (max - min); } }