title: Python Future return when First_Exception or First_Completed
body: <p>I am trying to make a scheduler that executes a DAG (Directed Acyclic Graph) in parlallel.</p>
  <p>Currently I am using Threads to execute every node of the Graph. However at some point I needed to wait for <strong>any</strong> thread to end to begin executing code in my small scheduler.</p>
  <p>I am having a hard time implementing this <em>wait for <strong>ANY</strong> thread to finish</em> properly.
  I tried using <a href="https://docs.python.org/3/library/threading.html#condition-objects" rel="nofollow noreferrer">Conditional Variables</a> so that I can wait for the event to be notified from within the thread using <code>notify(1)</code> and <code>wait</code> for the notification in the main thread. however this doesn't work properly.
  Currently my best implementation is to use <code>Event</code>; it sort of works...</p>
  <p>The best option I found is to use <a href="https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.wait" rel="nofollow noreferrer">ThreadPoolExecutor</a> and wait for FIRST_COMPLETED. However this doesn't completely work because the function `wait(fs, return_when=FIRST_COMPLETED) doesn't return if an exception occurs within the future execution...</p>
  <p>Another option is to use FIRST_EXCEPTION instead but this isn't adequate because I need to wait for <strong>ANY</strong> Future to complete and not wait for ALL futures to complete in the normal case which is not what the documentation states:</p>
  <ul>
  <li>FIRST_COMPLETED: The function will return when any future finishes or is cancelled.</li>
  <li>FIRST_EXCEPTION: The function will return when any future finishes by raising an exception. If no future raises an exception then it is equivalent to ALL_COMPLETED.</li>
  </ul>
  <pre><code># current pseudo code
  
  def exec_func(event: Event, graph):
    #do calculations
    my_node = ...
    graph.remove_node(my_node)
    event.set() # notify main thread that this thread **will** finish
  
  
  graph = ...
  while len(graph):
    if condition:
      event.wait()
      event.clear()
    # complex code
    # launch new thread
    Thread(target=a_root_of_graph(graph).exec_func, kwargs={'event':event, 'graph': graph}).start()
  </code></pre>
  <p>I just don't like the way this works... all the solutions I tried seems hacky at best and I want a simple solution to detect the end of any thread.</p>
  <p>Thank you and sorry for the bad explanation. Feel free to put down a comment to ask for clarification and more code</p>