ExclusiveLockManagerTest.java
/*******************************************************************************
* Copyright (c) 2022 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.common.concurrent.locks;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
class ExclusiveLockManagerTest {
private ExclusiveLockManager lockManager;
private ExclusiveLockManager lockManagerTracking;
private MemoryAppender memoryAppender;
@BeforeEach
void beforeEach() {
Properties.setLockTrackingEnabled(false);
lockManager = new ExclusiveLockManager(false, 1);
lockManagerTracking = new ExclusiveLockManager(true, 1);
Logger logger = (Logger) LoggerFactory.getLogger(ExclusiveLockManager.class.getName());
memoryAppender = new MemoryAppender();
memoryAppender.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
logger.detachAndStopAllAppenders();
logger.setLevel(Level.INFO);
logger.addAppender(memoryAppender);
memoryAppender.start();
}
@Test
void createLock() throws InterruptedException {
Lock lock = lockManager.getExclusiveLock();
assertTrue(lock.isActive());
lock.release();
assertFalse(lock.isActive());
}
@Test
@Timeout(2)
void cleanupUnreleasedLocks() throws InterruptedException {
lock(lockManager);
TestHelper.callGC(lockManager);
Lock exclusiveLock = lockManager.getExclusiveLock();
exclusiveLock.release();
}
@Test
@Timeout(2)
void cleanupUnreleasedLocksWithTracking() throws InterruptedException {
lock(lockManagerTracking);
Lock exclusiveLock = lockManagerTracking.getExclusiveLock();
exclusiveLock.release();
memoryAppender.waitForEvents();
assertThat(memoryAppender.countEventsForLogger(ExclusiveLockManager.class.getName())).isEqualTo(1);
memoryAppender.assertContains(
"at org.eclipse.rdf4j.common.concurrent.locks.ExclusiveLockManagerTest.cleanupUnreleasedLocksWithTracking",
Level.WARN);
}
@Test
@Timeout(2)
void deadlockTest() throws InterruptedException {
Thread thread = null;
try {
thread = new Thread(() -> {
Lock lock1 = null;
Lock lock2 = null;
try {
lock1 = lockManagerTracking.getExclusiveLock();
lock2 = lockManagerTracking.getExclusiveLock();
} catch (InterruptedException ignored) {
} finally {
if (lock1 != null) {
lock1.release();
}
if (lock2 != null) {
lock2.release();
}
}
});
thread.setDaemon(true);
thread.start();
memoryAppender.waitForEvents();
} finally {
TestHelper.interruptAndJoin(thread);
}
Lock lock = lockManagerTracking.getExclusiveLock();
assertTrue(lock.isActive());
lock.release();
assertThat(memoryAppender.countEventsForLogger(ExclusiveLockManager.class.getName())).isEqualTo(1);
memoryAppender.assertContains("is possibly deadlocked waiting on \"ExclusiveLockManager\" with id", Level.WARN);
memoryAppender.assertContains(
"at org.eclipse.rdf4j.common.concurrent.locks.ExclusiveLockManagerTest.lambda$deadlockTest$0(ExclusiveLockManagerTest.",
Level.WARN);
}
@Test
@Timeout(2)
void stalledTest() throws InterruptedException {
Lock exclusiveLock1 = lockManagerTracking.getExclusiveLock();
Thread thread = null;
try {
thread = new Thread(() -> {
try {
Lock exclusiveLock2 = lockManagerTracking.getExclusiveLock();
exclusiveLock2.release();
} catch (InterruptedException ignored) {
}
});
thread.setDaemon(true);
thread.start();
memoryAppender.waitForEvents();
} finally {
TestHelper.interruptAndJoin(thread);
}
assertNull(lockManagerTracking.tryExclusiveLock());
assertTrue(exclusiveLock1.isActive());
exclusiveLock1.release();
assertFalse(exclusiveLock1.isActive());
assertThat(memoryAppender.countEventsForLogger(ExclusiveLockManager.class.getName())).isGreaterThanOrEqualTo(1);
memoryAppender.assertContains("is waiting on a possibly stalled lock \"ExclusiveLockManager\" with id",
Level.INFO);
memoryAppender.assertContains(
"at org.eclipse.rdf4j.common.concurrent.locks.ExclusiveLockManagerTest.stalledTest(ExclusiveLockManagerTest.java:",
Level.INFO);
}
private void lock(ExclusiveLockManager lockManager) throws InterruptedException {
Lock lock = lockManager.getExclusiveLock();
assertTrue(lock.isActive());
}
}