]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.api.acr/src/org/argeo/api/acr/uuid/ConcurrentTimeUuidState.java
Experiment with package level A2 metadata
[lgpl/argeo-commons.git] / org.argeo.api.acr / src / org / argeo / api / acr / uuid / ConcurrentTimeUuidState.java
1 package org.argeo.api.acr.uuid;
2
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;
8
9 /**
10 * A simple base implementation of {@link TimeUuidState}, which maintains
11 * different clock sequences for each thread.
12 */
13 public class ConcurrentTimeUuidState implements TimeUuidState {
14 // public final static ThreadLocal<Boolean> isTimeUuidThread = new ThreadLocal<>() {
15 //
16 // @Override
17 // protected Boolean initialValue() {
18 // return false;
19 // }
20 // };
21
22 /** The maximum possible value of the clocksequence. */
23 private final static int MAX_CLOCKSEQUENCE = 16384;
24
25 private final ThreadLocal<Holder> holder;
26
27 private final Instant startInstant;
28 /** A start timestamp to which {@link System#nanoTime()}/100 can be added. */
29 private final long startTimeStamp;
30
31 private final Clock clock;
32 private final boolean useClockForMeasurement;
33
34 private final SecureRandom secureRandom;
35
36 public ConcurrentTimeUuidState(SecureRandom secureRandom, Clock clock) {
37 useClockForMeasurement = clock != null;
38 this.clock = clock != null ? clock : Clock.systemUTC();
39
40 Objects.requireNonNull(secureRandom);
41 this.secureRandom = secureRandom;
42
43 // compute the start reference
44 startInstant = Instant.now(this.clock);
45 long nowVm = nowVm();
46 Duration duration = Duration.between(TimeUuidState.GREGORIAN_START, startInstant);
47 startTimeStamp = durationToUuidTimestamp(duration) - nowVm;
48
49 // initalise a state per thread
50 holder = new ThreadLocal<>() {
51
52 @Override
53 protected Holder initialValue() {
54 Holder value = new Holder();
55 value.lastTimestamp = startTimeStamp;
56 value.clockSequence = newClockSequence();
57 // isTimeUuidThread.set(true);
58 return value;
59 }
60 };
61 }
62
63 /*
64 * TIME OPERATIONS
65 */
66
67 public long useTimestamp() {
68
69 long previousTimestamp = holder.get().lastTimestamp;
70 long now = computeNow();
71
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)
78 break;
79 newClockSequence = newClockSequence();
80 }
81 if (newClockSequence != holder.get().clockSequence)
82 throw new IllegalStateException("Cannot change clock sequence");
83 holder.get().clockSequence = newClockSequence;
84 }
85
86 // very unlikely case where it took less than 100ns between both
87 if (previousTimestamp == now) {
88 try {
89 Thread.sleep(0, 100);
90 } catch (InterruptedException e) {
91 // silent
92 }
93 now = computeNow();
94 assert previousTimestamp != now;
95 }
96 holder.get().lastTimestamp = now;
97 return now;
98 }
99
100 protected long computeNow() {
101 if (useClockForMeasurement) {
102 Duration duration = Duration.between(TimeUuidState.GREGORIAN_START, Instant.now(clock));
103 return durationToUuidTimestamp(duration);
104 } else {
105 return startTimeStamp + nowVm();
106 }
107 }
108
109 private long nowVm() {
110 return System.nanoTime() / 100;
111 }
112
113 protected long durationToUuidTimestamp(Duration duration) {
114 return (duration.getSeconds() * 10000000 + duration.getNano() / 100);
115 }
116
117 /*
118 * STATE OPERATIONS
119 */
120
121 protected long newClockSequence() {
122 return secureRandom.nextInt(ConcurrentTimeUuidState.MAX_CLOCKSEQUENCE);
123 }
124
125 /*
126 * ACCESSORS
127 */
128
129 // @Override
130 // public byte[] getNodeId() {
131 // byte[] arr = new byte[6];
132 // System.arraycopy(holder.get().nodeId, 0, arr, 0, 6);
133 // return arr;
134 // }
135
136 @Override
137 public long getClockSequence() {
138 return holder.get().clockSequence;
139 }
140 }