project/com.nokia.carbide.cpp.epoc.engine.tests/src/com/nokia/carbide/cpp/epoc/engine/tests/model/ModelProviderThreadTests.java
/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package com.nokia.carbide.cpp.epoc.engine.tests.model;
import java.util.HashSet;
import java.util.Set;
import junit.framework.TestCase;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.text.Document;
import com.nokia.carbide.cpp.epoc.engine.model.IModelProvider;
import com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPModel;
import com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPOwnedModel;
import com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView;
import com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPViewConfiguration;
public class ModelProviderThreadTests {
private static boolean modelThreadFailed;
class Reader implements Runnable {
private IPath path;
private String result;
private IModelProvider<IMMPOwnedModel, IMMPModel> provider;
private IMMPViewConfiguration viewConfiguration;
public Reader(IModelProvider<IMMPOwnedModel, IMMPModel> provider, IMMPViewConfiguration viewConfiguration, IPath path) {
this.provider = provider;
this.viewConfiguration = viewConfiguration;
this.path = path;
}
public String getResult() {
return result;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
while (!modelThreadFailed && modelWritingCount < MAX_ITERATIONS) {
try {
doRead();
} catch (CoreException e) {
TestCase.fail(e.getMessage());
return;
}
Thread.yield();
if (Thread.interrupted())
return;
}
// failed, so force early exit
modelWritingCount = MAX_ITERATIONS;
}
/**
*
*/
private void doRead() throws CoreException {
IMMPModel model = provider.getSharedModel(path);
TestCase.assertNotNull(model);
try {
IMMPView view = model.createView(viewConfiguration);
try {
if (DUMP) System.out.println(Thread.currentThread().getName()+ "> r");
} finally {
view.dispose();
}
} finally {
provider.releaseSharedModel(model);
}
}
}
final boolean DUMP = false;
final int MAX_ITERATIONS = 50;
public static int modelWritingCount;
synchronized int nextModelWritingCount() {
return modelWritingCount++;
}
class Writer implements Runnable {
private IPath path;
private IModelProvider<IMMPOwnedModel, IMMPModel> provider;
private IMMPViewConfiguration viewConfiguration;
public Writer(IModelProvider<IMMPOwnedModel, IMMPModel> provider, IMMPViewConfiguration viewConfiguration, IPath path) {
this.provider = provider;
this.viewConfiguration = viewConfiguration;
this.path = path;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
int cnt;
while (!modelThreadFailed && (cnt = nextModelWritingCount()) < MAX_ITERATIONS) {
try {
addSource(cnt);
} catch (CoreException e) {
TestCase.fail(e.getMessage());
return;
}
if (Thread.interrupted())
return;
}
}
/**
*
*/
private void addSource(final int count) throws CoreException {
IMMPModel model = provider.getSharedModel(path);
TestCase.assertNotNull(model);
try {
IMMPView view = model.createView(viewConfiguration);
try {
if (DUMP) System.out.println(Thread.currentThread().getName()+ "> w"+count);
while (!modelThreadFailed) {
try {
view.getSources().add(new Path("" + count));
view.commit();
break;
} catch (IllegalStateException e) {
if (DUMP) System.out.println(Thread.currentThread().getName()+ "> out of sync, trying again");
view.revert();
} catch (Throwable t) {
if (DUMP) System.out.println(Thread.currentThread().getName()+ "> failure while committing");
t.printStackTrace();
modelThreadFailed = true;
}
}
} finally {
view.dispose();
}
} finally {
provider.releaseSharedModel(model);
}
}
}
/**
* Ensure the reader thread got sensible results.
* @param result
*/
private void validate(String result) {
if (result.trim().length() == 0)
return;
int maxNum = -1;
Set<Integer> found = new HashSet<Integer>();
String[] nums = result.split("\\s+");
for (String num : nums) {
int n = Integer.parseInt(num);
found.add(n);
maxNum = Math.max(n, maxNum);
}
if (maxNum + 1 != found.size()) {
TestCase.fail("Invalid model contents: " + result);
}
}
/**
* Ensure the models work sanely with threading.
* @throws Exception
*/
public void testThreading(IModelProvider<IMMPOwnedModel, IMMPModel> provider,
IMMPViewConfiguration viewConfig,
IPath mmpPath) throws CoreException {
// create the initial content
IMMPOwnedModel ownedModel = provider.createModel(mmpPath);
ownedModel.setDocument(new Document("// test file\n"));
provider.registerModel(ownedModel);
provider.releaseSharedModel(ownedModel);
final int READER_COUNT = 5;
final int WRITER_COUNT = 2;
Reader[] readers = new Reader[READER_COUNT];
Writer[] writers = new Writer[WRITER_COUNT];
Thread[] readerThreads = new Thread[READER_COUNT];
Thread[] writerThreads = new Thread[WRITER_COUNT];
try {
for (int i = 0; i < WRITER_COUNT; i++) {
writers[i] = new Writer(provider, viewConfig, mmpPath);
writerThreads[i] = new Thread(writers[i]);
writerThreads[i].start();
}
for (int i = 0; i < READER_COUNT; i++) {
readers[i] = new Reader(provider, viewConfig, mmpPath);
readerThreads[i] = new Thread(readers[i]);
readerThreads[i].start();
}
while (modelWritingCount < MAX_ITERATIONS) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
TestCase.fail();
}
}
} catch (Throwable t) {
for (int i = 0; i < WRITER_COUNT; i++) {
writerThreads[i].interrupt();
}
for (int i = 0; i < READER_COUNT; i++) {
readerThreads[i].interrupt();
}
TestCase.fail(t.toString());
}
try {
for (int i = 0; i < WRITER_COUNT; i++) {
writerThreads[i].join();
}
for (int i = 0; i < READER_COUNT; i++) {
readerThreads[i].join();
}
} catch (InterruptedException e) {
TestCase.fail();
}
TestCase.assertFalse(modelThreadFailed);
// now validate results
IMMPModel model = provider.getSharedModel(mmpPath);
TestCase.assertNotNull(model);
try {
IMMPView view = model.createView(viewConfig);
try {
StringBuilder builder = new StringBuilder();
for (IPath path : view.getSources()) {
builder.append(path.lastSegment());
builder.append(' ');
}
// can't actually validate -- we have no idea whether writers'
// commits have come in order or not, since one writer may have
// been out-of-sync and had to retry its commit late.
validate(builder.toString());
} finally {
view.dispose();
}
} finally {
provider.releaseSharedModel(model);
}
}
}