Skip to content

Commit 2d645e6

Browse files
authored
Release delegate held by cached supplier (#1056)
1 parent 3957163 commit 2d645e6

File tree

2 files changed

+95
-6
lines changed

2 files changed

+95
-6
lines changed

src/main/java/com/networknt/schema/CachedSupplier.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,25 @@
2323
* @param <T> the type cached
2424
*/
2525
public class CachedSupplier<T> implements Supplier<T> {
26-
private final Supplier<T> delegate;
27-
private T cache = null;
26+
private volatile Supplier<T> delegate;
27+
private volatile T cache = null;
2828

2929
public CachedSupplier(Supplier<T> delegate) {
3030
this.delegate = delegate;
3131
}
3232

3333
@Override
3434
public T get() {
35-
if (cache == null) {
36-
cache = delegate.get();
35+
if (this.delegate == null) {
36+
return this.cache;
3737
}
38-
return cache;
39-
}
4038

39+
synchronized(this) {
40+
if (this.delegate != null) {
41+
this.cache = this.delegate.get();
42+
this.delegate = null;
43+
}
44+
}
45+
return this.cache;
46+
}
4147
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (c) 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.networknt.schema;
17+
18+
import static org.junit.jupiter.api.Assertions.assertEquals;
19+
import static org.junit.jupiter.api.Assertions.assertNull;
20+
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.concurrent.CountDownLatch;
24+
import java.util.concurrent.atomic.AtomicInteger;
25+
26+
import org.junit.jupiter.api.Test;
27+
28+
/**
29+
* Test for CachedSupplier.
30+
*/
31+
class CachedSupplierTest {
32+
@Test
33+
void nullValue() {
34+
CachedSupplier<String> supplier = new CachedSupplier<>(null);
35+
assertNull(supplier.get());
36+
}
37+
38+
@Test
39+
void concurrency() throws Exception {
40+
AtomicInteger value = new AtomicInteger(0);
41+
42+
CachedSupplier<Integer> supplier = new CachedSupplier<>(() -> {
43+
return value.addAndGet(1);
44+
});
45+
Exception[] instance = new Exception[1];
46+
CountDownLatch latch = new CountDownLatch(1);
47+
List<Thread> threads = new ArrayList<>();
48+
for (int i = 0; i < 50; ++i) {
49+
Runnable runner = new Runnable() {
50+
public void run() {
51+
try {
52+
latch.await();
53+
} catch (InterruptedException e) {
54+
throw new RuntimeException(e);
55+
}
56+
try {
57+
int result = supplier.get();
58+
assertEquals(1, result);
59+
} catch(Exception e) {
60+
synchronized(instance) {
61+
instance[0] = e;
62+
}
63+
}
64+
}
65+
};
66+
Thread thread = new Thread(runner, "Thread" + i);
67+
thread.start();
68+
threads.add(thread);
69+
}
70+
latch.countDown(); // Release the latch for threads to run concurrently
71+
threads.forEach(t -> {
72+
try {
73+
t.join();
74+
} catch (InterruptedException e) {
75+
throw new RuntimeException(e);
76+
}
77+
});
78+
if (instance[0] != null) {
79+
throw instance[0];
80+
}
81+
assertEquals(1, value.get());
82+
}
83+
}

0 commit comments

Comments
 (0)