|
13 | 13 | import java.util.LinkedHashMap; |
14 | 14 | import java.util.UUID; |
15 | 15 | import java.util.concurrent.CountDownLatch; |
| 16 | +import java.util.concurrent.Executors; |
16 | 17 | import java.util.concurrent.TimeUnit; |
| 18 | +import java.util.concurrent.atomic.AtomicInteger; |
17 | 19 | import java.util.concurrent.atomic.AtomicReference; |
18 | 20 | import org.junit.jupiter.api.Test; |
19 | 21 | import org.junit.jupiter.api.io.TempDir; |
20 | 22 |
|
21 | 23 | class HttpEnvelopeWireCodecTest { |
22 | 24 | @TempDir Path directory; |
23 | 25 |
|
| 26 | + @Test |
| 27 | + void serializedWrappersCoordinateConcurrentAccessToOneCodec() throws Exception { |
| 28 | + CountDownLatch firstEntered = new CountDownLatch(1); |
| 29 | + CountDownLatch releaseFirst = new CountDownLatch(1); |
| 30 | + AtomicInteger active = new AtomicInteger(); |
| 31 | + AtomicInteger maximumActive = new AtomicInteger(); |
| 32 | + HttpEnvelopeWireCodec delegate = new HttpEnvelopeWireCodec() { |
| 33 | + private JsonEnvelope apply(JsonEnvelope envelope) { |
| 34 | + int current = active.incrementAndGet(); |
| 35 | + maximumActive.accumulateAndGet(current, Math::max); |
| 36 | + try { |
| 37 | + if (current == 1) { |
| 38 | + firstEntered.countDown(); |
| 39 | + assertTrue(releaseFirst.await(5, TimeUnit.SECONDS)); |
| 40 | + } |
| 41 | + return envelope; |
| 42 | + } catch (InterruptedException interrupted) { |
| 43 | + Thread.currentThread().interrupt(); |
| 44 | + throw new IllegalStateException(interrupted); |
| 45 | + } finally { |
| 46 | + active.decrementAndGet(); |
| 47 | + } |
| 48 | + } |
| 49 | + |
| 50 | + @Override public JsonEnvelope encode(JsonEnvelope envelope) { return apply(envelope); } |
| 51 | + @Override public JsonEnvelope decode(JsonEnvelope envelope) { return apply(envelope); } |
| 52 | + }; |
| 53 | + HttpEnvelopeWireCodec first = HttpEnvelopeWireCodec.serialized(delegate); |
| 54 | + HttpEnvelopeWireCodec second = HttpEnvelopeWireCodec.serialized(delegate); |
| 55 | + JsonEnvelope envelope = JsonEnvelope.builder("test").build(); |
| 56 | + var executor = Executors.newFixedThreadPool(2); |
| 57 | + try { |
| 58 | + var firstCall = executor.submit(() -> first.encode(envelope)); |
| 59 | + assertTrue(firstEntered.await(5, TimeUnit.SECONDS)); |
| 60 | + CountDownLatch secondAttempted = new CountDownLatch(1); |
| 61 | + var secondCall = executor.submit(() -> { |
| 62 | + secondAttempted.countDown(); |
| 63 | + return second.decode(envelope); |
| 64 | + }); |
| 65 | + assertTrue(secondAttempted.await(5, TimeUnit.SECONDS)); |
| 66 | + assertThrows(java.util.concurrent.TimeoutException.class, |
| 67 | + () -> secondCall.get(100, TimeUnit.MILLISECONDS)); |
| 68 | + assertEquals(1, maximumActive.get()); |
| 69 | + releaseFirst.countDown(); |
| 70 | + assertEquals(envelope, firstCall.get(5, TimeUnit.SECONDS)); |
| 71 | + assertEquals(envelope, secondCall.get(5, TimeUnit.SECONDS)); |
| 72 | + assertEquals(1, maximumActive.get()); |
| 73 | + } finally { |
| 74 | + releaseFirst.countDown(); |
| 75 | + executor.shutdownNow(); |
| 76 | + } |
| 77 | + } |
| 78 | + |
24 | 79 | @Test |
25 | 80 | void durableProxyQueueKeepsSemanticEnvelopeAcrossCodecChange() throws Exception { |
26 | 81 | Path outgoing = directory.resolve("outgoing"); |
|
0 commit comments