22
22
import java .util .concurrent .CountDownLatch ;
23
23
import java .util .concurrent .ExecutorService ;
24
24
import java .util .concurrent .Executors ;
25
+ import java .util .concurrent .Future ;
25
26
import java .util .concurrent .TimeUnit ;
26
27
import java .util .function .Predicate ;
27
28
28
- import org .jspecify .annotations .Nullable ;
29
29
import org .junit .jupiter .api .AfterEach ;
30
30
import org .junit .jupiter .api .BeforeEach ;
31
31
import org .junit .jupiter .api .Test ;
@@ -196,41 +196,21 @@ void getErrUsesCache() {
196
196
197
197
@ Test
198
198
void getOutCacheShouldNotReturnStaleDataWhenDataIsLoggedWhileReading () throws Exception {
199
- this .output .push ();
199
+ TestLatchedOutputCapture output = new TestLatchedOutputCapture ();
200
+ output .push ();
200
201
System .out .print ("A" );
201
- this .output .waitAfterBuildLatch = new CountDownLatch (1 );
202
-
203
- ExecutorService executorService = null ;
202
+ ExecutorService executor = Executors .newFixedThreadPool (2 );
204
203
try {
205
- executorService = Executors .newFixedThreadPool (2 );
206
- var readingThreadFuture = executorService .submit (() -> {
207
- // this will release the releaseAfterBuildLatch and block on the waitAfterBuildLatch
208
- assertThat (this .output .getOut ()).isEqualTo ("A" );
209
- });
210
- var writingThreadFuture = executorService .submit (() -> {
211
- // wait until we finished building the first result (but did not yet update the cache)
212
- try {
213
- this .output .releaseAfterBuildLatch .await ();
214
- }
215
- catch (InterruptedException e ) {
216
- throw new RuntimeException (e );
217
- }
218
- // print something else and then release the latch, for the other thread to continue
219
- System .out .print ("B" );
220
- this .output .waitAfterBuildLatch .countDown ();
221
- });
222
- readingThreadFuture .get ();
223
- writingThreadFuture .get ();
204
+ Future <?> reader = executor .submit (output ::releaseAfterBuildAndAssertResultIsA );
205
+ Future <?> writer = executor .submit (output ::awaitReleaseAfterBuildThenWriteBAndRelease );
206
+ reader .get ();
207
+ writer .get ();
224
208
}
225
209
finally {
226
- if (executorService != null ) {
227
- executorService .shutdown ();
228
- executorService .awaitTermination (10 , TimeUnit .SECONDS );
229
- }
210
+ executor .shutdown ();
211
+ executor .awaitTermination (10 , TimeUnit .SECONDS );
230
212
}
231
-
232
- // If not synchronized correctly this will fail, because the second print did not clear the cache and the cache will return stale data.
233
- assertThat (this .output .getOut ()).isEqualTo ("AB" );
213
+ assertThat (output .getOut ()).isEqualTo ("AB" );
234
214
}
235
215
236
216
private void pushAndPrint () {
@@ -257,28 +237,48 @@ static class TestOutputCapture extends OutputCapture {
257
237
258
238
int buildCount ;
259
239
260
- @ Nullable
261
- CountDownLatch waitAfterBuildLatch = null ;
240
+ @ Override
241
+ String build (Predicate <Type > filter ) {
242
+ this .buildCount ++;
243
+ return super .build (filter );
244
+ }
245
+
246
+ }
247
+
248
+ static class TestLatchedOutputCapture extends OutputCapture {
262
249
263
- CountDownLatch releaseAfterBuildLatch = new CountDownLatch (1 );
250
+ private final CountDownLatch waitAfterBuild = new CountDownLatch (1 );
251
+
252
+ private final CountDownLatch releaseAfterBuild = new CountDownLatch (1 );
264
253
265
254
@ Override
266
255
String build (Predicate <Type > filter ) {
267
- this .buildCount ++;
268
256
var result = super .build (filter );
269
- this .releaseAfterBuildLatch .countDown ();
270
- if (this .waitAfterBuildLatch != null ) {
271
- try {
272
- this .waitAfterBuildLatch .await ();
273
- }
274
- catch (InterruptedException e ) {
275
- Thread .currentThread ().interrupt ();
276
- throw new RuntimeException (e );
277
- }
278
- }
257
+ this .releaseAfterBuild .countDown ();
258
+ await (this .waitAfterBuild );
279
259
return result ;
280
260
}
281
261
262
+ void releaseAfterBuildAndAssertResultIsA () {
263
+ assertThat (getOut ()).isEqualTo ("A" );
264
+ }
265
+
266
+ void awaitReleaseAfterBuildThenWriteBAndRelease () {
267
+ await (this .releaseAfterBuild );
268
+ System .out .print ("B" );
269
+ this .waitAfterBuild .countDown ();
270
+ }
271
+
272
+ private void await (CountDownLatch latch ) {
273
+ try {
274
+ latch .await ();
275
+ }
276
+ catch (InterruptedException ex ) {
277
+ Thread .currentThread ().interrupt ();
278
+ throw new RuntimeException (ex );
279
+ }
280
+ }
281
+
282
282
}
283
283
284
284
}
0 commit comments