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