/* * @@COPYRIGHT@@ */ package com.cosylab.acs.maci.manager; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; import java.util.logging.Level; import java.util.logging.Logger; import com.cosylab.acs.maci.ComponentInfo; import com.cosylab.acs.maci.ContainerInfo; import com.cosylab.acs.maci.HandleConstants; import com.cosylab.acs.maci.IntArray; import com.cosylab.acs.maci.RemoteException; /** * Manages TS. */ @SuppressWarnings("unchecked") public class ComponentInfoTopologicalSortManager implements Runnable { /** * Components data store. */ private HandleDataStore components; /** * Containers data store. */ private HandleDataStore containers; /** * Stastus flag. */ private volatile boolean destroyed = false; /** * Lock. */ private ReaderPreferenceReadWriteLock activationPendingRWLock; //private ReadWriteLock activationPendingRWLock; /** * Dirty map. */ private HashSet dirtyContainerMap = new HashSet(); /** * Lock. */ private Object listLock = new Object(); /** * Current ordered TS list. */ private ComponentInfo[] currentTSList = new ComponentInfo[0]; /** * List of all pending container shutdowns. */ private Set pendingContainerShutdown; /** * Thread pool (guarantees order of execution). */ private ThreadPoolExecutor threadPool; /** * Logger. */ private Logger logger; /** * @param components * @param containers */ public ComponentInfoTopologicalSortManager(HandleDataStore components, HandleDataStore containers, /* ReadWriteLock */ ReaderPreferenceReadWriteLock activationPendingRWLock, Set pendingContainerShutdown, ThreadPoolExecutor threadPool, Logger logger) { this.components = components; this.containers = containers; this.activationPendingRWLock = activationPendingRWLock; this.pendingContainerShutdown = pendingContainerShutdown; this.threadPool = threadPool; this.logger = logger; // make all containers dirty synchronized (containers) { int h = containers.first(); while (h != 0) { int containerHandle = ((ContainerInfo)containers.get(h)).getHandle(); synchronized (dirtyContainerMap) { dirtyContainerMap.add(new Integer(containerHandle)); } h = containers.next(h); } } new Thread(this, "ComponentInfoTopologicalSortManager").start(); } /** * (Sync is clean since activationPendingRWLock is acquired and all * notify request is issued only from its reader lock). * @see java.lang.Runnable#run() */ public void run() { while (!destroyed) { synchronized (this) { int dirtyContainersCount; synchronized (dirtyContainerMap) { dirtyContainersCount = dirtyContainerMap.size(); } if (dirtyContainersCount == 0) { try { this.wait(); } catch (InterruptedException e) { return; } } } if (destroyed) return; // aquire writer lock to prevent activation/deactivation activationPendingRWLock.writeLock().lock(); try { Integer[] conts; synchronized (dirtyContainerMap) { conts = new Integer[dirtyContainerMap.size()]; dirtyContainerMap.toArray(conts); dirtyContainerMap.clear(); } ComponentInfo[] orderedList; synchronized (components) { List list = ComponentInfoTopologicalSort.sort(components); orderedList = new ComponentInfo[list.size()]; list.toArray(orderedList); } synchronized (listLock) { currentTSList = orderedList; } for (int i = 0; i < conts.length; i++) { int handle = conts[i].intValue() & HandleConstants.HANDLE_MASK; ContainerInfo containerInfo = null; synchronized (containers) { if (containers.isAllocated(handle)) containerInfo = (ContainerInfo)containers.get(handle); } if (containerInfo == null || containerInfo.getName() == null) break; String containerName = containerInfo.getName(); // check if shutdown state if (pendingContainerShutdown.contains(containerName)) break; IntArray containerOrderdList = new IntArray(10); for (int j = 0; j < orderedList.length; j++) { // if component already deactivated, skip it if (orderedList[j].getContainer() == 0) continue; // this is null proof if (containerName.equals(orderedList[j].getContainerName())) { containerOrderdList.add(orderedList[j].getHandle()); } } // optimization if (containerOrderdList.size() > 1) notifyContainerShutdownOrder(containerInfo, containerOrderdList.toArray()); } } finally { activationPendingRWLock.writeLock().unlock(); } try{ Thread.sleep(100); }catch(InterruptedException ex){ logger.log(Level.WARNING, "Exception caught while waiting in run() method"); ex.printStackTrace(); } } } /** * Request topological sort. */ public void requestTopologicalSort() { // aquire writer lock to prevent activation/deactivation activationPendingRWLock.writeLock().lock(); try { ComponentInfo[] orderedList; synchronized (components) { List list = ComponentInfoTopologicalSort.sort(components); orderedList = new ComponentInfo[list.size()]; list.toArray(orderedList); } synchronized (listLock) { currentTSList = orderedList; } } finally { activationPendingRWLock.writeLock().unlock(); } } /** * Notify about (possible) topology change and initiates sorting. * @param containerHandleToNotify container handle to notify. */ public synchronized void notifyTopologyChange(int containerHandleToNotify) { synchronized (dirtyContainerMap) { dirtyContainerMap.add(new Integer(containerHandleToNotify)); } this.notify(); } /** * Get component shutdown order for container. * @param containerInfo valid container's info, if <code>null</code> complete TS is returned. * @return component shutdown order */ public ComponentInfo[] getComponentShutdownOrder(ContainerInfo containerInfo) { if (containerInfo == null) { synchronized (listLock) { return currentTSList; } } String containerName = containerInfo.getName(); ComponentInfo[] orderedList; synchronized (listLock) { orderedList = currentTSList; } List containerOrderdList = new ArrayList(10); for (int j = 0; j < orderedList.length; j++) { // if component already deactivated, skip it if (orderedList[j].getContainer() == 0) continue; // this is null proof if (containerName.equals(orderedList[j].getContainerName())) { containerOrderdList.add(orderedList[j]); } } ComponentInfo[] retVal = new ComponentInfo[containerOrderdList.size()]; containerOrderdList.toArray(retVal); return retVal; } /** * Destroy (terminate thread). */ public synchronized void destroy() { destroyed = true; this.notify(); } /** * Informs containers abouts its component shutdown order. * @param containerInfo container to inform * @param handles ordered list of component handles */ private Map pendingContainerNotifications = new HashMap(); private void notifyContainerShutdownOrder(ContainerInfo containerInfo, int[] handles) { /** * Task thats invokes <code>Container#shutdown</code> method. */ class ContainerSetShutdownOrderTask implements Runnable { private boolean done = false; private int[] handles; private ContainerInfo containerInfo; public ContainerSetShutdownOrderTask(ContainerInfo containerInfo, int[] handles) { this.containerInfo = containerInfo; this.handles = handles; } /** * Add new notification. This will keep only the latest one. * @param handles * @return <code>true<code> if notification is being accepted */ public synchronized boolean addNotification(int[] handles) { if (done) return false; this.handles = handles; return true; } public void run() { final int MAX_RETRIES = 3; for (int retries = 0; retries < MAX_RETRIES; retries++) { int[] hs = null; try { synchronized (this) { hs = handles; } (containerInfo.getContainer()).set_component_shutdown_order(hs); synchronized (this) { if (handles == hs) { // no new handles, done... done = true; break; } else { // all OK, new handles, reset retries retries = 0; continue; } } } catch (RemoteException re) { logger.log(Level.SEVERE, "RemoteException caught while invoking 'Container.set_component_shutdown_order' on "+containerInfo+".", re); } catch (Throwable ex) { logger.log(Level.SEVERE, "Unhandled exception caught while invoking 'Container.set_component_shutdown_order' on "+containerInfo+".", ex); } // decrease retries, if we have a new set of handles synchronized (this) { if (handles != hs) retries--; } } } } synchronized (pendingContainerNotifications) { // get or create task ContainerSetShutdownOrderTask task = (ContainerSetShutdownOrderTask)pendingContainerNotifications.get(containerInfo); if (task == null || !task.addNotification(handles)) { // spawn new task which surely does not block try { threadPool.execute(new ContainerSetShutdownOrderTask(containerInfo, handles)); } catch (RejectedExecutionException ree) { // noop (threadPool) most likely in shutdown state } } } } }