Skip to content

Commit e065a30

Browse files
committed
Allow stop procedure to access still running services #937
1 parent 8fb5d1d commit e065a30

1 file changed

Lines changed: 64 additions & 28 deletions

File tree

bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/internal/p2/core/ProvisioningAgent.java

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.net.URI;
1919
import java.util.*;
20+
import java.util.function.Consumer;
2021
import org.eclipse.equinox.p2.core.IAgentLocation;
2122
import org.eclipse.equinox.p2.core.IProvisioningAgent;
2223
import org.eclipse.equinox.p2.core.spi.IAgentService;
@@ -30,12 +31,12 @@
3031
*/
3132
public class ProvisioningAgent implements IProvisioningAgent {
3233

33-
private final Map<String, Object> agentServices = Collections.synchronizedMap(new HashMap<>());
34+
private final Map<String, Object> agentServices = Collections.synchronizedMap(new LinkedHashMap<>());
3435
private BundleContext context;
3536
private volatile boolean stopped = false;
3637
private ServiceRegistration<IProvisioningAgent> reg;
3738
private final Map<String, ServiceTracker<IAgentServiceFactory, Object>> trackers = Collections
38-
.synchronizedMap(new HashMap<>());
39+
.synchronizedMap(new LinkedHashMap<>());
3940

4041
/**
4142
* Instantiates a provisioning agent.
@@ -50,7 +51,6 @@ public ProvisioningAgent() {
5051
public Object getService(String serviceName) {
5152
//synchronize so concurrent gets always obtain the same service
5253
synchronized (agentServices) {
53-
checkRunning();
5454
Object service = agentServices.get(serviceName);
5555
if (service != null) {
5656
return service;
@@ -59,6 +59,9 @@ public Object getService(String serviceName) {
5959
ServiceTracker<IAgentServiceFactory, Object> tracker = trackers.computeIfAbsent(serviceName,
6060
ref -> {
6161
try {
62+
if (stopped) {
63+
return null;
64+
}
6265
Filter filter = context.createFilter(
6366
String.format("(&(%s=%s)(|(%s=%s)(p2.agent.servicename=%s)))", //$NON-NLS-1$
6467
Constants.OBJECTCLASS, IAgentServiceFactory.class.getName(), //
@@ -69,6 +72,9 @@ public Object getService(String serviceName) {
6972
throw new AssertionError(e);
7073
}
7174
});
75+
if (tracker == null) {
76+
return null;
77+
}
7278
tracker.open();
7379
return tracker.getService();
7480
}
@@ -118,9 +124,6 @@ public void setLocation(URI location) {
118124

119125
@Override
120126
public void unregisterService(String serviceName, Object service) {
121-
if (stopped) {
122-
return;
123-
}
124127
agentServices.remove(serviceName, service);
125128
if (service instanceof IAgentService) {
126129
((IAgentService) service).stop();
@@ -129,29 +132,20 @@ public void unregisterService(String serviceName, Object service) {
129132

130133
@Override
131134
public void stop() {
132-
List<Object> toStop;
133-
synchronized (agentServices) {
134-
toStop = new ArrayList<>(agentServices.values());
135-
}
136-
//give services a chance to do their own shutdown
137-
for (Object service : toStop) {
138-
if (service instanceof IAgentService) {
139-
if (service != this) {
140-
((IAgentService) service).stop();
141-
}
142-
}
143-
}
144135
stopped = true;
145-
//close all service trackers
146-
synchronized (trackers) {
147-
for (ServiceTracker<IAgentServiceFactory, Object> t : trackers.values()) {
148-
t.close();
136+
try {
137+
disposeInTurns(agentServices.entrySet(), entry -> {
138+
unregisterService(entry.getKey(), entry.getValue());
139+
});
140+
} finally {
141+
try {
142+
disposeInTurns(trackers.values(), ServiceTracker::close);
143+
} finally {
144+
if (reg != null) {
145+
reg.unregister();
146+
reg = null;
147+
}
149148
}
150-
trackers.clear();
151-
}
152-
if (reg != null) {
153-
reg.unregister();
154-
reg = null;
155149
}
156150
}
157151

@@ -182,10 +176,52 @@ public void removedService(ServiceReference<IAgentServiceFactory> reference, Obj
182176
if (service instanceof IAgentService agentService) {
183177
agentService.stop();
184178
}
185-
context.ungetService(reference);
179+
if (service != null) {
180+
context.ungetService(reference);
181+
}
186182
}
187183

188184
};
189185

186+
/**
187+
* Given a thread-safe collection, drain all elements one-by-one, disposing each
188+
* without holding locks and allowing concurrent modification form. Other
189+
* threads should not attempt to add elements to collection.<br>
190+
* Usage example:
191+
*
192+
* <pre>{@code
193+
* volatile boolean stopped = false;
194+
* ...
195+
* stopped = true; // prevent other threads from adding to the collection
196+
* disposeInTurns(agentServices.entrySet(), entry -> {
197+
* unregisterService(entry.getKey(), entry.getValue());
198+
* });
199+
* }</pre>
200+
*
201+
*/
202+
@SuppressWarnings("unchecked")
203+
private <T> void disposeInTurns(Collection<T> input, Consumer<T> dispose) {
204+
RuntimeException error = null;
205+
while (!input.isEmpty()) {
206+
Object[] toStop = input.toArray(Object[]::new);
207+
Collections.reverse(Arrays.asList(toStop)); // Remove in an inverse order of adding
208+
for (Object item : toStop) {
209+
if (input.remove(item)) {
210+
try {
211+
dispose.accept((T) item);
212+
} catch (RuntimeException e) {
213+
if (error == null) {
214+
error = e;
215+
} else {
216+
error.addSuppressed(e);
217+
}
218+
}
219+
}
220+
}
221+
}
222+
if (error != null) {
223+
throw error;
224+
}
225+
}
190226

191227
}

0 commit comments

Comments
 (0)