|
33 | 33 | import io.netty.util.concurrent.DefaultThreadFactory; |
34 | 34 | import java.lang.reflect.Method; |
35 | 35 | import java.time.Clock; |
| 36 | +import java.util.concurrent.CountDownLatch; |
| 37 | +import java.util.concurrent.ExecutorService; |
| 38 | +import java.util.concurrent.Executors; |
| 39 | +import java.util.concurrent.TimeUnit; |
| 40 | +import java.util.concurrent.atomic.AtomicInteger; |
| 41 | +import java.util.concurrent.atomic.AtomicReference; |
36 | 42 | import java.util.NavigableMap; |
37 | 43 | import java.util.Set; |
38 | 44 | import java.util.TreeMap; |
@@ -274,4 +280,100 @@ public void testDelaySequence(InMemoryDelayedDeliveryTracker tracker) throws Exc |
274 | 280 | tracker.close(); |
275 | 281 | } |
276 | 282 |
|
| 283 | + @Test(dataProvider = "delayedTracker") |
| 284 | + public void testRaceConditionInUpdateTimer(InMemoryDelayedDeliveryTracker tracker) throws Exception { |
| 285 | + final int numThreads = 15; |
| 286 | + final int operationsPerThread = 2000; |
| 287 | + final CountDownLatch startLatch = new CountDownLatch(1); |
| 288 | + final CountDownLatch doneLatch = new CountDownLatch(numThreads); |
| 289 | + final AtomicInteger errors = new AtomicInteger(0); |
| 290 | + final AtomicReference<Exception> firstException = new AtomicReference<>(); |
| 291 | + |
| 292 | + @Cleanup("shutdown") |
| 293 | + ExecutorService executorService = Executors.newFixedThreadPool(32); |
| 294 | + |
| 295 | + for (int i = 0; i < numThreads / 4; i++) { |
| 296 | + executorService.submit(() -> { |
| 297 | + try { |
| 298 | + startLatch.await(); |
| 299 | + for (int j = 0; j < operationsPerThread; j++) { |
| 300 | + tracker.getNumberOfDelayedMessages(); |
| 301 | + Thread.sleep(1); |
| 302 | + } |
| 303 | + } catch (Exception e) { |
| 304 | + errors.incrementAndGet(); |
| 305 | + firstException.compareAndSet(null, e); |
| 306 | + e.printStackTrace(); |
| 307 | + } finally { |
| 308 | + doneLatch.countDown(); |
| 309 | + } |
| 310 | + }); |
| 311 | + } |
| 312 | + |
| 313 | + for (int i = numThreads / 4; i < numThreads; i++) { |
| 314 | + executorService.submit(() -> { |
| 315 | + try { |
| 316 | + startLatch.await(); |
| 317 | + for (int j = 0; j < operationsPerThread; j++) { |
| 318 | + tracker.addMessage(1, 1, 10); |
| 319 | + Thread.sleep(1); |
| 320 | + } |
| 321 | + } catch (Exception e) { |
| 322 | + errors.incrementAndGet(); |
| 323 | + firstException.compareAndSet(null, e); |
| 324 | + e.printStackTrace(); |
| 325 | + } finally { |
| 326 | + doneLatch.countDown(); |
| 327 | + } |
| 328 | + }); |
| 329 | + } |
| 330 | + |
| 331 | + for (int i = numThreads / 4; i < numThreads; i++) { |
| 332 | + executorService.submit(() -> { |
| 333 | + try { |
| 334 | + startLatch.await(); |
| 335 | + for (int j = 0; j < operationsPerThread; j++) { |
| 336 | + tracker.clear(); |
| 337 | + Thread.sleep(1); |
| 338 | + } |
| 339 | + } catch (Exception e) { |
| 340 | + errors.incrementAndGet(); |
| 341 | + firstException.compareAndSet(null, e); |
| 342 | + e.printStackTrace(); |
| 343 | + } finally { |
| 344 | + doneLatch.countDown(); |
| 345 | + } |
| 346 | + }); |
| 347 | + } |
| 348 | + |
| 349 | + for (int i = numThreads / 4; i < numThreads; i++) { |
| 350 | + executorService.submit(() -> { |
| 351 | + try { |
| 352 | + startLatch.await(); |
| 353 | + for (int j = 0; j < operationsPerThread; j++) { |
| 354 | + tracker.getScheduledMessages(1); |
| 355 | + Thread.sleep(1); |
| 356 | + } |
| 357 | + } catch (Exception e) { |
| 358 | + errors.incrementAndGet(); |
| 359 | + firstException.compareAndSet(null, e); |
| 360 | + e.printStackTrace(); |
| 361 | + } finally { |
| 362 | + doneLatch.countDown(); |
| 363 | + } |
| 364 | + }); |
| 365 | + } |
| 366 | + |
| 367 | + startLatch.countDown(); |
| 368 | + assertTrue(doneLatch.await(30, TimeUnit.SECONDS), "Test should complete within 30 seconds"); |
| 369 | + |
| 370 | + if (errors.get() > 0) { |
| 371 | + Exception exception = firstException.get(); |
| 372 | + if (exception != null) { |
| 373 | + System.err.println("First exception caught: " + exception.getMessage()); |
| 374 | + exception.printStackTrace(); |
| 375 | + } |
| 376 | + } |
| 377 | + assertEquals(errors.get(), 0, "No exceptions should occur during concurrent operations"); |
| 378 | + } |
277 | 379 | } |
0 commit comments