package org.epics.archiverappliance.engine.epics; import gov.aps.jca.Channel; import gov.aps.jca.event.AbstractEventDispatcher; import gov.aps.jca.event.AccessRightsEvent; import gov.aps.jca.event.AccessRightsListener; import gov.aps.jca.event.ConnectionEvent; import gov.aps.jca.event.ConnectionListener; import gov.aps.jca.event.ContextExceptionEvent; import gov.aps.jca.event.ContextExceptionListener; import gov.aps.jca.event.ContextMessageEvent; import gov.aps.jca.event.ContextMessageListener; import gov.aps.jca.event.GetEvent; import gov.aps.jca.event.GetListener; import gov.aps.jca.event.MonitorEvent; import gov.aps.jca.event.MonitorListener; import gov.aps.jca.event.PutEvent; import gov.aps.jca.event.PutListener; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import org.apache.log4j.Logger; /** * Attempt to distribute the load of serializing the event across multiple threads * All events that have a Channel as the source are sent to a thread based on the hash of the pv name. * Everything else uses one thread similar to QueuedEventDispacther. * @author mshankar * */ public class JCAEventDispatcherBasedOnPVName extends AbstractEventDispatcher { ExecutorService allOtherEventsHandler = null; ExecutorService[] pvNameEventsHandlers = new ExecutorService[Math.max(Runtime.getRuntime().availableProcessors()/4, 4)]; int numThreads = pvNameEventsHandlers.length; private static Logger logger = Logger.getLogger(JCAEventDispatcherBasedOnPVName.class.getName()); public JCAEventDispatcherBasedOnPVName() { super(); allOtherEventsHandler = Executors.newFixedThreadPool(1, new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r, "PVNameDispatcherAllOtherEvents"); return t; } }); for (int i = 0; i < numThreads; i++) { final int tnum = i; pvNameEventsHandlers[i] = Executors.newFixedThreadPool(1, new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r, "PVNameDispatcherPVNameEvents " + tnum); return t; } }); } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void dispatch(ContextMessageEvent arg0, List arg1) { try { allOtherEventsHandler.submit(new Runnable() { ContextMessageEvent arg0; List<ContextMessageListener> arg1; public Runnable initialize(ContextMessageEvent arg0, List<ContextMessageListener> arg1) { this.arg0 = arg0; this.arg1 = arg1; return this; } @Override public void run() { for(ContextMessageListener listener : arg1) { try { listener.contextMessage(arg0); } catch(Throwable t) { logger.warn("Exception dispatching context message event", t); } } } }.initialize(arg0, (List<ContextMessageListener>) arg1)); } catch(Throwable t) { logger.warn("Exception dispatching context message event", t); } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void dispatch(ContextExceptionEvent arg0, List arg1) { try { allOtherEventsHandler.submit(new Runnable() { ContextExceptionEvent arg0; List<ContextExceptionListener> arg1; public Runnable initialize(ContextExceptionEvent arg0, List<ContextExceptionListener> arg1) { this.arg0 = arg0; this.arg1 = arg1; return this; } @Override public void run() { for(ContextExceptionListener listener : arg1) { try { listener.contextException(arg0); } catch(Throwable t) { logger.warn("Exception dispatching context exception event", t); } } } }.initialize(arg0, (List<ContextExceptionListener>) arg1)); } catch(Throwable t) { logger.warn("Exception dispatching context exception event", t); } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void dispatch(ConnectionEvent arg0, List arg1) { String pvName = ((Channel)arg0.getSource()).getName(); String pvNameOnly = pvName.split("\\.")[0]; int threadId = Math.abs(pvNameOnly.hashCode()) % numThreads; try { pvNameEventsHandlers[threadId].submit(new Runnable() { ConnectionEvent arg0; List<ConnectionListener> arg1; public Runnable initialize(ConnectionEvent arg0, List<ConnectionListener> arg1) { this.arg0 = arg0; this.arg1 = arg1; return this; } @Override public void run() { for(ConnectionListener listener : arg1) { try { listener.connectionChanged(arg0); } catch(Throwable t) { logger.warn("Exception dispatching connection event", t); } } } }.initialize(arg0, (List<ConnectionListener>) arg1)); } catch(Throwable t) { logger.warn("Exception dispatching connection event", t); } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void dispatch(AccessRightsEvent arg0, List arg1) { String pvName = ((Channel)arg0.getSource()).getName(); String pvNameOnly = pvName.split("\\.")[0]; int threadId = Math.abs(pvNameOnly.hashCode()) % numThreads; try { pvNameEventsHandlers[threadId].submit(new Runnable() { AccessRightsEvent arg0; List<AccessRightsListener> arg1; public Runnable initialize(AccessRightsEvent arg0, List<AccessRightsListener> arg1) { this.arg0 = arg0; this.arg1 = arg1; return this; } @Override public void run() { for(AccessRightsListener listener : arg1) { try { listener.accessRightsChanged(arg0); } catch(Throwable t) { logger.warn("Exception dispatching access rights event", t); } } } }.initialize(arg0, (List<AccessRightsListener>) arg1)); } catch(Throwable t) { logger.warn("Exception dispatching access rights event", t); } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void dispatch(MonitorEvent arg0, List arg1) { String pvName = ((Channel)arg0.getSource()).getName(); String pvNameOnly = pvName.split("\\.")[0]; int threadId = Math.abs(pvNameOnly.hashCode()) % numThreads; try { pvNameEventsHandlers[threadId].submit(new Runnable() { MonitorEvent arg0; List<MonitorListener> arg1; public Runnable initialize(MonitorEvent arg0, List<MonitorListener> arg1) { this.arg0 = arg0; this.arg1 = arg1; return this; } @Override public void run() { for(MonitorListener listener : arg1) { try { listener.monitorChanged(arg0); } catch(Throwable t) { logger.warn("Exception dispatching monitor event", t); } } } }.initialize(arg0, (List<MonitorListener>) arg1)); } catch(Throwable t) { logger.warn("Exception dispatching monitor event", t); } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void dispatch(GetEvent arg0, List arg1) { String pvName = ((Channel)arg0.getSource()).getName(); String pvNameOnly = pvName.split("\\.")[0]; int threadId = Math.abs(pvNameOnly.hashCode()) % numThreads; try { pvNameEventsHandlers[threadId].submit(new Runnable() { GetEvent arg0; List<GetListener> arg1; public Runnable initialize(GetEvent arg0, List<GetListener> arg1) { this.arg0 = arg0; this.arg1 = arg1; return this; } @Override public void run() { for(GetListener listener : arg1) { try { listener.getCompleted(arg0); } catch(Throwable t) { logger.warn("Exception dispatching get event", t); } } } }.initialize(arg0, (List<GetListener>) arg1)); } catch(Throwable t) { logger.warn("Exception dispatching get event", t); } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void dispatch(PutEvent arg0, List arg1) { String pvName = ((Channel)arg0.getSource()).getName(); String pvNameOnly = pvName.split("\\.")[0]; int threadId = Math.abs(pvNameOnly.hashCode()) % numThreads; try { pvNameEventsHandlers[threadId].submit(new Runnable() { PutEvent arg0; List<PutListener> arg1; public Runnable initialize(PutEvent arg0, List<PutListener> arg1) { this.arg0 = arg0; this.arg1 = arg1; return this; } @Override public void run() { for(PutListener listener : arg1) { try { listener.putCompleted(arg0); } catch(Throwable t) { logger.warn("Exception dispatching put event", t); } } } }.initialize(arg0, (List<PutListener>) arg1)); } catch(Throwable t) { logger.warn("Exception dispatching put event", t); } } }