project/com.nokia.carbide.cpp.epoc.engine.tests/src/com/nokia/carbide/cpp/epoc/engine/tests/model/ModelProviderThreadTests.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project/com.nokia.carbide.cpp.epoc.engine.tests/src/com/nokia/carbide/cpp/epoc/engine/tests/model/ModelProviderThreadTests.java Fri Apr 03 23:33:03 2009 +0100
@@ -0,0 +1,275 @@
+/*
+* 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);
+ }
+ }
+
+}