1f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullinpackage org.testng.internal.thread.graph; 2f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin 3a17da98c9400d760ba306ed0d4017c9a4e64074fCédric Beustimport org.testng.TestNGException; 4926eb7592326c928cdaee8d304e24cfba7758356Cédric Beustimport org.testng.collections.Lists; 5926eb7592326c928cdaee8d304e24cfba7758356Cédric Beustimport org.testng.internal.DynamicGraph; 6926eb7592326c928cdaee8d304e24cfba7758356Cédric Beustimport org.testng.internal.DynamicGraph.Status; 7926eb7592326c928cdaee8d304e24cfba7758356Cédric Beust 8f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullinimport java.io.BufferedWriter; 9f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullinimport java.io.File; 10f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullinimport java.io.FileWriter; 11f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullinimport java.io.IOException; 12f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullinimport java.util.List; 13f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullinimport java.util.concurrent.BlockingQueue; 145fe2ad0b59d40d720b50cbd87b09cdc82ef8d653Cédric Beustimport java.util.concurrent.ThreadFactory; 15f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullinimport java.util.concurrent.ThreadPoolExecutor; 16f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullinimport java.util.concurrent.TimeUnit; 17f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin 18f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin/** 19f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin * An Executor that launches tasks per batches. It takes a {@code DynamicGraph} 20f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin * of tasks to be run and a {@code IThreadWorkerFactory} to initialize/create 21f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin * {@code Runnable} wrappers around those tasks 22f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin */ 23f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullinpublic class GraphThreadPoolExecutor<T> extends ThreadPoolExecutor { 24f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin private static final boolean DEBUG = false; 25926eb7592326c928cdaee8d304e24cfba7758356Cédric Beust /** Set to true if you want to generate GraphViz graphs */ 26f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin private static final boolean DOT_FILES = false; 27f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin 28f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin private DynamicGraph<T> m_graph; 29f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin private List<Runnable> m_activeRunnables = Lists.newArrayList(); 30f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin private IThreadWorkerFactory<T> m_factory; 31f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin private List<String> m_dotFiles = Lists.newArrayList(); 32cc950e9c5f035c275e46d2e58901784e03b6959fCédric Beust private int m_threadCount; 33f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin 34f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin public GraphThreadPoolExecutor(DynamicGraph<T> graph, IThreadWorkerFactory<T> factory, int corePoolSize, 35f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { 362c44dbf27511f605ff49a08215900734b0ba38acCédric Beust super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue 372c44dbf27511f605ff49a08215900734b0ba38acCédric Beust /* , new TestNGThreadPoolFactory() */); 38f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin ppp("Initializing executor with " + corePoolSize + " threads and following graph " + graph); 39cc950e9c5f035c275e46d2e58901784e03b6959fCédric Beust m_threadCount = maximumPoolSize; 40f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin m_graph = graph; 41f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin m_factory = factory; 42a17da98c9400d760ba306ed0d4017c9a4e64074fCédric Beust 43a17da98c9400d760ba306ed0d4017c9a4e64074fCédric Beust if (m_graph.getFreeNodes().isEmpty()) { 44a17da98c9400d760ba306ed0d4017c9a4e64074fCédric Beust throw new TestNGException("The graph of methods contains a cycle:" + graph.getEdges()); 45a17da98c9400d760ba306ed0d4017c9a4e64074fCédric Beust } 46f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 47f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin 48f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin public void run() { 49f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin synchronized(m_graph) { 50f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin if (DOT_FILES) { 51f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin m_dotFiles.add(m_graph.toDot()); 52f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 53cc950e9c5f035c275e46d2e58901784e03b6959fCédric Beust List<T> freeNodes = m_graph.getFreeNodes(); 54f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin runNodes(freeNodes); 55f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 56f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 57f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin 58f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin /** 59f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin * Create one worker per node and execute them. 60f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin */ 61cc950e9c5f035c275e46d2e58901784e03b6959fCédric Beust private void runNodes(List<T> freeNodes) { 62cc950e9c5f035c275e46d2e58901784e03b6959fCédric Beust List<IWorker<T>> runnables = m_factory.createWorkers(freeNodes); 63f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin for (IWorker<T> r : runnables) { 64f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin m_activeRunnables.add(r); 65f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin ppp("Added to active runnable"); 66f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin setStatus(r, Status.RUNNING); 67f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin ppp("Executing: " + r); 68f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin try { 69f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin execute(r); 70cc950e9c5f035c275e46d2e58901784e03b6959fCédric Beust// if (m_threadCount > 1) execute(r); 71cc950e9c5f035c275e46d2e58901784e03b6959fCédric Beust// else r.run(); 72f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 73f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin catch(Exception ex) { 74f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin ex.printStackTrace(); 75f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 76f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 77f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 78f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin 79f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin private void setStatus(IWorker<T> worker, Status status) { 80f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin ppp("Set status:" + worker + " status:" + status); 811dabd5707b72960fd9e54d49e3f5e8747c5be93eCédric Beust if (status == Status.FINISHED) { 821dabd5707b72960fd9e54d49e3f5e8747c5be93eCédric Beust m_activeRunnables.remove(worker); 831dabd5707b72960fd9e54d49e3f5e8747c5be93eCédric Beust } 84f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin synchronized(m_graph) { 85f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin for (T m : worker.getTasks()) { 86f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin m_graph.setStatus(m, status); 87f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 88f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 89f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 90f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin 91f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin @Override 92f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin public void afterExecute(Runnable r, Throwable t) { 93f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin ppp("Finished runnable:" + r); 94f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin setStatus((IWorker<T>) r, Status.FINISHED); 95f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin synchronized(m_graph) { 96f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin ppp("Node count:" + m_graph.getNodeCount() + " and " 97cccd860d060374b9e4829eff804091ece176b9a2Cédric Beust + m_graph.getNodeCountWithStatus(Status.FINISHED) + " finished"); 98f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin if (m_graph.getNodeCount() == m_graph.getNodeCountWithStatus(Status.FINISHED)) { 99f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin ppp("Shutting down executor " + this); 100f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin if (DOT_FILES) { 101f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin generateFiles(m_dotFiles); 102f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 103f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin shutdown(); 104f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } else { 105f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin if (DOT_FILES) { 106f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin m_dotFiles.add(m_graph.toDot()); 107f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 108cc950e9c5f035c275e46d2e58901784e03b6959fCédric Beust List<T> freeNodes = m_graph.getFreeNodes(); 109f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin runNodes(freeNodes); 110f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 111f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 112f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin// if (m_activeRunnables.isEmpty() && m_index < m_runnables.getSize()) { 113f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin// runNodes(m_index++); 114f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin// } 115f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 116f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin 117f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin private void generateFiles(List<String> files) { 118f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin try { 119f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin File dir = File.createTempFile("TestNG-", ""); 120f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin dir.delete(); 121f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin dir.mkdir(); 122f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin for (int i = 0; i < files.size(); i++) { 123f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin File f = new File(dir, "" + (i < 10 ? "0" : "") + i + ".dot"); 124f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin BufferedWriter bw = new BufferedWriter(new FileWriter(f)); 125f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin bw.append(files.get(i)); 126f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin bw.close(); 127f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 128f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin if (DOT_FILES) { 129f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin System.out.println("Created graph files in " + dir); 130f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 131f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } catch(IOException ex) { 132f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin ex.printStackTrace(); 133f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 134f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 135f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin 136f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin private void ppp(String string) { 137f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin if (DEBUG) { 138cc950e9c5f035c275e46d2e58901784e03b6959fCédric Beust System.out.println("============ [GraphThreadPoolExecutor] " + Thread.currentThread().getId() + " " 139f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin + string); 140f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 141f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin } 142f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin 143f0d2855eea4ce0e0cfc37ec62ae55dcbcfb37724nullin} 1445fe2ad0b59d40d720b50cbd87b09cdc82ef8d653Cédric Beust 1455fe2ad0b59d40d720b50cbd87b09cdc82ef8d653Cédric Beustclass TestNGThreadPoolFactory implements ThreadFactory { 1465fe2ad0b59d40d720b50cbd87b09cdc82ef8d653Cédric Beust private int m_count = 0; 1475fe2ad0b59d40d720b50cbd87b09cdc82ef8d653Cédric Beust 1485fe2ad0b59d40d720b50cbd87b09cdc82ef8d653Cédric Beust @Override 1495fe2ad0b59d40d720b50cbd87b09cdc82ef8d653Cédric Beust public Thread newThread(Runnable r) { 1505fe2ad0b59d40d720b50cbd87b09cdc82ef8d653Cédric Beust Thread result = new Thread(r); 1515fe2ad0b59d40d720b50cbd87b09cdc82ef8d653Cédric Beust result.setName("TestNG-" + m_count++); 1525fe2ad0b59d40d720b50cbd87b09cdc82ef8d653Cédric Beust return result; 1535fe2ad0b59d40d720b50cbd87b09cdc82ef8d653Cédric Beust } 1545fe2ad0b59d40d720b50cbd87b09cdc82ef8d653Cédric Beust} 155