/******************************************************************************* * 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.Collections; import java.util.HashSet; import java.util.Random; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Status; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; /** * Thread-based implementation of the data generator. * <p> * This generator is based around a queue of client requests and a thread which * reads the requests from the queue and processes them. The distinguishing * feature of this generator is that it uses a a blocking queue as the main * synchronization object. However, fListeners, fShutdown, and fChangedIndexes * fields also need to be thread-safe and so they implement their own * synchronization. * </p> */ public class DataGeneratorWithThread extends Thread implements IDataGenerator { // Request objects are used to serialize the interface calls into objects // which can then be pushed into a queue. abstract class Request { final RequestMonitor fRequestMonitor; Request(RequestMonitor rm) { fRequestMonitor = rm; } } class CountRequest extends Request { CountRequest(DataRequestMonitor<Integer> rm) { super(rm); } } class ItemRequest extends Request { final int fIndex; ItemRequest(int index, DataRequestMonitor<String> rm) { super(rm); fIndex = index; } } class ShutdownRequest extends Request { ShutdownRequest(RequestMonitor rm) { super(rm); } } // Main request queue of the data generator. The getValue(), getCount(), // and shutdown() methods write into the queue, while the run() method // reads from it. private final BlockingQueue<Request> fQueue = new LinkedBlockingQueue<Request>(); // ListenerList class provides thread safety. private ListenerList fListeners = new ListenerList(); // Current number of elements in this generator. private int fCount = MIN_COUNT; // Counter used to determine when to reset the element count. private int fCountResetTrigger = 0; // Elements which were modified since the last reset. private Set<Integer> fChangedIndexes = Collections.synchronizedSet(new HashSet<Integer>()); // Used to determine when to make changes in data. private long fLastChangeTime = System.currentTimeMillis(); // Flag indicating when the generator has been shut down. private AtomicBoolean fShutdown = new AtomicBoolean(false); public DataGeneratorWithThread() { // Immediately kick off the request processing thread. start(); } public void shutdown(RequestMonitor rm) { // Mark the generator as shut down. After the fShutdown flag is set, // all new requests should be shut down. if (!fShutdown.getAndSet(true)) { fQueue.add(new ShutdownRequest(rm)); } else { // rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); rm.done(); } } public void getCount(DataRequestMonitor<Integer> rm) { if (!fShutdown.get()) { fQueue.add(new CountRequest(rm)); } else { rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); rm.done(); } } public void getValue(int index, DataRequestMonitor<String> rm) { if (!fShutdown.get()) { fQueue.add(new ItemRequest(index, rm)); } else { rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Supplier shut down")); rm.done(); } } public void addListener(Listener listener) { fListeners.add(listener); } public void removeListener(Listener listener) { fListeners.remove(listener); } @Override public void run() { try { while(true) { // Get the next request from the queue. The time-out // ensures that that the random changes get processed. final Request request = fQueue.poll(100, TimeUnit.MILLISECONDS); // If a request was dequeued, process it. if (request != null) { // Simulate a processing delay. Thread.sleep(PROCESSING_DELAY); if (request instanceof CountRequest) { processCountRequest((CountRequest)request); } else if (request instanceof ItemRequest) { processItemRequest((ItemRequest)request); } else if (request instanceof ShutdownRequest) { // If shutting down, just break out of the while(true) // loop and thread will exit. request.fRequestMonitor.done(); break; } } // Simulate data changes. randomChanges(); } } catch (InterruptedException x) {} } 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(); } 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(); } private void randomChanges() { // Check if enough time is elapsed. if (System.currentTimeMillis() > fLastChangeTime + RANDOM_CHANGE_INTERVAL) { fLastChangeTime = System.currentTimeMillis(); // 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(); } } } 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 (Object listener : fListeners.getListeners()) { ((Listener)listener).countChanged(); } } 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 (Object listener : fListeners.getListeners()) { ((Listener)listener).valuesChanged(set); } } }