project/com.nokia.carbide.cpp.epoc.engine.tests/src/com/nokia/carbide/cpp/epoc/engine/tests/model/ModelProviderThreadTests.java
changeset 0 fb279309251b
child 2018 5034d483c2e2
--- /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);
+		}
+	}
+	
+}