1 package org
.argeo
.api
.acr
.uuid
;
3 import java
.security
.SecureRandom
;
4 import java
.time
.Clock
;
5 import java
.time
.Duration
;
6 import java
.time
.Instant
;
7 import java
.util
.Objects
;
10 * A simple base implementation of {@link TimeUuidState}, which maintains
11 * different clock sequences for each thread.
13 public class ConcurrentTimeUuidState
implements TimeUuidState
{
14 // public final static ThreadLocal<Boolean> isTimeUuidThread = new ThreadLocal<>() {
17 // protected Boolean initialValue() {
22 /** The maximum possible value of the clocksequence. */
23 private final static int MAX_CLOCKSEQUENCE
= 16384;
25 private final ThreadLocal
<Holder
> holder
;
27 private final Instant startInstant
;
28 /** A start timestamp to which {@link System#nanoTime()}/100 can be added. */
29 private final long startTimeStamp
;
31 private final Clock clock
;
32 private final boolean useClockForMeasurement
;
34 private final SecureRandom secureRandom
;
36 public ConcurrentTimeUuidState(SecureRandom secureRandom
, Clock clock
) {
37 useClockForMeasurement
= clock
!= null;
38 this.clock
= clock
!= null ? clock
: Clock
.systemUTC();
40 Objects
.requireNonNull(secureRandom
);
41 this.secureRandom
= secureRandom
;
43 // compute the start reference
44 startInstant
= Instant
.now(this.clock
);
46 Duration duration
= Duration
.between(TimeUuidState
.GREGORIAN_START
, startInstant
);
47 startTimeStamp
= durationToUuidTimestamp(duration
) - nowVm
;
49 // initalise a state per thread
50 holder
= new ThreadLocal
<>() {
53 protected Holder
initialValue() {
54 Holder value
= new Holder();
55 value
.lastTimestamp
= startTimeStamp
;
56 value
.clockSequence
= newClockSequence();
57 // isTimeUuidThread.set(true);
67 public long useTimestamp() {
69 long previousTimestamp
= holder
.get().lastTimestamp
;
70 long now
= computeNow();
72 // rare case where we are sooner
73 // (e.g. if system time has changed in between and we use the clock)
74 if (previousTimestamp
> now
) {
75 long newClockSequence
= newClockSequence();
76 for (int i
= 0; i
< 64; i
++) {
77 if (newClockSequence
!= holder
.get().clockSequence
)
79 newClockSequence
= newClockSequence();
81 if (newClockSequence
!= holder
.get().clockSequence
)
82 throw new IllegalStateException("Cannot change clock sequence");
83 holder
.get().clockSequence
= newClockSequence
;
86 // very unlikely case where it took less than 100ns between both
87 if (previousTimestamp
== now
) {
90 } catch (InterruptedException e
) {
94 assert previousTimestamp
!= now
;
96 holder
.get().lastTimestamp
= now
;
100 protected long computeNow() {
101 if (useClockForMeasurement
) {
102 Duration duration
= Duration
.between(TimeUuidState
.GREGORIAN_START
, Instant
.now(clock
));
103 return durationToUuidTimestamp(duration
);
105 return startTimeStamp
+ nowVm();
109 private long nowVm() {
110 return System
.nanoTime() / 100;
113 protected long durationToUuidTimestamp(Duration duration
) {
114 return (duration
.getSeconds() * 10000000 + duration
.getNano() / 100);
121 protected long newClockSequence() {
122 return secureRandom
.nextInt(ConcurrentTimeUuidState
.MAX_CLOCKSEQUENCE
);
130 // public byte[] getNodeId() {
131 // byte[] arr = new byte[6];
132 // System.arraycopy(holder.get().nodeId, 0, arr, 0, 6);
137 public long getClockSequence() {
138 return holder
.get().clockSequence
;