Skip to content

ClassCastException when concurrently computeIfAbsent on WiredCache #155

Description

@dawnwords

Version of Cache2k:1.4.0.Final

A ClassCastException occurs in the following simple unit test.

  @Test
  public void testCache2kComputeIfAbsentConcurrently() throws InterruptedException {
    final Cache<String, State> cache = Cache2kBuilder.of(String.class, State.class)
        .expireAfterWrite(1, TimeUnit.HOURS)
        .addListener((CacheEntryExpiredListener<String, State>) (cache1, entry) ->
            logger.info("entry expired: {}", entry.getValue()))
        .build();
    logger.info("which cache: {}", cache.getClass().getName());
    final int threadCount = 16;
    final String key = "key";

    final CountDownLatch startLatch = new CountDownLatch(threadCount);
    final CountDownLatch stopLatch = new CountDownLatch(threadCount);
    final AtomicInteger exceptionCount = new AtomicInteger();
    for (int i = 0; i < threadCount; i++) {
      new Thread(() -> {
        try {
          startLatch.await();
          final State r = cache.computeIfAbsent(key, State::new);
          logger.info("got State: {}", r);
        } catch (Exception e) {
          logger.error("got Exception", e);
          exceptionCount.incrementAndGet();
        } finally {
          stopLatch.countDown();
        }
      }).start();
      startLatch.countDown();
    }
    stopLatch.await();
    assertThat(exceptionCount.get(), Matchers.is(0));
  }

  private static class State {
  }

If I addListener on Cache2kBuilder and a WiredCache instance is built, the assertion at the end of the unit test on Exception count may fail with the following logs:

[2020-10-01T20:08:38.919+08:00] [main           ] -> [INFO ] which cache: org.cache2k.core.WiredCache
[2020-10-01T20:08:38.943+08:00] [Thread-5       ] -> [INFO ] got State: test.Cache2kTest$State@6fddf965
[2020-10-01T20:08:38.943+08:00] [Thread-7       ] -> [INFO ] got State: test.Cache2kTest$State@6fddf965
[2020-10-01T20:08:38.944+08:00] [Thread-6       ] -> [INFO ] got State: test.Cache2kTest$State@6fddf965
[2020-10-01T20:08:38.944+08:00] [Thread-16      ] -> [INFO ] got State: test.Cache2kTest$State@6fddf965
[2020-10-01T20:08:38.943+08:00] [Thread-14      ] -> [INFO ] got State: test.Cache2kTest$State@6fddf965
[2020-10-01T20:08:38.944+08:00] [Thread-17      ] -> [INFO ] got State: test.Cache2kTest$State@6fddf965
[2020-10-01T20:08:38.945+08:00] [Thread-15      ] -> [INFO ] got State: test.Cache2kTest$State@6fddf965
[2020-10-01T20:08:38.945+08:00] [Thread-10      ] -> [INFO ] got State: test.Cache2kTest$State@6fddf965
[2020-10-01T20:08:38.945+08:00] [Thread-13      ] -> [INFO ] got State: test.Cache2kTest$State@6fddf965
[2020-10-01T20:08:38.945+08:00] [Thread-2       ] -> [INFO ] got State: test.Cache2kTest$State@6fddf965
[2020-10-01T20:08:38.946+08:00] [Thread-8       ] -> [INFO ] got State: test.Cache2kTest$State@6fddf965
[2020-10-01T20:08:38.954+08:00] [Thread-11      ] -> [ERROR] got Exception
java.lang.ClassCastException: org.cache2k.core.CompactEntry$InitialValueInEntryNeverReturned cannot be cast to test.Cache2kTest$State
	at test.Cache2kTest.lambda$testCache2k$7(Cache2kTest.java:158)
	at java.lang.Thread.run(Thread.java:748)
[2020-10-01T20:08:38.956+08:00] [Thread-3       ] -> [ERROR] got Exception
java.lang.ClassCastException: org.cache2k.core.CompactEntry$InitialValueInEntryNeverReturned cannot be cast to test.Cache2kTest$State
	at test.Cache2kTest.lambda$testCache2k$7(Cache2kTest.java:158)
	at java.lang.Thread.run(Thread.java:748)
[2020-10-01T20:08:38.956+08:00] [Thread-12      ] -> [ERROR] got Exception
java.lang.ClassCastException: org.cache2k.core.CompactEntry$InitialValueInEntryNeverReturned cannot be cast to test.Cache2kTest$State
	at test.Cache2kTest.lambda$testCache2k$7(Cache2kTest.java:158)
	at java.lang.Thread.run(Thread.java:748)
[2020-10-01T20:08:38.956+08:00] [Thread-4       ] -> [ERROR] got Exception
java.lang.ClassCastException: org.cache2k.core.CompactEntry$InitialValueInEntryNeverReturned cannot be cast to test.Cache2kTest$State
	at test.Cache2kTest.lambda$testCache2k$7(Cache2kTest.java:158)
	at java.lang.Thread.run(Thread.java:748)
[2020-10-01T20:08:38.956+08:00] [Thread-9       ] -> [ERROR] got Exception
java.lang.ClassCastException: org.cache2k.core.CompactEntry$InitialValueInEntryNeverReturned cannot be cast to test.Cache2kTest$State
	at test.Cache2kTest.lambda$testCache2k$7(Cache2kTest.java:158)
	at java.lang.Thread.run(Thread.java:748)

If I comment the addListener part and the Cache instance falls back to HeapCache, the unit test will pass and everything seems to be fine.

Is there any other required configuration to be included when using addListener on Cache2kBuilder?

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions