blob: 94095bc333c2091785379579619f0d6cc6d7191f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.tests.internal.registrycache;
import java.io.DataInputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.eclipse.core.internal.plugins.InternalFactory;
import org.eclipse.core.internal.plugins.RegistryCacheReader;
import org.eclipse.core.internal.runtime.InternalPlatform;
import org.eclipse.core.internal.runtime.Policy;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.model.*;
public class LazyCacheTests extends SimpleCacheTests {
public LazyCacheTests(String name) {
super(name);
}
public PluginRegistryModel doCacheRead(DataInputStream input, Factory factory) {
// Cobble together a plugin path
Map regIndex = InternalPlatform.getRegIndex();
URL[] pluginPath = null;
if (regIndex != null) {
int entrySize = regIndex.keySet().size();
pluginPath = new URL[entrySize];
int i = 0;
for (Iterator list = regIndex.keySet().iterator(); list.hasNext();) {
String fileName = (String) list.next();
fileName = "file:" + fileName;
try {
pluginPath[i++] = new URL(fileName);
} catch (MalformedURLException badURL) {
assertTrue("2.1 Bad url found for " + fileName + ".", true);
}
}
}
RegistryCacheReader cacheReader = new RegistryCacheReader(factory, true);
PluginRegistryModel newRegistry = cacheReader.readPluginRegistry(input, pluginPath, true);
cacheReader.setLazilyLoadExtensions(false);
return newRegistry;
}
/**
* See bug 36659 - there is a thread safety issue with lazily loading the
* plug-in registry. This test consists in forcing the full loading of all
* extensions in all plug-ins in the registry simultaneously in two
* different threads.
*/
public void testThreadSafety() {
MultiStatus problems = new MultiStatus(org.eclipse.core.runtime.Platform.PI_RUNTIME, Platform.PARSE_PROBLEM, Policy.bind("registryTestProblems", new String[0]), null);
InternalFactory factory = new InternalFactory(problems);
String[] localXMLFiles = setupExtensionTest();
// Don't resolve the registry as all the prerequisites aren't there
PluginRegistryModel registry = doInitialParsing(factory, localXMLFiles, false);
registry.markReadOnly();
// try hard to reproduce the thread safety problem
for (int i = 0; i < 50; i++) {
Semaphore semaphore = new Semaphore();
PluginRegistryModel cachedRegistry = doCacheWriteAndRead(registry, factory);
// the registry visitor is a runnable that will force all extensions of all plug-ins to be loaded
RegistryVisitor registryVisitor = new RegistryVisitor(cachedRegistry, semaphore);
// create and start 2 threads running the registry visitor
Thread[] threads = new Thread[2];
for (int j = 0; j < threads.length; j++) {
threads[j] = new Thread(registryVisitor);
threads[j].start();
}
for (int j = 0; j < threads.length; j++);
// wait for all threads to finish so we can know the outcome
try {
semaphore.down(threads.length);
} catch (InterruptedException e) {
fail("2." + i, e);
}
Throwable result = registryVisitor.getResult();
// no errors
if (result == null)
return;
// hopefully an org.eclipse.core.internal.runtime.AssertionFailedException (internal)
// thrown by trying to modify a read-only model object
if (result instanceof Exception)
fail("3." + i, (Exception) registryVisitor.getResult());
if (result instanceof Error)
throw (Error) result;
}
}
/**
* Forces all extensions to be fully loaded.
*/
class RegistryVisitor implements Runnable {
private PluginRegistryModel registry;
private Semaphore semaphore;
private Throwable result;
RegistryVisitor(PluginRegistryModel registry, Semaphore semaphore) {
this.registry = registry;
this.semaphore = semaphore;
}
private Throwable getResult() {
return result;
}
public synchronized void setResult(Throwable result) {
// to avoid overwriting the original failure
if (this.result == null)
this.result = result;
}
public void run() {
try {
int rootElementsLoaded = 0;
PluginDescriptorModel[] plugins = registry.getPlugins();
// should never happen - just to ensure the registry is good for our test
assertTrue("The plug-in registry has no plug-ins", plugins.length > 0);
for (int j = 0; j < plugins.length; j++) {
ExtensionModel[] extensions = plugins[j].getDeclaredExtensions();
for (int k = 0; k < extensions.length; k++) {
ConfigurationElementModel[] elements = extensions[k].getSubElements();
if (elements == null)
continue;
rootElementsLoaded += elements.length;
}
}
// should never happen - just to ensure the registry is good for our test
assertTrue("The plug-in registry has no extension elements", rootElementsLoaded > 0);
} catch (Throwable e) {
setResult(e);
} finally {
// one more thread has finished
semaphore.up(1);
}
}
}
static class Semaphore {
private int counter;
public synchronized void up(int count) {
this.counter+= count;
this.notifyAll();
}
public synchronized void down(int count) throws InterruptedException {
while (this.counter < count)
wait();
this.counter -= count;
}
}
public static Test suite() {
return new TestSuite(LazyCacheTests.class);
}
}