/******************************************************************************* * Copyright (c) 2006, 2009 Wind River Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ //#ifdef exercises package org.eclipse.cdt.examples.dsf.dataviewer; //#else //#package org.eclipse.cdt.examples.dsf.dataviewer.answers; //#endif import java.util.HashSet; //#ifdef answers //#import java.util.Iterator; //#endif import java.util.LinkedList; import java.util.List; import java.util.Random; import java.util.Set; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; //#ifdef answers //#import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; //#import org.eclipse.cdt.dsf.concurrent.Immutable; //#import org.eclipse.cdt.dsf.concurrent.ThreadSafe; //#endif import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; /** * DSF Executor-based implementation of the data generator. * <p> * This generator uses a queue of client requests and processes these * requests periodically using a DSF executor. The main feature of this * generator is that it uses the executor as its only synchronization object. * This means that all the fields with the exception of the executor can only * be accessed while running in the executor thread. * </p> */ //#ifdef exercises //TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) //indicating allowed thread access to this class/method/member //#else //#@ThreadSafe //#endif public class DataGeneratorWithExecutor implements IDataGenerator { // Request objects are used to serialize the interface calls into objects // which can then be pushed into a queue. //#ifdef exercises // TODO Ecercise 4 - Add an annotationindicating allowed concurrency access // Hint: Request and its subclasses have all their fields declared as final. //#else //# @Immutable //#endif abstract class Request { final RequestMonitor fRequestMonitor; Request(RequestMonitor rm) { fRequestMonitor = rm; } } //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @Immutable //#endif class CountRequest extends Request { CountRequest(DataRequestMonitor<Integer> rm) { super(rm); } } //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @Immutable //#endif class ItemRequest extends Request { final int fIndex; ItemRequest(int index, DataRequestMonitor<String> rm) { super(rm); fIndex = index; } } // The executor used to access all internal data of the generator. //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member // Hint: If a member does not have an annotation, the programmer can assume // that the concurrency rule that applies to the class also applies to this // member. //#endif private DsfExecutor fExecutor; // Main request queue of the data generator. The getValue(), getCount(), // and shutdown() methods write into the queue, while the serviceQueue() // method reads from it. // The executor used to access all internal data of the generator. //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif private List<Request> fQueue = new LinkedList<Request>(); // List of listeners is not synchronized, it also has to be accessed // using the executor. //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif private List<Listener> fListeners = new LinkedList<Listener>(); // Current number of elements in this generator. //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif private int fCount = MIN_COUNT; // Counter used to determine when to reset the element count. //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif private int fCountResetTrigger = 0; // Elements which were modified since the last reset. //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif private Set<Integer> fChangedIndexes = new HashSet<Integer>(); // Flag used to ensure that requests are processed sequentially. //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif private boolean fServiceQueueInProgress = false; //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#endif public DataGeneratorWithExecutor() { // Create the executor fExecutor = new DefaultDsfExecutor("Supplier Executor"); // Schedule a runnable to make the random changes. fExecutor.scheduleAtFixedRate( new DsfRunnable() { public void run() { randomChanges(); } }, RANDOM_CHANGE_INTERVAL, RANDOM_CHANGE_INTERVAL, TimeUnit.MILLISECONDS); } //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#endif public void shutdown(final RequestMonitor rm) { try { fExecutor.execute( new DsfRunnable() { public void run() { // Empty the queue of requests and fail them. for (Request request : fQueue) { request.fRequestMonitor.setStatus( new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); request.fRequestMonitor.done(); } fQueue.clear(); // Kill executor. fExecutor.shutdown(); rm.done(); } }); } catch (RejectedExecutionException e) { rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); rm.done(); } } //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#endif public void getCount(final DataRequestMonitor<Integer> rm) { try { fExecutor.execute( new DsfRunnable() { public void run() { fQueue.add(new CountRequest(rm)); serviceQueue(); } }); } catch (RejectedExecutionException e) { rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); rm.done(); } } //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#endif public void getValue(final int index, final DataRequestMonitor<String> rm) { try { fExecutor.execute( new DsfRunnable() { public void run() { fQueue.add(new ItemRequest(index, rm)); serviceQueue(); } }); } catch (RejectedExecutionException e) { rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); rm.done(); } } //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#endif public void addListener(final Listener listener) { try { fExecutor.execute( new DsfRunnable() { public void run() { fListeners.add(listener); } }); } catch (RejectedExecutionException e) {} } //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#endif public void removeListener(final Listener listener) { try { fExecutor.execute( new DsfRunnable() { public void run() { fListeners.remove(listener); } }); } catch (RejectedExecutionException e) {} } // Main processing function of this generator. //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif private void serviceQueue() { //#ifdef exercises // TODO Exercise 3 - Add logic to discard cancelled requests from queue. // Hint: Since serviceQueue() is called using the executor, and the // fQueue list can only be modified when running in the executor // thread. This method can safely iterate and modify fQueue without // risk of race conditions or concurrent modification exceptions. //#else //# for (Iterator<Request> requestItr = fQueue.iterator(); requestItr.hasNext();) { //# Request request = requestItr.next(); //# if (request.fRequestMonitor.isCanceled()) { //# request.fRequestMonitor.setStatus( //# new Status(IStatus.CANCEL, DsfExamplesPlugin.PLUGIN_ID, "Request canceled")); //# request.fRequestMonitor.done(); //# requestItr.remove(); //# } //# } //#endif // If a queue servicing is already scheduled, do nothing. if (fServiceQueueInProgress) { return; } if (fQueue.size() != 0) { // If there are requests to service, remove one from the queue and // schedule a runnable to process the request after a processing // delay. fServiceQueueInProgress = true; final Request request = fQueue.remove(0); fExecutor.schedule( new DsfRunnable() { public void run() { if (request instanceof CountRequest) { processCountRequest((CountRequest)request); } else if (request instanceof ItemRequest) { processItemRequest((ItemRequest)request); } // Reset the processing flag and process next // request. fServiceQueueInProgress = false; serviceQueue(); } }, PROCESSING_DELAY, TimeUnit.MILLISECONDS); } } //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif private void processCountRequest(CountRequest request) { @SuppressWarnings("unchecked") // Suppress warning about lost type info. DataRequestMonitor<Integer> rm = (DataRequestMonitor<Integer>)request.fRequestMonitor; rm.setData(fCount); rm.done(); } //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif private void processItemRequest(ItemRequest request) { @SuppressWarnings("unchecked") // Suppress warning about lost type info. DataRequestMonitor<String> rm = (DataRequestMonitor<String>)request.fRequestMonitor; if (fChangedIndexes.contains(request.fIndex)) { rm.setData("Changed: " + request.fIndex); } else { rm.setData(Integer.toString(request.fIndex)); } rm.done(); } /** * This method simulates changes in the supplier's data set. */ //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif private void randomChanges() { // Once every number of changes, reset the count, the rest of the // times just change certain values. if (++fCountResetTrigger % RANDOM_COUNT_CHANGE_INTERVALS == 0){ randomCountReset(); } else { randomDataChange(); } } /** * Calculates new size for provider's data set. */ //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif private void randomCountReset() { // Calculate the new count. Random random = new java.util.Random(); fCount = MIN_COUNT + Math.abs(random.nextInt()) % (MAX_COUNT - MIN_COUNT); // Reset the changed values. fChangedIndexes.clear(); // Notify listeners for (Listener listener : fListeners) { listener.countChanged(); } } /** * Invalidates a random range of indexes. */ //#ifdef exercises // TODO Exercise 4 - Add an annotation (ThreadSafe/ConfinedToDsfExecutor) // indicating allowed thread access to this class/method/member //#else //# @ConfinedToDsfExecutor("fExecutor") //#endif private void randomDataChange() { // Calculate the indexes to change. Random random = new java.util.Random(); Set<Integer> set = new HashSet<Integer>(); for (int i = 0; i < fCount * RANDOM_CHANGE_SET_PERCENTAGE / 100; i++) { set.add( new Integer(Math.abs(random.nextInt()) % fCount) ); } // Add the indexes to an overall set of changed indexes. fChangedIndexes.addAll(set); // Notify listeners for (Listener listener : fListeners) { listener.valuesChanged(set); } } }