Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java')
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java86
1 files changed, 60 insertions, 26 deletions
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java
index 73bfac5bb..1372e0d55 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java
@@ -28,12 +28,15 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.eclipse.osgi.container.Module.StartOptions;
import org.eclipse.osgi.container.Module.State;
import org.eclipse.osgi.container.Module.StopOptions;
+import org.eclipse.osgi.container.ModuleContainer.ResolutionLock.Permits;
import org.eclipse.osgi.container.ModuleContainerAdaptor.ContainerEvent;
import org.eclipse.osgi.container.ModuleContainerAdaptor.ModuleEvent;
import org.eclipse.osgi.container.ModuleDatabase.Sort;
@@ -485,10 +488,10 @@ public final class ModuleContainer implements DebugOptionsListener {
return new ModuleResolutionReport(null, Collections.<Resource, List<Entry>> emptyMap(), new ResolutionException("Unable to resolve while shutting down the framework.")); //$NON-NLS-1$
}
ResolutionReport report = null;
- try (ResolutionLock resolutionLock = new ResolutionLock(1)) {
+ try (ResolutionLock.Permits resolutionPermits = _resolutionLock.acquire(1)) {
do {
try {
- report = resolveAndApply(triggers, triggersMandatory, restartTriggers);
+ report = resolveAndApply(triggers, triggersMandatory, restartTriggers, resolutionPermits);
} catch (RuntimeException e) {
if (e.getCause() instanceof BundleException) {
BundleException be = (BundleException) e.getCause();
@@ -505,7 +508,7 @@ public final class ModuleContainer implements DebugOptionsListener {
return report;
}
- private ResolutionReport resolveAndApply(Collection<Module> triggers, boolean triggersMandatory, boolean restartTriggers) {
+ private ResolutionReport resolveAndApply(Collection<Module> triggers, boolean triggersMandatory, boolean restartTriggers, ResolutionLock.Permits resolutionPermits) {
if (triggers == null)
triggers = new ArrayList<>(0);
Collection<ModuleRevision> triggerRevisions = new ArrayList<>(triggers.size());
@@ -545,7 +548,7 @@ public final class ModuleContainer implements DebugOptionsListener {
modulesResolved.add(deltaRevision.getRevisions().getModule());
}
- return applyDelta(deltaWiring, modulesResolved, triggers, timestamp, restartTriggers) ? report : null;
+ return applyDelta(deltaWiring, modulesResolved, triggers, timestamp, restartTriggers, resolutionPermits) ? report : null;
}
/**
@@ -560,7 +563,7 @@ public final class ModuleContainer implements DebugOptionsListener {
Map<ModuleRevision, ModuleWiring> deltaWiring;
Collection<Module> modulesResolved;
long timestamp;
- try (ResolutionLock resolutionLock = new ResolutionLock(MAX_RESOLUTION_PERMITS)) {
+ try (Permits resolutionPermits = _resolutionLock.acquire(ResolutionLock.MAX_RESOLUTION_PERMITS)) {
do {
result = null;
Map<ModuleRevision, ModuleWiring> wiringClone = null;
@@ -644,7 +647,7 @@ public final class ModuleContainer implements DebugOptionsListener {
// Save the result
ModuleWiring wiring = deltaWiring.get(revision);
result = findExistingDynamicWire(wiring, dynamicPkgName);
- } while (!applyDelta(deltaWiring, modulesResolved, Collections.<Module> emptyList(), timestamp, false));
+ } while (!applyDelta(deltaWiring, modulesResolved, Collections.<Module> emptyList(), timestamp, false, resolutionPermits));
} catch (ResolutionLockException e) {
return null;
}
@@ -681,8 +684,7 @@ public final class ModuleContainer implements DebugOptionsListener {
// between taking the snapshot and successfully applying the
// results. Instead of resorting to single threaded operations
// we choose to limit the number of concurrent resolves
- final static int MAX_RESOLUTION_PERMITS = 10;
- final Semaphore _resolutionLock = new Semaphore(MAX_RESOLUTION_PERMITS);
+ final ResolutionLock _resolutionLock = new ResolutionLock();
final ReentrantLock _bundleStateLock = new ReentrantLock();
static class ResolutionLockException extends Exception {
@@ -697,32 +699,61 @@ public final class ModuleContainer implements DebugOptionsListener {
}
}
- class ResolutionLock implements Closeable {
- private final int permits;
-
- ResolutionLock(int permits) throws ResolutionLockException {
- this.permits = permits;
- boolean previousInterruption = Thread.interrupted();
- try {
- if (!_resolutionLock.tryAcquire(permits, 30, TimeUnit.SECONDS)) {
- throw new ResolutionLockException();
+ /**
+ * A resolution hook allows for a max of 10 threads to do resolution operations
+ * at the same time. The implementation uses a semaphore to grant the max number
+ * of permits (threads) but a reentrant read lock is also used to detect reentry.
+ * If a thread reenters then no extra permits are required by the thread.
+ * This lock returns a Permits object that implements closeable for use in
+ * try->with. If permits is closed multiple times then the additional close
+ * operations are a no-op.
+ */
+ static class ResolutionLock {
+ final static int MAX_RESOLUTION_PERMITS = 10;
+ final Semaphore permitPool = new Semaphore(MAX_RESOLUTION_PERMITS);
+ final ReentrantReadWriteLock reenterLock = new ReentrantReadWriteLock();
+
+ class Permits implements Closeable {
+ private final int aquiredPermits;
+ private final AtomicBoolean closed = new AtomicBoolean();
+
+ Permits(int requestedPermits) throws ResolutionLockException {
+ if (reenterLock.getReadHoldCount() > 0) {
+ // thread is already holding permits; don't request more
+ requestedPermits = 0;
}
- } catch (InterruptedException e) {
- throw new ResolutionLockException(e);
- } finally {
- if (previousInterruption) {
- Thread.currentThread().interrupt();
+ this.aquiredPermits = requestedPermits;
+ boolean previousInterruption = Thread.interrupted();
+ try {
+ if (!permitPool.tryAcquire(requestedPermits, 30, TimeUnit.SECONDS)) {
+ throw new ResolutionLockException();
+ }
+ } catch (InterruptedException e) {
+ throw new ResolutionLockException(e);
+ } finally {
+ if (previousInterruption) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ // mark this thread as holding permits
+ reenterLock.readLock().lock();
+ }
+
+ @Override
+ public void close() {
+ if (closed.compareAndSet(false, true)) {
+ permitPool.release(aquiredPermits);
+ reenterLock.readLock().unlock();
}
}
}
- @Override
- public void close() {
- _resolutionLock.release(permits);
+ Permits acquire(int requestedPermits) throws ResolutionLockException {
+ return new Permits(requestedPermits);
}
}
- private boolean applyDelta(Map<ModuleRevision, ModuleWiring> deltaWiring, Collection<Module> modulesResolved, Collection<Module> triggers, long timestamp, boolean restartTriggers) {
+ private boolean applyDelta(Map<ModuleRevision, ModuleWiring> deltaWiring, Collection<Module> modulesResolved, Collection<Module> triggers, long timestamp, boolean restartTriggers, ResolutionLock.Permits resolutionPermits) {
List<Module> modulesLocked = new ArrayList<>(modulesResolved.size());
// now attempt to apply the delta
try {
@@ -809,6 +840,9 @@ public final class ModuleContainer implements DebugOptionsListener {
}
}
+ // release resolution permits before firing events
+ resolutionPermits.close();
+
for (Module module : modulesLocked) {
adaptor.publishModuleEvent(ModuleEvent.RESOLVED, module, module);
}

Back to the top