Skip to content

Commit dcad985

Browse files
committed
TSpace: simplifying usage of monotonic nanoTime()
1 parent d9bc510 commit dcad985

File tree

2 files changed

+77
-74
lines changed

2 files changed

+77
-74
lines changed

jpos/src/main/java/org/jpos/space/TSpace.java

Lines changed: 55 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* jPOS Project [http://jpos.org]
3-
* Copyright (C) 2000-2021 jPOS Software SRL
3+
* Copyright (C) 2000-2023 jPOS Software SRL
44
*
55
* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU Affero General Public License as
@@ -17,11 +17,10 @@
1717
*/
1818

1919
package org.jpos.space;
20-
import org.jpos.util.Loggeable;
2120

21+
import org.jpos.util.Loggeable;
2222
import java.io.PrintStream;
2323
import java.io.Serializable;
24-
import java.time.Duration;
2524
import java.util.*;
2625
import java.util.concurrent.TimeUnit;
2726

@@ -40,8 +39,9 @@ public class TSpace<K,V> implements LocalSpace<K,V>, Loggeable, Runnable {
4039
private static final long GCLONG = 60*1000;
4140
private static final long NRD_RESOLUTION = 500L;
4241
private static final int MAX_ENTRIES_IN_DUMP = 1000;
42+
private static final long ONE_MILLION = 1_000_000L; // multiplier millis --> nanos
4343
private final Set[] expirables;
44-
private Duration lastLongGC = Duration.ofNanos(System.nanoTime());
44+
private long lastLongGC = System.nanoTime();
4545

4646
public TSpace () {
4747
super();
@@ -70,7 +70,7 @@ public void out (K key, V value, long timeout) {
7070
throw new NullPointerException ("key=" + key + ", value=" + value);
7171
Object v = value;
7272
if (timeout > 0) {
73-
v = new Expirable (value, Duration.ofNanos(System.nanoTime()).plus(Duration.ofMillis(timeout)));
73+
v = new Expirable (value, System.nanoTime() + (timeout * ONE_MILLION));
7474
}
7575
synchronized (this) {
7676
List l = getList(key);
@@ -112,18 +112,18 @@ public synchronized V in (Object key) {
112112

113113
@Override
114114
public synchronized V in (Object key, long timeout) {
115-
Object obj;
116-
Duration to = Duration.ofMillis(timeout);
117-
Duration now = Duration.ofNanos(System.nanoTime());
118-
Duration duration;
119-
while ((obj = inp (key)) == null &&
120-
to.compareTo(duration = Duration.ofNanos(System.nanoTime()).minus(now)) > 0)
115+
V obj;
116+
long now = System.nanoTime();
117+
long to = now + timeout * ONE_MILLION;
118+
long waitFor;
119+
while ( (obj = inp (key)) == null &&
120+
(waitFor = (to - System.nanoTime())) >= 0 )
121121
{
122122
try {
123-
this.wait (Math.max(to.minus(duration).toMillis(), 1L));
123+
this.wait(Math.max(waitFor / ONE_MILLION, 1L));
124124
} catch (InterruptedException e) { }
125125
}
126-
return (V) obj;
126+
return obj;
127127
}
128128

129129
@Override
@@ -139,18 +139,18 @@ public synchronized V rd (Object key) {
139139

140140
@Override
141141
public synchronized V rd (Object key, long timeout) {
142-
Object obj;
143-
Duration to = Duration.ofMillis(timeout);
144-
Duration now = Duration.ofNanos(System.nanoTime());
145-
Duration duration;
146-
while ((obj = rdp (key)) == null &&
147-
to.compareTo(duration = Duration.ofNanos(System.nanoTime()).minus(now)) > 0)
142+
V obj;
143+
long now = System.nanoTime();
144+
long to = now + (timeout * ONE_MILLION);
145+
long waitFor;
146+
while ( (obj = rdp (key)) == null &&
147+
(waitFor = (to - System.nanoTime())) >= 0 )
148148
{
149149
try {
150-
this.wait (Math.max(to.minus(duration).toMillis(), 1L));
150+
this.wait(Math.max(waitFor / ONE_MILLION, 1L));
151151
} catch (InterruptedException e) { }
152152
}
153-
return (V) obj;
153+
return obj;
154154
}
155155

156156
@Override
@@ -164,18 +164,19 @@ public synchronized void nrd (Object key) {
164164

165165
@Override
166166
public synchronized V nrd (Object key, long timeout) {
167-
Object obj;
168-
Duration to = Duration.ofMillis(timeout);
169-
Duration now = Duration.ofNanos(System.nanoTime());
170-
Duration duration;
171-
while ((obj = rdp (key)) != null &&
172-
to.compareTo(duration = Duration.ofNanos(System.nanoTime()).minus(now)) > 0)
167+
V obj;
168+
long now = System.nanoTime();
169+
long to = now + (timeout * ONE_MILLION);
170+
long waitFor;
171+
while ( (obj = rdp (key)) != null &&
172+
(waitFor = (to - System.nanoTime())) >= 0 )
173173
{
174174
try {
175-
this.wait (Math.min(NRD_RESOLUTION, Math.max(to.minus(duration).toMillis(), 1L)));
175+
this.wait(Math.min(NRD_RESOLUTION,
176+
Math.max(waitFor / ONE_MILLION, 1L)));
176177
} catch (InterruptedException ignored) { }
177178
}
178-
return (V) obj;
179+
return obj;
179180
}
180181

181182
@Override
@@ -189,9 +190,9 @@ public void run () {
189190

190191
public void gc () {
191192
gc(0);
192-
if (Duration.ofMillis(GCLONG).compareTo(Duration.ofNanos(System.nanoTime()).minus(lastLongGC)) > 0) {
193+
if (System.nanoTime() - lastLongGC > GCLONG) {
193194
gc(1);
194-
lastLongGC = Duration.ofNanos(System.nanoTime());
195+
lastLongGC = System.nanoTime();
195196
}
196197
}
197198

@@ -339,7 +340,7 @@ public void push (K key, V value, long timeout) {
339340
throw new NullPointerException ("key=" + key + ", value=" + value);
340341
Object v = value;
341342
if (timeout > 0) {
342-
v = new Expirable (value, Duration.ofNanos(System.nanoTime()).plus(Duration.ofMillis(timeout)));
343+
v = new Expirable (value, System.nanoTime() + (timeout * ONE_MILLION));
343344
}
344345
synchronized (this) {
345346
List l = getList(key);
@@ -376,7 +377,7 @@ public void put (K key, V value, long timeout) {
376377
throw new NullPointerException ("key=" + key + ", value=" + value);
377378
Object v = value;
378379
if (timeout > 0) {
379-
v = new Expirable (value, Duration.ofNanos(System.nanoTime()).plus(Duration.ofMillis(timeout)));
380+
v = new Expirable (value, System.nanoTime() + (timeout * ONE_MILLION));
380381
}
381382
synchronized (this) {
382383
List l = new LinkedList();
@@ -402,15 +403,15 @@ public boolean existAny (K[] keys) {
402403

403404
@Override
404405
public boolean existAny (K[] keys, long timeout) {
405-
Duration to = Duration.ofMillis(timeout);
406-
Duration now = Duration.ofNanos(System.nanoTime());
407-
Duration duration;
408-
while (to.compareTo(duration = Duration.ofNanos(System.nanoTime()).minus(now)) > 0) {
406+
long now = System.nanoTime();
407+
long to = now + (timeout * ONE_MILLION);
408+
long waitFor;
409+
while ((waitFor = (to - System.nanoTime())) >= 0) {
409410
if (existAny (keys))
410411
return true;
411412
synchronized (this) {
412413
try {
413-
wait (Math.max(to.minus(duration).toMillis(), 1L));
414+
this.wait(Math.max(waitFor / ONE_MILLION, 1L));
414415
} catch (InterruptedException e) { }
415416
}
416417
}
@@ -521,19 +522,24 @@ private void unregisterExpirable(Object k) {
521522

522523
static class Expirable implements Comparable, Serializable {
523524

524-
static final long serialVersionUID = 0xA7F22BF5;
525+
private static final long serialVersionUID = 0xA7F22BF5;
525526

526527
Object value;
527-
Duration expires;
528528

529-
public Expirable (Object value, Duration expires) {
529+
/**
530+
* When to expire, in the future, as given by monotonic System.nanoTime().<br>
531+
* IMPORTANT: always use a nanosec offset from System.nanoTime()!
532+
*/
533+
long expires;
534+
535+
Expirable (Object value, long expires) {
530536
super();
531537
this.value = value;
532538
this.expires = expires;
533539
}
534540

535-
public boolean isExpired () {
536-
return expires.compareTo(Duration.ofNanos(System.nanoTime())) < 0;
541+
boolean isExpired () {
542+
return (System.nanoTime() - expires) > 0;
537543
}
538544

539545
@Override
@@ -544,20 +550,16 @@ public String toString() {
544550
+ ",expired=" + isExpired ();
545551
}
546552

547-
public Object getValue() {
553+
Object getValue() {
548554
return isExpired() ? null : value;
549555
}
550556

551557
@Override
552-
public int compareTo (Object obj) {
553-
Expirable other = (Expirable) obj;
554-
Duration otherExpires = other.expires;
555-
if (otherExpires.equals(expires))
556-
return 0;
557-
else if (expires.compareTo(otherExpires) < 0)
558-
return -1;
559-
else
560-
return 1;
558+
public int compareTo (Object other) {
559+
long diff = this.expires - ((Expirable)other).expires;
560+
return diff > 0 ? 1 :
561+
diff < 0 ? -1 :
562+
0;
561563
}
562564
}
563565

jpos/src/test/java/org/jpos/space/TSpaceTest.java

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
@SuppressWarnings("unchecked")
4444
@ExtendWith(MockitoExtension.class)
4545
public class TSpaceTest {
46+
static final long EXPIRE_OFFSET = Duration.ofDays(9999).toNanos();
4647

4748
@Test
4849
public void testConstructor() throws Throwable {
@@ -63,27 +64,27 @@ public void testDump() throws Throwable {
6364

6465
@Test
6566
public void testExpirableCompareTo() throws Throwable {
66-
int result = new TSpace.Expirable(Integer.valueOf(0), Duration.ofMillis(1L)).compareTo(new TSpace.Expirable(new Object(), Duration.ofMillis(0L)));
67+
int result = new TSpace.Expirable(Integer.valueOf(0), 1L).compareTo(new TSpace.Expirable(new Object(), 0L));
6768
assertEquals(1, result, "result");
6869
}
6970

7071
@Test
7172
public void testExpirableCompareTo1() throws Throwable {
72-
TSpace.Expirable obj = new TSpace.Expirable(new Object(), Duration.ofMillis(0L));
73-
int result = new TSpace.Expirable(new Object(), Duration.ofMillis(0L)).compareTo(obj);
73+
TSpace.Expirable obj = new TSpace.Expirable(new Object(), 0L);
74+
int result = new TSpace.Expirable(new Object(), 0L).compareTo(obj);
7475
assertEquals(0, result, "result");
7576
}
7677

7778
@Test
7879
public void testExpirableCompareTo2() throws Throwable {
79-
int result = new TSpace.Expirable(null, Duration.ofMillis(0L)).compareTo(new TSpace.Expirable(new Object(), Duration.ofMillis(1L)));
80+
int result = new TSpace.Expirable(null, 0L).compareTo(new TSpace.Expirable(new Object(), 1L));
8081
assertEquals(-1, result, "result");
8182
}
8283

8384
@Test
8485
public void testExpirableCompareToThrowsNullPointerException() throws Throwable {
8586
try {
86-
new TSpace.Expirable(new Object(), Duration.ofMillis(100L)).compareTo(null);
87+
new TSpace.Expirable(new Object(), 100L).compareTo(null);
8788
fail("Expected NullPointerException to be thrown");
8889
} catch (NullPointerException ex) {
8990
if (isJavaVersionAtMost(JAVA_14)) {
@@ -97,51 +98,53 @@ public void testExpirableCompareToThrowsNullPointerException() throws Throwable
9798
@Test
9899
public void testExpirableConstructor() throws Throwable {
99100
Object value = new Object();
100-
TSpace.Expirable expirable = new TSpace.Expirable(value, Duration.ofMillis(100L));
101-
assertEquals(Duration.ofMillis(100L), expirable.expires, "expirable.expires");
101+
TSpace.Expirable expirable = new TSpace.Expirable(value, 100L);
102+
assertEquals(100L, expirable.expires, "expirable.expires");
102103
assertSame(value, expirable.value, "expirable.value");
103104
}
104105

105106
@Test
106107
public void testExpirableGetValue() throws Throwable {
107-
String result = (String) new TSpace.Expirable("", Duration.ofNanos(System.nanoTime()).plus(Duration.ofMillis(9184833384926L))).getValue();
108+
String result = (String) new TSpace.Expirable("", System.nanoTime() + EXPIRE_OFFSET).getValue();
108109
assertEquals("", result, "result");
109110
}
110111

111112
@Test
112113
public void testExpirableGetValue1() throws Throwable {
113-
Object result = new TSpace.Expirable(null, Duration.ofMillis(9184833384926L)).getValue();
114+
Object result = new TSpace.Expirable(null, System.nanoTime() + EXPIRE_OFFSET).getValue();
114115
assertNull(result, "result");
115116
}
116117

117118
@Test
118119
public void testExpirableGetValue2() throws Throwable {
119-
Object result = new TSpace.Expirable(new Object(), Duration.ofNanos(System.nanoTime()).minus(Duration.ofMillis(100L))).getValue();
120+
// using negative offset to ensure expiration (literally, object is born already expired)
121+
Object result = new TSpace.Expirable(new Object(), System.nanoTime() - EXPIRE_OFFSET).getValue();
120122
assertNull(result, "result");
121123
}
122124

123125
@Test
124126
public void testExpirableIsExpired() throws Throwable {
125-
boolean result = new TSpace.Expirable("", Duration.ofNanos(System.nanoTime()).plus(Duration.ofMillis(9184833384926L))).isExpired();
127+
boolean result = new TSpace.Expirable("", System.nanoTime() + EXPIRE_OFFSET).isExpired();
126128
assertFalse(result, "result");
127129
}
128130

129131
@Test
130132
public void testExpirableIsExpired1() throws Throwable {
131-
boolean result = new TSpace.Expirable(new Object(), Duration.ofNanos(System.nanoTime()).minus(Duration.ofMillis(100L))).isExpired();
133+
// using negative offset to ensure expiration (literally, object is born already expired)
134+
boolean result = new TSpace.Expirable(new Object(), System.nanoTime() - EXPIRE_OFFSET).isExpired();
132135
assertTrue(result, "result");
133136
}
134137

135138
@Test
136139
public void testExpirableToString() throws Throwable {
137-
new TSpace.Expirable(";\"i", Duration.ofMillis(100L)).toString();
140+
new TSpace.Expirable(";\"i", 100L).toString();
138141
assertTrue(true, "Test completed without Exception");
139142
}
140143

141144
@Test
142145
public void testExpirableToStringThrowsNullPointerException() throws Throwable {
143146
try {
144-
new TSpace.Expirable(null, Duration.ofMillis(100L)).toString();
147+
new TSpace.Expirable(null,100L).toString();
145148
fail("Expected NullPointerException to be thrown");
146149
} catch (NullPointerException ex) {
147150
if (isJavaVersionAtMost(JAVA_14)) {
@@ -154,7 +157,6 @@ public void testExpirableToStringThrowsNullPointerException() throws Throwable {
154157

155158
@Test
156159
public void testGc() throws Throwable {
157-
158160
TSpace tSpace = new TSpace();
159161
tSpace.gc();
160162
assertEquals(0, tSpace.entries.size(), "tSpace.entries.size()");
@@ -208,6 +210,7 @@ public void testInp1() throws Throwable {
208210
"tSpace.entries.get(\"\").get(0) had \"testString\" removed");
209211
assertEquals("testString", result, "result");
210212
assertEquals(3, tSpace.entries.size(), "tSpace.entries.size()");
213+
tSpace.dump(System.out, ">>");
211214
}
212215

213216
@Test
@@ -240,12 +243,10 @@ public void testNotifyReaders() {
240243
final Space sp = new TSpace();
241244
final AtomicInteger ai = new AtomicInteger(10);
242245
for (int i=0; i<10; i++) {
243-
new Thread() {
244-
public void run() {
245-
if (sp.rd("TEST", 5000L) != null)
246-
ai.decrementAndGet();
247-
}
248-
}.start();
246+
new Thread(()->{
247+
if (sp.rd("TEST", 5000L) != null)
248+
ai.decrementAndGet();
249+
}).start();
249250
}
250251
sp.out("TEST", Boolean.TRUE);
251252
ISOUtil.sleep(500L);

0 commit comments

Comments
 (0)