Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[incubator-kie-drools-5974] Possible deadlock with KieRepositoryImpl$… #6006

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;

import org.drools.compiler.compiler.io.memory.MemoryFileSystem;
import org.drools.compiler.kproject.models.KieModuleModelImpl;
Expand Down Expand Up @@ -70,6 +71,8 @@ public class KieRepositoryImpl

private final KieModuleRepo kieModuleRepo;

private final ReentrantLock lock = new ReentrantLock();

public static void setInternalKieScanner(InternalKieScanner scanner) {
synchronized (KieScannerHolder.class) {
KieScannerHolder.kieScanner = scanner;
Expand Down Expand Up @@ -98,7 +101,7 @@ private static InternalKieScanner getInternalKieScanner() {
}

public KieRepositoryImpl() {
kieModuleRepo = new KieModuleRepo();
kieModuleRepo = new KieModuleRepo(lock);
}

public void setDefaultGAV(ReleaseId releaseId) {
Expand Down Expand Up @@ -192,7 +195,12 @@ private KieModule checkClasspathForKieModule(ReleaseId releaseId) {
}

private KieModule loadKieModuleFromMavenRepo(ReleaseId releaseId, PomModel pomModel) {
return KieScannerHolder.kieScanner.loadArtifact( releaseId, pomModel );
try {
lock.lock();
return KieScannerHolder.kieScanner.loadArtifact(releaseId, pomModel);
} finally {
lock.unlock();
}
}

private static class DummyKieScanner
Expand Down Expand Up @@ -329,6 +337,7 @@ public static class KieModuleRepo {
= Integer.parseInt(System.getProperty(CACHE_VERSIONS_MAX_PROPERTY, "10"));

// FIELDS -----------------------------------------------------------------------------------------------------------------
private final ReentrantLock lock;

// kieModules evicts based on access-time, not on insertion-time
public final Map<String, NavigableMap<ComparableVersion, KieModule>> kieModules
Expand All @@ -348,44 +357,59 @@ protected boolean removeEldestEntry( Map.Entry<ReleaseId, KieModule> eldest ) {
};

// METHODS ----------------------------------------------------------------------------------------------------------------
public KieModuleRepo(ReentrantLock lock) {
this.lock = lock;
}

public synchronized KieModule remove(ReleaseId releaseId) {
KieModule removedKieModule = null;
String ga = releaseId.getGroupId() + ":" + releaseId.getArtifactId();
ComparableVersion comparableVersion = new ComparableVersion(releaseId.getVersion());
public KieModule remove(ReleaseId releaseId) {
try {
lock.lock();

KieModule removedKieModule = null;
String ga = releaseId.getGroupId() + ":" + releaseId.getArtifactId();
ComparableVersion comparableVersion = new ComparableVersion(releaseId.getVersion());

NavigableMap<ComparableVersion, KieModule> artifactMap = kieModules.get(ga);
if (artifactMap != null) {
removedKieModule = artifactMap.remove(comparableVersion);
if (artifactMap.isEmpty()) {
kieModules.remove(ga);
NavigableMap<ComparableVersion, KieModule> artifactMap = kieModules.get(ga);
if (artifactMap != null) {
removedKieModule = artifactMap.remove(comparableVersion);
if (artifactMap.isEmpty()) {
kieModules.remove(ga);
}
oldKieModules.remove(releaseId);
}
oldKieModules.remove(releaseId);
}

return removedKieModule;
return removedKieModule;
} finally {
lock.unlock();
}
}

public synchronized void store(KieModule kieModule) {
ReleaseId releaseId = kieModule.getReleaseId();
String ga = releaseId.getGroupId() + ":" + releaseId.getArtifactId();
ComparableVersion comparableVersion = new ComparableVersion(releaseId.getVersion());
public void store(KieModule kieModule) {
try {
lock.lock();

ReleaseId releaseId = kieModule.getReleaseId();
String ga = releaseId.getGroupId() + ":" + releaseId.getArtifactId();
ComparableVersion comparableVersion = new ComparableVersion(releaseId.getVersion());

NavigableMap<ComparableVersion, KieModule> artifactMap = kieModules.get(ga);
if( artifactMap == null ) {
artifactMap = createNewArtifactMap();
kieModules.put(ga, artifactMap);
}
NavigableMap<ComparableVersion, KieModule> artifactMap = kieModules.get(ga);
if( artifactMap == null ) {
artifactMap = createNewArtifactMap();
kieModules.put(ga, artifactMap);
}

KieModule oldReleaseIdKieModule = oldKieModules.get(releaseId);
// variable used in order to test race condition
if (oldReleaseIdKieModule == null) {
KieModule oldKieModule = artifactMap.get(comparableVersion);
if (oldKieModule != null) {
oldKieModules.put( releaseId, oldKieModule );
KieModule oldReleaseIdKieModule = oldKieModules.get(releaseId);
// variable used in order to test race condition
if (oldReleaseIdKieModule == null) {
KieModule oldKieModule = artifactMap.get(comparableVersion);
if (oldKieModule != null) {
oldKieModules.put( releaseId, oldKieModule );
}
}
artifactMap.put( comparableVersion, kieModule );
} finally {
lock.unlock();
}
artifactMap.put( comparableVersion, kieModule );
}

/**
Expand Down Expand Up @@ -421,56 +445,72 @@ public KieModule put( ComparableVersion key, KieModule value ) {
return newArtifactMap;
}

synchronized KieModule loadOldAndRemove(ReleaseId releaseId) {
return oldKieModules.remove(releaseId);
KieModule loadOldAndRemove(ReleaseId releaseId) {
try {
lock.lock();
return oldKieModules.remove(releaseId);
} finally {
lock.unlock();
}
}

synchronized KieModule load(InternalKieScanner kieScanner, ReleaseId releaseId) {
return load(kieScanner, releaseId, new VersionRange(releaseId.getVersion()));
KieModule load(InternalKieScanner kieScanner, ReleaseId releaseId) {
try {
lock.lock();
return load(kieScanner, releaseId, new VersionRange(releaseId.getVersion()));
} finally {
lock.unlock();
}
}

synchronized KieModule load(InternalKieScanner kieScanner, ReleaseId releaseId, VersionRange versionRange) {
String ga = releaseId.getGroupId() + ":" + releaseId.getArtifactId();
KieModule load(InternalKieScanner kieScanner, ReleaseId releaseId, VersionRange versionRange) {
try {
lock.lock();

NavigableMap<ComparableVersion, KieModule> artifactMap = kieModules.get(ga);
if ( artifactMap == null || artifactMap.isEmpty() ) {
return null;
}
KieModule kieModule = artifactMap.get(new ComparableVersion(releaseId.getVersion()));

if (versionRange.fixed) {
if ( kieModule != null && releaseId.isSnapshot() ) {
String oldSnapshotVersion = ((ReleaseIdImpl)kieModule.getReleaseId()).getSnapshotVersion();
if ( oldSnapshotVersion != null ) {
String currentSnapshotVersion = kieScanner.getArtifactVersion(releaseId);
if (currentSnapshotVersion != null &&
new ComparableVersion(currentSnapshotVersion).compareTo(new ComparableVersion(oldSnapshotVersion)) > 0) {
// if the snapshot currently available on the maven repo is newer than the cached one
// return null to enforce the building of this newer version
return null;
String ga = releaseId.getGroupId() + ":" + releaseId.getArtifactId();

NavigableMap<ComparableVersion, KieModule> artifactMap = kieModules.get(ga);
if ( artifactMap == null || artifactMap.isEmpty() ) {
return null;
}
KieModule kieModule = artifactMap.get(new ComparableVersion(releaseId.getVersion()));

if (versionRange.fixed) {
if ( kieModule != null && releaseId.isSnapshot() ) {
String oldSnapshotVersion = ((ReleaseIdImpl)kieModule.getReleaseId()).getSnapshotVersion();
if ( oldSnapshotVersion != null ) {
String currentSnapshotVersion = kieScanner.getArtifactVersion(releaseId);
if (currentSnapshotVersion != null &&
new ComparableVersion(currentSnapshotVersion).compareTo(new ComparableVersion(oldSnapshotVersion)) > 0) {
// if the snapshot currently available on the maven repo is newer than the cached one
// return null to enforce the building of this newer version
return null;
}
}
}
return kieModule;
}
return kieModule;
}

Map.Entry<ComparableVersion, KieModule> entry =
versionRange.upperBound == null ?
artifactMap.lastEntry() :
versionRange.upperInclusive ?
artifactMap.floorEntry(new ComparableVersion(versionRange.upperBound)) :
artifactMap.lowerEntry(new ComparableVersion(versionRange.upperBound));
Map.Entry<ComparableVersion, KieModule> entry =
versionRange.upperBound == null ?
artifactMap.lastEntry() :
versionRange.upperInclusive ?
artifactMap.floorEntry(new ComparableVersion(versionRange.upperBound)) :
artifactMap.lowerEntry(new ComparableVersion(versionRange.upperBound));

if ( entry == null ) {
return null;
}
if ( entry == null ) {
return null;
}

if ( versionRange.lowerBound == null ) {
return entry.getValue();
}
if ( versionRange.lowerBound == null ) {
return entry.getValue();
}

int comparison = entry.getKey().compareTo(new ComparableVersion(versionRange.lowerBound));
return comparison > 0 || (comparison == 0 && versionRange.lowerInclusive) ? entry.getValue() : null;
int comparison = entry.getKey().compareTo(new ComparableVersion(versionRange.lowerBound));
return comparison > 0 || (comparison == 0 && versionRange.lowerInclusive) ? entry.getValue() : null;
} finally {
lock.unlock();
}
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;

import org.drools.compiler.kie.builder.impl.BuildContext;
import org.drools.compiler.kie.builder.impl.InternalKieModule;
Expand Down Expand Up @@ -89,7 +90,7 @@ public class KieModuleRepoTest {

@Before
public void before() throws Exception {
kieModuleRepo = new KieModuleRepo();
kieModuleRepo = new KieModuleRepo(new ReentrantLock());

// store the original values as we need to restore them after the test
maxSizeGaCacheOrig = KieModuleRepo.MAX_SIZE_GA_CACHE;
Expand Down Expand Up @@ -139,6 +140,15 @@ private static KieContainerImpl createMockKieContainer( final ReleaseId projectR
kieModuleRepoField.setAccessible(true);
kieModuleRepoField.set(kieRepository, kieModuleRepo);
kieModuleRepoField.setAccessible(false);
// share the lock between KieModuleRepo and KieRepository
final Field KieModuleRepoLockField = KieModuleRepo.class.getDeclaredField("lock");
KieModuleRepoLockField.setAccessible(true);
Object lock = KieModuleRepoLockField.get(kieModuleRepo);
KieModuleRepoLockField.setAccessible(false);
final Field kieRepositoryImplLockField = KieRepositoryImpl.class.getDeclaredField("lock");
kieRepositoryImplLockField.setAccessible(true);
kieRepositoryImplLockField.set(kieRepository, lock);
kieRepositoryImplLockField.setAccessible(false);
Comment on lines +143 to +151
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reflection is a little ugly, but reflection was already used in this test, so I followed the approach rather than introducing public accessor for lock.


// kie container
final KieContainerImpl kieContainerImpl = new KieContainerImpl(mockKieProject, kieRepository);
Expand Down
21 changes: 20 additions & 1 deletion kie-ci/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

<properties>
<java.module.name>org.kie.ci</java.module.name>
<excludedGroups>org.kie.test.testcategory.TurtleTestCategory</excludedGroups>
</properties>

<dependencies>
Expand Down Expand Up @@ -158,7 +159,12 @@
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-test-util</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -235,4 +241,17 @@
</plugins>
</build>

<profiles>
<profile>
<id>runTurtleTests</id>
<activation>
<property>
<name>runTurtleTests</name>
</property>
</activation>
<properties>
<excludedGroups/>
</properties>
</profile>
</profiles>
</project>
Loading
Loading