]> https://gitweb.dealii.org/ - dealii.git/commitdiff
update taskflow to 2.5.0 10468/head
authorTimo Heister <timo.heister@gmail.com>
Fri, 5 Jun 2020 11:47:15 +0000 (07:47 -0400)
committerTimo Heister <timo.heister@gmail.com>
Fri, 5 Jun 2020 17:37:20 +0000 (13:37 -0400)
31 files changed:
bundled/README.md
bundled/setup_bundled.cmake
bundled/taskflow-2.5.0/LICENSE [moved from bundled/taskflow-2.4.0/LICENSE with 98% similarity]
bundled/taskflow-2.5.0/README.md [moved from bundled/taskflow-2.4.0/README.md with 71% similarity]
bundled/taskflow-2.5.0/include/taskflow/core/error.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/core/error.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/core/executor.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/core/executor.hpp with 80% similarity]
bundled/taskflow-2.5.0/include/taskflow/core/flow_builder.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/core/flow_builder.hpp with 94% similarity]
bundled/taskflow-2.5.0/include/taskflow/core/graph.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/core/graph.hpp with 99% similarity]
bundled/taskflow-2.5.0/include/taskflow/core/notifier.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/core/notifier.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/core/observer.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/core/observer.hpp with 91% similarity]
bundled/taskflow-2.5.0/include/taskflow/core/task.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/core/task.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/core/taskflow.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/core/taskflow.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/core/topology.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/core/topology.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/core/tsq.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/core/tsq.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/cuda/cuda_device.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/cuda/cuda_device.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/cuda/cuda_error.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/cuda/cuda_error.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/cuda/cuda_flow.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/cuda/cuda_flow.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/cuda/cuda_graph.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/cuda/cuda_graph.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/cuda/cuda_task.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/cuda/cuda_task.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/declarations.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/declarations.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/nstd/any.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/nstd/any.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/nstd/optional.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/nstd/optional.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/nstd/variant.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/nstd/variant.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/taskflow.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/taskflow.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/utility/object_pool.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/utility/object_pool.hpp with 99% similarity]
bundled/taskflow-2.5.0/include/taskflow/utility/os.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/utility/os.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/utility/passive_vector.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/utility/passive_vector.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/utility/singleton.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/utility/singleton.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/utility/stringify.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/utility/stringify.hpp with 100% similarity]
bundled/taskflow-2.5.0/include/taskflow/utility/traits.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/utility/traits.hpp with 99% similarity]
bundled/taskflow-2.5.0/include/taskflow/utility/uuid.hpp [moved from bundled/taskflow-2.4.0/include/taskflow/utility/uuid.hpp with 100% similarity]

index f7ec2e9144483e4b14796610b7c20e2c4fc1f127..2cd62d722289f7058994c6a2d4dadc85d48a79a7 100644 (file)
@@ -19,13 +19,13 @@ and licensed under the Boost Software License Version 1.0. See
 A full version of the library can be downloaded at http://www.boost.org/.
 
 
-cpp-taskflow-*
+taskflow-*
 --------------
 
-Contains the cpp-taskflow project licensed under the MIT license.
+Contains the taskflow project licensed under the MIT license.
 
 A full version of the project project can be downloaded at
-https://cpp-taskflow.github.io/.
+https://taskflow.github.io/.
 
 
 muparser_*
index 38f5b5e782fdcc491397e238ecfe5226f1a82ff1..11cdeaf44c9b0dd51e405b2cc98452196fc711b1 100644 (file)
@@ -40,7 +40,7 @@ OPTION(DEAL_II_FORCE_BUNDLED_TASKFLOW
   "Always use the bundled taskflow header library instead of an external one."
   OFF)
 
-SET(TASKFLOW_FOLDER "${CMAKE_SOURCE_DIR}/bundled/taskflow-2.4.0")
+SET(TASKFLOW_FOLDER "${CMAKE_SOURCE_DIR}/bundled/taskflow-2.5.0")
 
 
 #
similarity index 98%
rename from bundled/taskflow-2.4.0/LICENSE
rename to bundled/taskflow-2.5.0/LICENSE
index 889eb137e4733572fc705cda39efbf77c8303e83..871f579f65bdefc2fa7f014ed2472a4841456fdc 100644 (file)
@@ -1,4 +1,4 @@
-MIT License
+Taskflow LICENSE
 
 Copyright (c) 2018-2020 T.-W. Huang, C.-X. Lin, G. Guo, and M. Wong
 
similarity index 71%
rename from bundled/taskflow-2.4.0/README.md
rename to bundled/taskflow-2.5.0/README.md
index 07fa377a85c4480301f61e61d58e8560b5b89f95..780f4df9ef6a5952bd32a2414e872b54da0691fb 100644 (file)
@@ -1,32 +1,33 @@
-# Cpp-Taskflow <img align="right" width="10%" src="image/cpp-taskflow_logo.png">
+# Taskflow <img align="right" width="10%" src="image/taskflow_logo.png">
 
-[![Codacy Badge](https://api.codacy.com/project/badge/Grade/bb04cb8e4aca401b8206c054e79fd5e3)](https://app.codacy.com/app/tsung-wei-huang/cpp-taskflow?utm_source=github.com&utm_medium=referral&utm_content=cpp-taskflow/cpp-taskflow&utm_campaign=Badge_Grade_Dashboard)
-[![Linux Build Status](https://travis-ci.com/cpp-taskflow/cpp-taskflow.svg?branch=master)](https://travis-ci.com/cpp-taskflow/cpp-taskflow)
-[![Windows Build status](https://ci.appveyor.com/api/projects/status/te9bjp4yfhq7f8hq?svg=true)](https://ci.appveyor.com/project/TsungWeiHuang/cpp-taskflow)
+[![Codacy Badge](https://api.codacy.com/project/badge/Grade/3bbdc89f9a7a41eaa17559fab8a64cde)](https://app.codacy.com/gh/taskflow/taskflow?utm_source=github.com&utm_medium=referral&utm_content=taskflow/taskflow&utm_campaign=Badge_Grade_Dashboard)
+[![Linux Build Status](https://travis-ci.com/taskflow/taskflow.svg?branch=master)](https://travis-ci.com/taskflow/taskflow)
+[![Windows Build status](https://ci.appveyor.com/api/projects/status/rbjl16i6c9ahxr16?svg=true)](https://ci.appveyor.com/project/tsung-wei-huang/taskflow)
 [![Wiki](image/api-doc.svg)][wiki]
-[![TFProf](image/tfprof.svg)](https://cpp-taskflow.github.io/tfprof/)
+[![TFProf](image/tfprof.svg)](https://taskflow.github.io/tfprof/)
 [![Cite](image/cite-arXiv.svg)](https://arxiv.org/abs/2004.10908v2)
 
-Cpp-Taskflow helps you quickly write parallel programs with high performance scalability
-and simultaneous high productivity.
+Taskflow helps you quickly write parallel tasks programs in modern C++
 
-# Why Cpp-Taskflow?
+:exclamation: Starting from [v2.5.0](https://github.com/taskflow/taskflow/releases/tag/2.5.0), we have renamed cpp-taskflow to ***taskflow*** to broaden its support and future application scopes. The core codebase remains *unchanged*. You may only need to [change the remote URL](https://help.github.com/en/github/using-git/changing-a-remotes-url) to this new repository. Thank your for the support!
 
-Cpp-Taskflow is faster, more expressive, and easier for drop-in integration
+# Why Taskflow?
+
+Taskflow is faster, more expressive, and easier for drop-in integration
 than many of existing task programming frameworks
 in handling complex parallel workloads.
 
 ![](image/performance.png)
 
-Cpp-Taskflow lets you quickly implement task decomposition strategies
+Taskflow lets you quickly implement task decomposition strategies
 that incorporate both regular and irregular compute patterns,
 together with an efficient *work-stealing* scheduler to optimize your multithreaded performance.
 
-| [Static Tasking](#get-started-with-cpp-taskflow) | [Dynamic Tasking](#dynamic-tasking) |
+| [Static Tasking](#get-started-with-taskflow) | [Dynamic Tasking](#dynamic-tasking) |
 | :------------: | :-------------: |
 | ![](image/static_graph.svg) | <img align="right" src="image/dynamic_graph.svg" width="100%"> |
 
-Cpp-Taskflow supports conditional tasking for you to make rapid control-flow decisions
+Taskflow supports conditional tasking for you to make rapid control-flow decisions
 across dependent tasks to implement cycles and conditions that were otherwise difficult to do
 with existing tools.
 
@@ -34,7 +35,7 @@ with existing tools.
 | :-----------------: |
 | ![](image/condition.svg) |
 
-Cpp-Taskflow is composable. You can create large parallel graphs through
+Taskflow is composable. You can create large parallel graphs through
 composition of modular and reusable blocks that are easier to optimize
 at an individual scope.
 
@@ -42,7 +43,7 @@ at an individual scope.
 | :---------------: |
 |![](image/framework.svg)|
 
-Cpp-Taskflow supports heterogeneous tasking for you to 
+Taskflow supports heterogeneous tasking for you to 
 accelerate a wide range of scientific computing applications
 by harnessing the power of CPU-GPU collaborative computing.
 
@@ -50,50 +51,54 @@ by harnessing the power of CPU-GPU collaborative computing.
 | :-----------------: |
 | ![](image/cudaflow.svg) |
 
+
+Taskflow provides visualization and tooling needed for profiling Taskflow programs.
+
+| [Taskflow Profiler](https://taskflow.github.io/tfprof) |
+| :-----------------: |
+| ![](image/tfprof.png) |
+
 We are committed to support trustworthy developments for both academic and industrial research projects 
-in parallel computing. Check out [Who is Using Cpp-Taskflow](#who-is-using-cpp-taskflow) and what our users say:
+in parallel computing. Check out [Who is Using Taskflow](#who-is-using-taskflow) and what our users say:
 
-+ *"Cpp-Taskflow is the cleanest Task API I've ever seen." [Damien Hocking @Corelium Inc](http://coreliuminc.com)*
-+ *"Cpp-Taskflow has a very simple and elegant tasking interface. The performance also scales very well." [Glen Fraser][totalgee]*
-+ *"Cpp-Taskflow lets me handle parallel processing in a smart way." [Hayabusa @Cpp-Learning](https://cpp-learning.com/cpp-taskflow/)*
-+ *"Cpp-Taskflow improves the throughput of our graph engine in just a few hours of coding." [Jean-Michaël @KDAB](https://ossia.io/)*
++ *"Taskflow is the cleanest Task API I've ever seen." [Damien Hocking @Corelium Inc](http://coreliuminc.com)*
++ *"Taskflow has a very simple and elegant tasking interface. The performance also scales very well." [Glen Fraser][totalgee]*
++ *"Taskflow lets me handle parallel processing in a smart way." [Hayabusa @Learning](https://cpp-learning.com/cpp-taskflow/)*
++ *"Taskflow improves the throughput of our graph engine in just a few hours of coding." [Jean-Michaël @KDAB](https://ossia.io/)*
 + *"Best poster award for open-source parallel programming library." [Cpp Conference 2018][Cpp Conference 2018]*
 + *"Second Prize of Open-source Software Competition." [ACM Multimedia Conference 2019](https://tsung-wei-huang.github.io/img/mm19-ossc-award.jpg)*
 
 See a quick [presentation][Presentation] and 
-visit the [documentation][wiki] to learn more about Cpp-Taskflow.
+visit the [documentation][wiki] to learn more about Taskflow.
 Technical details can be referred to our [arXiv paper](https://arxiv.org/abs/2004.10908v2).
 
 # Table of Contents
 
-* [Get Started with Cpp-Taskflow](#get-started-with-cpp-taskflow)
+* [Get Started with Taskflow](#get-started-with-taskflow)
 * [Create a Taskflow Application](#create-a-taskflow-application)
    * [Step 1: Create a Taskflow](#step-1-create-a-taskflow)
    * [Step 2: Define Task Dependencies](#step-2-define-task-dependencies)
    * [Step 3: Execute a Taskflow](#step-3-execute-a-taskflow)
 * [Dynamic Tasking](#dynamic-tasking)
 * [Conditional Tasking](#conditional-tasking)
-   * [Step 1: Create a Condition Task](#step-1-create-a-condition-task)
-   * [Step 2: Scheduling Rules for Condition Tasks](#step-2-scheduling-rules-for-condition-tasks)
 * [Composable Tasking](#composable-tasking)
 * [Concurrent CPU-GPU Tasking](#concurrent-cpu-gpu-tasking)
    * [Step 1: Create a cudaFlow](#step-1-create-a-cudaflow)
    * [Step 2: Compile and Execute a cudaFlow](#step-2-compile-and-execute-a-cudaflow)
 * [Visualize a Taskflow Graph](#visualize-a-taskflow-graph)
-* [Monitor Thread Activities](#monitor-thread-activities)
 * [API Reference](#api-reference)
 * [System Requirements](#system-requirements)
 * [Compile Unit Tests, Examples, and Benchmarks](#compile-unit-tests-examples-and-benchmarks)
-* [Who is Using Cpp-Taskflow?](#who-is-using-cpp-taskflow)
+* [Who is Using Taskflow?](#who-is-using-taskflow)
 
 
-# Get Started with Cpp-Taskflow
+# Get Started with Taskflow
 
-The following example [simple.cpp](./examples/simple.cpp) shows the basic Cpp-Taskflow API
+The following example [simple.cpp](./examples/simple.cpp) shows the basic Taskflow API
 you need in most applications.
 
 ```cpp
-#include <taskflow/taskflow.hpp>  // Cpp-Taskflow is header-only
+#include <taskflow/taskflow.hpp>  // Taskflow is header-only
 
 int main(){
   
@@ -131,7 +136,7 @@ TaskD
 
 # Create a Taskflow Application
 
-Cpp-Taskflow defines a very expressive API to create task dependency graphs.
+Taskflow defines a very expressive API to create task dependency graphs.
 Most applications are developed through the following three steps:
 
 ## Step 1: Create a Taskflow
@@ -258,11 +263,7 @@ the execution of a subflow and so on.
 
 # Conditional Tasking
 
-Taskflow supports *conditional tasking* for users to implement *dynamic* and *cyclic* control flows.
-You can create highly versatile and efficient parallel patterns through condition tasks.
-
-## Step 1: Create a Condition Task
-
+Taskflow supports *conditional tasking* for users to implement *general* control flow with cycles and conditionals.
 A *condition task* evalutes a set of instructions and returns an integer index
 of the next immediate successor to execute.
 The index is defined with respect to the order of its successor construction.
@@ -286,35 +287,6 @@ cond.precede(cond, stop);  // cond--0-->cond, cond--1-->stop
 executor.run(tf).wait();
 ```
 
-If the return value from `cond` is 0, it loops back to itself, or otherwise to `stop`.
-Cpp-Taskflow terms the preceding link from a condition task a *weak dependency*
-(dashed lines above).
-Others are *strong depedency* (solid lines above).
-
-
-## Step 2: Scheduling Rules for Condition Tasks
-
-When you submit a taskflow to an executor,
-the scheduler starts with tasks of *zero dependency* (both weak and strong dependencies)
-and continues to execute successive tasks whenever *strong dependencies* are met.
-However, 
-the scheduler skips this rule for a condition task and jumps directly to its successor
-indexed by the return value.
-
-![](image/conditional-tasking-rules.svg)
-
-It is users' responsibility to ensure a taskflow is properly conditioned. 
-Top things to avoid include no source tasks to start with and task race.
-The figure shows common pitfalls and their remedies.
-In the risky scenario, task X may not be raced if P and M is exclusively
-branching to X.
-
-![](image/conditional-tasking-pitfalls.svg)
-
-
-A good practice for avoiding mistakes of conditional tasking is to infer the execution flow of your graphs based on our scheduling rules.
-Make sure there is no task race.
-
 <div align="right"><b><a href="#table-of-contents">[↑]</a></b></div>
 
 
@@ -355,7 +327,7 @@ to compose a larger taskflow and so on.
 
 # Concurrent CPU-GPU Tasking
 
-Cpp-Taskflow enables concurrent CPU-GPU tasking by leveraging
+Taskflow enables concurrent CPU-GPU tasking by leveraging
 [Nvidia CUDA Toolkit][cuda-toolkit].
 You can harness the power of CPU-GPU collaborative computing 
 to implement heterogeneous decomposition algorithms.
@@ -484,52 +456,29 @@ tf.dump(std::cout);       // dump the graph including dynamic tasks
 
 
 
-# Monitor Thread Activities 
-
-Cpp-Taskflow Profiler ([TFProf](https://github.com/cpp-taskflow/tfprof)) 
-provides the visualization and tooling needed for profiling cpp-taskflow programs.
-
-<p align="center">
-   <a href="https://cpp-taskflow.github.io/tfprof/">
-     <img width="100%" src="image/tfprof.png">
-   </a>
-</p>
-
-<div align="right"><b><a href="#table-of-contents">[↑]</a></b></div>
-
 # API Reference
 
 The official [documentation][wiki] explains a complete list of 
-Cpp-Taskflow API. 
+Taskflow API. 
 Here, we highlight commonly used methods.
 
 ## Taskflow API
 
 The class `tf::Taskflow` is the main place to create a task dependency graph.
-The table below summarizes a list of commonly used methods.
-
-| Method   | Argument  | Return  | Description |
-| -------- | --------- | ------- | ----------- |
-| emplace  | callables | tasks   | creates a task with a given callable(s) |
-| placeholder     | none        | task         | inserts a node without any work; work can be assigned later |
-| parallel_for    | beg, end, callable, chunk | task pair | concurrently applies the callable chunk by chunk to the result of dereferencing every iterator in the range | 
-| parallel_for    | beg, end, step, callable, chunk | task pair | concurrently applies the callable chunk by chunk to an index-based range with a step size | 
-| num_workers     | none        | size | queries the number of working threads in the pool |  
-| dump            | ostream     | none | dumps the taskflow to an output stream in GraphViz format |
 
 ### *emplace/placeholder*
 
 You can use `emplace` to create a task from a target callable.
 
 ```cpp
-tf::Task task = tf.emplace([] () { std::cout << "my task\n"; });
+tf::Task task = taskflow.emplace([] () { std::cout << "my task\n"; });
 ```
 
 When a task cannot be determined beforehand, you can create a placeholder and assign the callable later.
 
 ```cpp
-tf::Task A = tf.emplace([](){});
-tf::Task B = tf.placeholder();
+tf::Task A = taskflow.emplace([](){});
+tf::Task B = taskflow.placeholder();
 A.precede(B);
 B.work([](){ /* do something */ });
 ```
@@ -542,7 +491,7 @@ The method `parallel_for` creates a subgraph that applies the callable to each i
 
 ```cpp
 auto v = {'A', 'B', 'C', 'D'};
-auto [S, T] = tf.parallel_for(
+auto [S, T] = taskflow.parallel_for(
   v.begin(),    // iterator to the beginning
   v.end(),      // iterator to the end
   [] (int i) { 
@@ -558,7 +507,7 @@ You can specify a *chunk* size (default one) in the last argument to force a tas
 
 ```cpp
 auto v = {'A', 'B', 'C', 'D'};
-auto [S, T] = tf.parallel_for(
+auto [S, T] = taskflow.parallel_for(
   v.begin(),    // iterator to the beginning
   v.end(),      // iterator to the end
   [] (int i) { 
@@ -575,7 +524,7 @@ starting index, ending index (exclusive), and step size.
 
 ```cpp
 // [0, 11) with a step size of 2
-auto [S, T] = tf.parallel_for(
+auto [S, T] = taskflow.parallel_for(
   0, 11, 2, 
   [] (int i) {
     std::cout << "parallel_for on index " << i << std::endl;
@@ -589,20 +538,7 @@ auto [S, T] = tf.parallel_for(
 
 Each time you create a task, the taskflow object adds a node to the present task dependency graph
 and return a *task handle* to you.
-A task handle is a lightweight object that defines a set of methods for users to
-access and modify the attributes of the associated task.
-The table below summarizes a list of commonly used methods.
-
-| Method         | Argument    | Return | Description |
-| -------------- | ----------- | ------ | ----------- |
-| name           | string      | self   | assigns a human-readable name to the task |
-| work           | callable    | self   | assigns a work of a callable object to the task |
-| precede        | task list   | self   | enables this task to run *before* the given tasks |
-| succeed        | task list   | self   | enables this task to run *after* the given tasks |
-| num_dependents | none        | size   | returns the number of dependents (inputs) of this task |
-| num_successors | none        | size   | returns the number of successors (outputs) of this task |
-| empty          | none        | bool   | returns true if the task points to a graph node or false otherwise |
-| has_work       | none        | bool   | returns true if the task points to a graph node with a callable assigned |
+You can access or modify the attributes of the associated task node.
 
 ### *name*
 
@@ -620,9 +556,9 @@ The method `work` lets you assign a callable to a task.
 A.work([] () { std::cout << "hello world!"; });
 ```
 
-### *precede*
+### *precede/succeed*
 
-The method `precede` lets you add a preceding link from self to other tasks.
+The method `precede/succedd` lets you add a preceding/succeeding link between tasks.
 
 <img align="right" width="30%" src="image/broadcast.svg">
 
@@ -635,7 +571,7 @@ The method `succeed` is similar to `precede` but operates in the opposite direct
 
 ### *empty/has_work*
 
-A task is empty is it is not associated with any graph node.
+A task is empty if it is not associated with any graph node.
 
 ```cpp
 tf::Task task;  // assert(task.empty());
@@ -649,20 +585,11 @@ tf::Task task = taskflow.placeholder();  // assert(!task.has_work());
 
 ## Executor API
 
-The class `tf::Executor` is used for execution of one or multiple taskflow objects.
-The table below summarizes a list of commonly used methods. 
-
-| Method    | Argument       | Return        | Description              |
-| --------- | -------------- | ------------- | ------------------------ |
-| run       | taskflow       | future | runs the taskflow once    |
-| run_n     | taskflow, N    | future | runs the taskflow N times |
-| run_until | taskflow, binary predicate | future | keeps running the taskflow until the predicate becomes true |
-| wait_for_all | none | none | blocks until all running tasks finish |
-| make_observer | arguments to forward to user-derived constructor | pointer to the observer | creates an observer to monitor the thread activities of the executor |
+The class `tf::Executor` is used for executing one or multiple taskflow objects.
 
 ### *run/run_n/run_until*
 
-The run series are non-blocking call to execute a taskflow graph.
+The run series are *thread-safe* and *non-blocking* calls to execute a taskflow.
 Issuing multiple runs on the same taskflow will automatically synchronize 
 to a sequential chain of executions.
 
@@ -678,21 +605,21 @@ The first run finishes before the second run, and the second run finishes before
 
 # System Requirements
 
-To use the latest [Cpp-Taskflow](https://github.com/cpp-taskflow/cpp-taskflow/archive/master.zip), you only need a [C++14][C++14] compiler.
+To use the latest [Taskflow](https://github.com/taskflow/taskflow/archive/master.zip), you only need a [C++14][C++14] compiler.
 
 + GNU C++ Compiler at least v5.0 with -std=c++14
 + Clang C++ Compiler at least v4.0 with -std=c++14
-+ Microsoft Visual Studio at least v15.7 (MSVC++ 19.14); see [vcpkg guide](https://github.com/cpp-taskflow/cpp-taskflow/issues/143)
++ Microsoft Visual Studio at least v15.7 (MSVC++ 19.14); see [vcpkg guide](https://github.com/taskflow/taskflow/issues/143)
 + AppleClang Xode Version at least v8
 + Nvidia CUDA Toolkit and Compiler ([nvcc][nvcc]) at least v10.0 with -std=c++14
 
-Cpp-Taskflow works on Linux, Windows, and Mac OS X. See the [C++ compiler support](https://en.cppreference.com/w/cpp/compiler_support) status.
+Taskflow works on Linux, Windows, and Mac OS X. See the [C++ compiler support](https://en.cppreference.com/w/cpp/compiler_support) status.
 
 <div align="right"><b><a href="#table-of-contents">[↑]</a></b></div>
 
 # Compile Unit Tests, Examples, and Benchmarks
 
-Cpp-Taskflow uses [CMake](https://cmake.org/) to build examples and unit tests.
+Taskflow uses [CMake](https://cmake.org/) to build examples and unit tests.
 We recommend using out-of-source build.
 
 ```bash
@@ -705,7 +632,7 @@ We recommend using out-of-source build.
 
 ## Examples
 
-The folder `examples/` contains several examples and is a great place to learn to use Cpp-Taskflow.
+The folder `examples/` contains several examples and is a great place to learn to use Taskflow.
 
 | Example |  Description |
 | ------- |  ----------- | 
@@ -727,9 +654,9 @@ compile the benchmarks.
 
 <div align="right"><b><a href="#table-of-contents">[↑]</a></b></div>
 
-# Who is Using Cpp-Taskflow?
+# Who is Using Taskflow?
 
-Cpp-Taskflow is being used in both industry and academic projects to scale up existing workloads 
+Taskflow is being used in both industry and academic projects to scale up existing workloads 
 that incorporate complex task dependencies. 
 
 - [OpenTimer][OpenTimer]: A High-performance Timing Analysis Tool for Very Large Scale Integration (VLSI) Systems
@@ -743,15 +670,16 @@ that incorporate complex task dependencies.
 - [Heteroflow](https://github.com/Heteroflow/Heteroflow): A Modern C++ Parallel CPU-GPU Task Programming Library
 - [OpenPhySyn](https://github.com/The-OpenROAD-Project/OpenPhySyn): A plugin-based physical synthesis optimization kit as part of the OpenRoad flow
 - [OSSIA](https://ossia.io/): Open-source Software System for Interactive Applications
+- [deal.II](https://github.com/dealii/dealii): A C++ software library to support the creation of finite element code
 
-[More...](https://github.com/search?q=cpp-taskflow&type=Code)
+[More...](https://github.com/search?q=taskflow&type=Code)
 
 <div align="right"><b><a href="#table-of-contents">[↑]</a></b></div>
 
 # Contributors
 
-Cpp-Taskflow is being actively developed and contributed by the 
-[these people](https://github.com/cpp-taskflow/cpp-taskflow/graphs/contributors).
+Taskflow is being actively developed and contributed by the 
+[these people](https://github.com/taskflow/taskflow/graphs/contributors).
 Meanwhile, we appreciate the support from many organizations for our developments.
 
 
@@ -760,21 +688,19 @@ Meanwhile, we appreciate the support from many organizations for our development
 
 # License
 
-Cpp-Taskflow is licensed under the [MIT License](./LICENSE).
+Taskflow is licensed under the [MIT License](./LICENSE).
 
 * * *
 
 [Tsung-Wei Huang]:       https://tsung-wei-huang.github.io/
 [Chun-Xun Lin]:          https://github.com/clin99
 [Martin Wong]:           https://ece.illinois.edu/directory/profile/mdfwong
-[Andreas Olofsson]:      https://github.com/aolofsson
-[Gitter]:                https://gitter.im/cpp-taskflow/Lobby
 [Gitter badge]:          ./image/gitter_badge.svg
-[GitHub releases]:       https://github.com/coo-taskflow/cpp-taskflow/releases
-[GitHub issues]:         https://github.com/cpp-taskflow/cpp-taskflow/issues
-[GitHub insights]:       https://github.com/cpp-taskflow/cpp-taskflow/pulse
-[GitHub pull requests]:  https://github.com/cpp-taskflow/cpp-taskflow/pulls
-[GitHub contributors]:   https://github.com/cpp-taskflow/cpp-taskflow/graphs/contributors
+[GitHub releases]:       https://github.com/taskflow/taskflow/releases
+[GitHub issues]:         https://github.com/taskflow/taskflow/issues
+[GitHub insights]:       https://github.com/taskflow/taskflow/pulse
+[GitHub pull requests]:  https://github.com/taskflow/taskflow/pulls
+[GitHub contributors]:   https://github.com/taskflow/taskflow/graphs/contributors
 [GraphViz]:              https://www.graphviz.org/
 [AwesomeGraphViz]:       https://dreampuf.github.io/GraphvizOnline/
 [OpenMP Tasking]:        https://www.openmp.org/spec-html/5.0/openmpsu99.html 
@@ -791,8 +717,8 @@ Cpp-Taskflow is licensed under the [MIT License](./LICENSE).
 [UIUC]:                  https://illinois.edu/
 [CSL]:                   https://csl.illinois.edu/
 [UofU]:                  https://www.utah.edu/
-[wiki]:                  https://cpp-taskflow.github.io/cpp-taskflow/index.html
-[release notes]:         https://cpp-taskflow.github.io/cpp-taskflow/Releases.html
+[wiki]:                  https://taskflow.github.io/taskflow/index.html
+[release notes]:         https://taskflow.github.io/taskflow/Releases.html
 [PayMe]:                 https://www.paypal.me/twhuang/10
 [C++17]:                 https://en.wikipedia.org/wiki/C%2B%2B17
 [C++14]:                 https://en.wikipedia.org/wiki/C%2B%2B14
@@ -816,6 +742,6 @@ Cpp-Taskflow is licensed under the [MIT License](./LICENSE).
 [NovusCore]:             https://github.com/novuscore/NovusCore
 [SA-PCB]:                https://github.com/choltz95/SA-PCB
 
-[Presentation]:          https://cpp-taskflow.github.io/
+[Presentation]:          https://taskflow.github.io/
 [chrome://tracing]:      chrome://tracing
 
similarity index 80%
rename from bundled/taskflow-2.4.0/include/taskflow/core/executor.hpp
rename to bundled/taskflow-2.5.0/include/taskflow/core/executor.hpp
index 8d178ee221ced9bdd8e6fde165ce73567fcd6403..8c47499bfbf9f3471f254c19a53a6a8d75a75590 100644 (file)
@@ -36,12 +36,14 @@ namespace tf {
 
 @brief execution interface for running a taskflow graph
 
-An executor object manages a set of worker threads and implements 
+An executor object manages a set of worker threads and internalements 
 an efficient work-stealing scheduling algorithm to run a taskflow.
 
 */
 class Executor {
 
+  friend class Subflow;
+
   struct Worker {
     size_t id;
     size_t victim;
@@ -50,7 +52,6 @@ class Executor {
     Notifier::Waiter* waiter;
     std::mt19937 rdgen { std::random_device{}() };
     TaskQueue<Node*> wsq[NUM_DOMAINS];
-    Node* cache {nullptr};
   };
     
   struct PerThread {
@@ -258,21 +259,23 @@ class Executor {
     void _worker_loop(Worker&);
     void _exploit_task(Worker&, Node*&);
     void _explore_task(Worker&, Node*&);
-    void _schedule(Node*, bool);
+    void _schedule(Node*);
     void _schedule(PassiveVector<Node*>&);
     void _invoke(Worker&, Node*);
     void _invoke_static_work(Worker&, Node*);
-    void _invoke_dynamic_work(Worker&, Node*, bool&);
+    void _invoke_dynamic_work(Worker&, Node*);
+    void _invoke_dynamic_work_internal(Worker&, Node*, Graph&, bool);
+    void _invoke_dynamic_work_external(Graph&, Node*);
     void _invoke_condition_work(Worker&, Node*);
-    void _invoke_module_work(Worker&, Node*, bool&);
+    void _invoke_module_work(Worker&, Node*);
 
 #ifdef TF_ENABLE_CUDA
     void _invoke_cudaflow_work(Worker&, Node*);
-    void _invoke_cudaflow_work_impl(Worker&, Node*);
+    void _invoke_cudaflow_work_internal(Worker&, Node*);
 #endif
 
     void _set_up_topology(Topology*);
-    void _tear_down_topology(Topology**); 
+    void _tear_down_topology(Topology*); 
     void _increment_topology();
     void _decrement_topology();
     void _decrement_topology_and_notify();
@@ -515,8 +518,6 @@ inline void Executor::_explore_task(Worker& w, Node*& t) {
 // Procedure: _exploit_task
 inline void Executor::_exploit_task(Worker& w, Node*& t) {
   
-  assert(!w.cache);
-
   if(t) {
 
     const auto d = w.domain;
@@ -524,75 +525,22 @@ inline void Executor::_exploit_task(Worker& w, Node*& t) {
     if(_num_actives[d].fetch_add(1) == 0 && _num_thieves[d] == 0) {
       _notifier[d].notify(false);
     }
-    
-    auto tpg = t->_topology;
-    auto par = t->_parent;
-    auto exe = size_t{1};
 
     do {
-      _invoke(w, t);
 
-      if(w.cache) {
-        t = w.cache;
-        w.cache = nullptr;
-      }
-      else {
-        t = w.wsq[d].pop();
-        if(t) {
-          // We only increment the counter when poping task from wsq 
-          // (NOT including cache!)
-          if(t->_parent == par) {
-            exe++;
-          }
-          // joined subflow
-          else {
-            if(par == nullptr) {
-              // still have tasks so the topology join counter can't be zero
-              t->_topology->_join_counter.fetch_sub(exe);
-            }
-            else {
-              auto ret = par->_join_counter.fetch_sub(exe);
-              if(ret == exe) {
-                if(par->domain() == d) {
-                  w.wsq[d].push(par);
-                }
-                else {
-                  _schedule(par, false);
-                }
-              }
-            }
-            exe = 1;
-            par = t->_parent;
-          }
-        }
-        else {
-          // If no more local tasks!
-          if(par == nullptr) {
-            if(tpg->_join_counter.fetch_sub(exe) == exe) {
-              // TODO: Store tpg in local variable not in w
-              _tear_down_topology(&tpg);
-              if(tpg != nullptr) {
-                t = w.wsq[d].pop();
-                if(t) {
-                  exe = 1;
-                }
-              }
-            }
-          }
-          else {
-            if(par->_join_counter.fetch_sub(exe) == exe) {
-              if(par->domain() == d) {
-                t = par;
-                par = par->_parent;
-                exe = 1;
-              }
-              else {
-                _schedule(par, false);
-              }
-            }
-          }
+      _invoke(w, t);
+      
+      if(t->_parent == nullptr) {
+        if(t->_topology->_join_counter.fetch_sub(1) == 1) {
+          _tear_down_topology(t->_topology);
         }
       }
+      else {  // joined subflow
+        t->_parent->_join_counter.fetch_sub(1);
+      }
+
+      t = w.wsq[d].pop();
+
     } while(t);
 
     --_num_actives[d];
@@ -711,7 +659,7 @@ inline size_t Executor::num_observers() const {
 // Procedure: _schedule
 // The main procedure to schedule a give task node.
 // Each task node has two types of tasks - regular and subflow.
-inline void Executor::_schedule(Node* node, bool bypass_hint) {
+inline void Executor::_schedule(Node* node) {
   
   //assert(_workers.size() != 0);
 
@@ -721,16 +669,10 @@ inline void Executor::_schedule(Node* node, bool bypass_hint) {
   auto worker = _per_thread().worker;
 
   if(worker != nullptr && worker->executor == this) {
-    if(bypass_hint) {
-      assert(!worker->cache);
-      worker->cache = node;
-    }
-    else {
-      worker->wsq[d].push(node);
-      if(worker->domain != d) {
-        if(_num_actives[d] == 0 && _num_thieves[d] == 0) {
-          _notifier[d].notify(false);
-        }
+    worker->wsq[d].push(node);
+    if(worker->domain != d) {
+      if(_num_actives[d] == 0 && _num_thieves[d] == 0) {
+        _notifier[d].notify(false);
       }
     }
     return;
@@ -823,25 +765,13 @@ inline void Executor::_invoke(Worker& worker, Node* node) {
 
     // module task
     case Node::MODULE_WORK: {
-      bool first_time = !node->_has_state(Node::SPAWNED);
-      bool emptiness  = false;
-      _invoke_module_work(worker, node, emptiness);
-      if(first_time && !emptiness) {
-        return;
-      }
+      _invoke_module_work(worker, node);
     }
     break;
 
     // dynamic task
     case Node::DYNAMIC_WORK: {
-      // Need to create a subflow if it is the first time entering here
-      if(!node->_has_state(Node::SPAWNED)) {
-        bool join = false;
-        _invoke_dynamic_work(worker, node, join);
-        if(join) {
-          return;
-        }
-      }
+      _invoke_dynamic_work(worker, node);
     }
     break;
 
@@ -863,7 +793,6 @@ inline void Executor::_invoke(Worker& worker, Node* node) {
     default:
     break;
   }
-  
 
   // We MUST recover the dependency since subflow may have  
   // a condition node to go back (cyclic).
@@ -877,30 +806,13 @@ inline void Executor::_invoke(Worker& worker, Node* node) {
     node->_join_counter = node->num_dependents();
   }
 
-  node->_unset_state(Node::SPAWNED);
-
   // At this point, the node storage might be destructed.
-  Node* cache {nullptr};
-
   for(size_t i=0; i<num_successors; ++i) {
     if(--(node->_successors[i]->_join_counter) == 0) {
-      if(node->_successors[i]->domain() != worker.domain) {
-        c.fetch_add(1);
-        _schedule(node->_successors[i], false);
-      }
-      else {
-        if(cache) {
-          c.fetch_add(1);
-          _schedule(cache, false);
-        }
-        cache = node->_successors[i];
-      }
+      c.fetch_add(1);
+      _schedule(node->_successors[i]);
     }
   }
-
-  if(cache) {
-    _schedule(cache, true);
-  }
 }
 
 // Procedure: _observer_prologue
@@ -925,53 +837,77 @@ inline void Executor::_invoke_static_work(Worker& worker, Node* node) {
 }
 
 // Procedure: _invoke_dynamic_work
-inline void Executor::_invoke_dynamic_work(Worker& worker, Node* node, bool& join) {
+inline void Executor::_invoke_dynamic_work(Worker& w, Node* node) {
 
-  _observer_prologue(worker, node);
-    
-  auto& subgraph = nstd::get<Node::DynamicWork>(node->_handle).subgraph;
+  _observer_prologue(w, node);
+
+  auto& handle = nstd::get<Node::DynamicWork>(node->_handle);
+
+  handle.subgraph.clear();
+
+  Subflow fb(*this, node, handle.subgraph); 
+
+  handle.work(fb);
+
+  if(!fb._joined) {
+    _invoke_dynamic_work_internal(w, node, handle.subgraph, fb._detach);
+  }
+  
+  // TODO 
+  _observer_epilogue(w, node);
+}
+
+// Procedure: _invoke_dynamic_work_external
+inline void Executor::_invoke_dynamic_work_external(Graph& g, Node* p) {
 
-  subgraph.clear();
-  Subflow fb(subgraph); 
+  auto worker = _per_thread().worker;
 
-  nstd::get<Node::DynamicWork>(node->_handle).work(fb);
+  assert(worker && worker->executor == this);
+  
+  _invoke_dynamic_work_internal(*worker, p, g, false);
+}
 
-  node->_set_state(Node::SPAWNED);
+// Procedure: _invoke_dynamic_work_internal
+inline void Executor::_invoke_dynamic_work_internal(Worker& w, Node* p, Graph& g, bool d) {
 
-  if(!subgraph.empty()) {
+  if(!g.empty()) {
 
     PassiveVector<Node*> src; 
 
-    for(auto n : subgraph._nodes) {
+    for(auto n : g._nodes) {
 
-      n->_topology = node->_topology;
+      n->_topology = p->_topology;
       n->_set_up_join_counter();
+      n->_parent = d ? nullptr : p;
       
-      if(!fb.detached()) {
-        n->_parent = node;
-      }
-
       if(n->num_dependents() == 0) {
         src.push_back(n);
       }
     }
-
-    join = fb.joined();
-
-    if(!join) {  // Detach mode
-      node->_topology->_join_counter.fetch_add(src.size());         
+    
+    // detach here
+    if(d) { 
+      p->_topology->_join_counter.fetch_add(src.size());
+      _schedule(src);
     }
-    else {       // Join mode (spawned nodes need second-round execution
-      node->_join_counter.fetch_add(src.size());
+    // join here
+    else {  
+      p->_join_counter.fetch_add(src.size());
+      _schedule(src);
+      Node* t = nullptr;
 
-      node->_parent ? node->_parent->_join_counter.fetch_add(1) :
-                      node->_topology->_join_counter.fetch_add(1);
-    }
+      do {
+        t = w.wsq[w.domain].pop();
+    
+        if(t) {
+          _invoke(w, t);
+          t->_parent ? t->_parent->_join_counter.fetch_sub(1) :
+                       t->_topology->_join_counter.fetch_sub(1);
+        }
 
-    _schedule(src);
+      } while(p->_join_counter != 0);
+    }
   }
-  
-  _observer_epilogue(worker, node);
 }
 
 // Procedure: _invoke_condition_work
@@ -992,14 +928,19 @@ inline void Executor::_invoke_condition_work(Worker& worker, Node* node) {
     auto s = node->_successors[id];
     s->_join_counter.store(0);
 
-    if(s->domain() == worker.domain) {
-      _schedule(s, true);
-    }
-    else {
-      node->_parent ? node->_parent->_join_counter.fetch_add(1) :
-                      node->_topology->_join_counter.fetch_add(1);
-      _schedule(s, false);
-    }
+    node->_parent ? node->_parent->_join_counter.fetch_add(1) :
+                    node->_topology->_join_counter.fetch_add(1);
+    _schedule(s);
+
+
+    //if(s->domain() == worker.domain) {
+    //  _schedule(s, true);
+    //}
+    //else {
+    //  node->_parent ? node->_parent->_join_counter.fetch_add(1) :
+    //                  node->_topology->_join_counter.fetch_add(1);
+    //  _schedule(s, false);
+    //}
   }
 
   _observer_epilogue(worker, node);
@@ -1009,12 +950,12 @@ inline void Executor::_invoke_condition_work(Worker& worker, Node* node) {
 // Procedure: _invoke_cudaflow_work
 inline void Executor::_invoke_cudaflow_work(Worker& worker, Node* node) {
   _observer_prologue(worker, node);  
-  _invoke_cudaflow_work_impl(worker, node);
+  _invoke_cudaflow_work_internal(worker, node);
   _observer_epilogue(worker, node);
 }
 
-// Procedure: _invoke_cudaflow_work_impl
-inline void Executor::_invoke_cudaflow_work_impl(Worker& w, Node* node) {
+// Procedure: _invoke_cudaflow_work_internal
+inline void Executor::_invoke_cudaflow_work_internal(Worker& w, Node* node) {
   
   assert(w.domain == node->domain());
 
@@ -1065,51 +1006,15 @@ inline void Executor::_invoke_cudaflow_work_impl(Worker& w, Node* node) {
 #endif
 
 // Procedure: _invoke_module_work
-inline void Executor::_invoke_module_work(Worker& worker, Node* node, bool& ept) {
+inline void Executor::_invoke_module_work(Worker& w, Node* node) {
 
-  // second time to enter this context
-  if(node->_has_state(Node::SPAWNED)) {
-    return;
-  }
-  
-  _observer_prologue(worker, node);
+  _observer_prologue(w, node);
   
-  // first time to enter this context
-  node->_set_state(Node::SPAWNED);
-
   auto module = nstd::get<Node::ModuleWork>(node->_handle).module;
-
-  if(module->empty()) {
-    ept = true;
-    return;
-  }
-
-  PassiveVector<Node*> src;
-
-  for(auto n: module->_graph._nodes) {
-
-    n->_topology = node->_topology;
-    n->_parent = node;
-    n->_set_up_join_counter();
-
-    if(n->num_dependents() == 0) {
-      src.push_back(n);
-    }
-  }
-
-  node->_join_counter.fetch_add(src.size());
-    
-  if(node->_parent == nullptr) {
-    node->_topology->_join_counter.fetch_add(1);
-  }
-  else {
-    node->_parent->_join_counter.fetch_add(1);
-  }
   
-  // src can't be empty (banned outside)
-  _schedule(src);
+  _invoke_dynamic_work_internal(w, node, module->_graph, false);
   
-  _observer_epilogue(worker, node);  
+  _observer_epilogue(w, node);  
 }
 
 // Function: run
@@ -1162,26 +1067,26 @@ inline void Executor::_set_up_topology(Topology* tpg) {
 }
 
 // Function: _tear_down_topology
-inline void Executor::_tear_down_topology(Topology** tpg) {
+inline void Executor::_tear_down_topology(Topology* tpg) {
 
-  auto &f = (*tpg)->_taskflow;
+  auto &f = tpg->_taskflow;
 
   //assert(&tpg == &(f._topologies.front()));
 
   // case 1: we still need to run the topology again
-  if(! (*tpg)->_pred() ) {
+  if(! tpg->_pred() ) {
     //tpg->_recover_num_sinks();
 
-    assert((*tpg)->_join_counter == 0);
-    (*tpg)->_join_counter = (*tpg)->_sources.size();
+    assert(tpg->_join_counter == 0);
+    tpg->_join_counter = tpg->_sources.size();
 
-    _schedule((*tpg)->_sources); 
+    _schedule(tpg->_sources); 
   }
   // case 2: the final run of this topology
   else {
     
-    if((*tpg)->_call != nullptr) {
-      (*tpg)->_call();
+    if(tpg->_call != nullptr) {
+      tpg->_call();
     }
 
     f._mtx.lock();
@@ -1189,20 +1094,20 @@ inline void Executor::_tear_down_topology(Topology** tpg) {
     // If there is another run (interleave between lock)
     if(f._topologies.size() > 1) {
 
-      assert((*tpg)->_join_counter == 0);
+      assert(tpg->_join_counter == 0);
 
       // Set the promise
-      (*tpg)->_promise.set_value();
+      tpg->_promise.set_value();
       f._topologies.pop_front();
       f._mtx.unlock();
       
       // decrement the topology but since this is not the last we don't notify
       _decrement_topology();
 
-      *tpg = &(f._topologies.front());
+      tpg = &(f._topologies.front());
 
-      _set_up_topology(*tpg);
-      _schedule((*tpg)->_sources);
+      _set_up_topology(tpg);
+      _schedule(tpg->_sources);
 
       //f._topologies.front()._bind(f._graph);
       //*tpg = &(f._topologies.front());
@@ -1218,7 +1123,7 @@ inline void Executor::_tear_down_topology(Topology** tpg) {
 
       // Need to back up the promise first here becuz taskflow might be 
       // destroy before taskflow leaves
-      auto p {std::move((*tpg)->_promise)};
+      auto p {std::move(tpg->_promise)};
 
       f._topologies.pop_front();
 
@@ -1228,9 +1133,6 @@ inline void Executor::_tear_down_topology(Topology** tpg) {
       p.set_value();
 
       _decrement_topology_and_notify();
-
-      // Reset topology so caller can stop execution
-      *tpg = nullptr;
     }
   }
 }
@@ -1306,6 +1208,29 @@ inline void Executor::wait_for_all() {
   _topology_cv.wait(lock, [&](){ return _num_topologies == 0; });
 }
 
+// ----------------------------------------------------------------------------
+// Cyclic Dependency
+// ----------------------------------------------------------------------------
+
+inline void Subflow::join() {
+
+  if(_joined) {
+    TF_THROW("subflow already joined");
+  }
+
+  _executor._invoke_dynamic_work_external(_graph, _parent);
+  _joined = true;
+}
+
 }  // end of namespace tf -----------------------------------------------------
 
 
+
+
+
+
+
+
+
+
+
similarity index 94%
rename from bundled/taskflow-2.4.0/include/taskflow/core/flow_builder.hpp
rename to bundled/taskflow-2.5.0/include/taskflow/core/flow_builder.hpp
index d0677dbf20817031691e68633f56798b2a8e3cdd..477ec3d5cba8fa9052b2f1626fb41bb06af43519 100644 (file)
@@ -12,17 +12,10 @@ namespace tf {
 */
 class FlowBuilder {
 
-  friend class Task;
+  friend class Executor;
 
   public:
     
-    /**
-    @brief constructs a flow builder object
-
-    @param graph a task dependency graph to manipulate
-    */
-    FlowBuilder(Graph& graph);
-    
     /**
     @brief creates a static task from a given callable object
     
@@ -96,9 +89,13 @@ class FlowBuilder {
     /**
     @brief constructs a task dependency graph of range-based parallel_for
     
-    The task dependency graph applies a callable object 
-    to the dereferencing of every iterator 
-    in the range [beg, end) chunk by chunk.
+    The task dependency graph applies the callable object
+    @p callable to each object obtained by dereferencing
+    every iterator in the range [beg, end). The range
+    is split into chunks of size @p chunk, where each of them
+    is processed by one Task.
+
+    The callable needs to accept a single argument, the object in the range.
 
     @tparam I input iterator type
     @tparam C callable type
@@ -335,10 +332,20 @@ class FlowBuilder {
     */
     void succeed(std::initializer_list<Task> others, Task A);
     
-  private:
-
+  protected:
+    
+    /**
+    @brief constructs a flow builder with a graph
+    */
+    FlowBuilder(Graph& graph);
+    
+    /**
+    @brief associated graph object
+    */
     Graph& _graph;
 
+  private:
+
     template <typename L>
     void _linearize(L&);
 };
@@ -856,24 +863,29 @@ std::pair<Task, Task> FlowBuilder::reduce(I beg, I end, T& result, B&& op) {
 
 @brief building methods of a subflow graph in dynamic tasking
 
+By default, a subflow automatically joins its parent node. You may explicitly
+join or detach a subflow by calling Subflow::join or Subflow::detach.
+
 */ 
 class Subflow : public FlowBuilder {
 
+  friend class Executor;
+
   public:
     
-    /**
-    @brief constructs a subflow builder object
-    */
-    template <typename... Args>
-    Subflow(Args&&... args);
-    
     /**
     @brief enables the subflow to join its parent task
+
+    Performs an immediate action to join the subflow. Once the subflow is joined,
+    it is considered finished and you may not apply any other actions to it.
     */
     void join();
 
     /**
     @brief enables the subflow to detach from its parent task
+
+    A joined subflow cannot be detached. The subflow will be detached upon leaving
+    its execution context.
     */
     void detach();
     
@@ -883,39 +895,46 @@ class Subflow : public FlowBuilder {
     bool detached() const;
 
     /**
-    @brief queries if the subflow will join its parent task
+    @brief queries if the subflow is joinable
+
+    When a subflow is joined, it becomes not joinable.
     */
-    bool joined() const;
+    bool joinable() const;
 
   private:
+    
+    Subflow(Executor&, Node*, Graph&);
+
+    Executor& _executor;
+    Node* _parent;
 
-    bool _detached {false};
+    bool _joined {false};
+    bool _detach {false};
 };
 
 // Constructor
-template <typename... Args>
-Subflow::Subflow(Args&&... args) :
-  FlowBuilder {std::forward<Args>(args)...} {
-}
-
-// Procedure: join
-inline void Subflow::join() {
-  _detached = false;
+Subflow::Subflow(Executor& executor, Node* parent, Graph& graph) :
+  FlowBuilder {graph},
+  _executor   {executor},
+  _parent     {parent} {
 }
 
 // Procedure: detach
 inline void Subflow::detach() {
-  _detached = true;
+  if(_joined) {
+    TF_THROW("subflow already joined");
+  }
+  _detach = true;
 }
 
 // Function: detached
 inline bool Subflow::detached() const {
-  return _detached;
+  return _detach;
 }
 
 // Function: joined
-inline bool Subflow::joined() const {
-  return !_detached;
+inline bool Subflow::joinable() const {
+  return !_joined;
 }
 
 
similarity index 99%
rename from bundled/taskflow-2.4.0/include/taskflow/core/graph.hpp
rename to bundled/taskflow-2.5.0/include/taskflow/core/graph.hpp
index b69cdf84fc8d850f71bc9b18a5ca6672a24e4552..af258ec5e7e68b0a8d9dc9b6c3e0b98f0a1ab3e4 100644 (file)
@@ -84,8 +84,7 @@ class Node {
   TF_ENABLE_POOLABLE_ON_THIS;
 
   // state bit flag
-  constexpr static int SPAWNED = 0x1;
-  constexpr static int BRANCH  = 0x2;
+  constexpr static int BRANCH  = 0x1;
   
   // static work handle
   struct StaticWork {
similarity index 91%
rename from bundled/taskflow-2.4.0/include/taskflow/core/observer.hpp
rename to bundled/taskflow-2.5.0/include/taskflow/core/observer.hpp
index 02e5d4f1e54c923049b069aacc4a7d68d77133e9..be73a0f6582ffee2419b1035adf9907f2e0d641a 100644 (file)
@@ -94,6 +94,7 @@ class ChromeTracingObserver : public ObserverInterface {
   struct Timeline {
     std::chrono::time_point<std::chrono::steady_clock> origin;
     std::vector<std::vector<Segment>> segments;
+    std::vector<std::stack<std::chrono::time_point<std::chrono::steady_clock>>> stacks;
   };  
 
   public:
@@ -149,8 +150,8 @@ inline ChromeTracingObserver::Segment::Segment(
 
 // Procedure: set_up
 inline void ChromeTracingObserver::set_up(size_t num_workers) {
-
   _timeline.segments.resize(num_workers);
+  _timeline.stacks.resize(num_workers);
 
   for(size_t w=0; w<num_workers; ++w) {
     _timeline.segments[w].reserve(32);
@@ -160,22 +161,29 @@ inline void ChromeTracingObserver::set_up(size_t num_workers) {
 }
 
 // Procedure: on_entry
-inline void ChromeTracingObserver::on_entry(size_t w, TaskView tv) {
-  _timeline.segments[w].emplace_back(
-    tv.name(), std::chrono::steady_clock::now()
-  );
+inline void ChromeTracingObserver::on_entry(size_t w, TaskView) {
+  _timeline.stacks[w].push(std::chrono::steady_clock::now());
 }
 
 // Procedure: on_exit
-inline void ChromeTracingObserver::on_exit(size_t w, TaskView) {
-  assert(_timeline.segments[w].size() > 0);
-  _timeline.segments[w].back().end = std::chrono::steady_clock::now();
+inline void ChromeTracingObserver::on_exit(size_t w, TaskView tv) {
+  assert(!_timeline.stacks[w].empty());
+
+  auto beg = _timeline.stacks[w].top();
+  _timeline.stacks[w].pop();
+
+  _timeline.segments[w].emplace_back(
+    tv.name(), beg, std::chrono::steady_clock::now()
+  );
 }
 
 // Function: clear
 inline void ChromeTracingObserver::clear() {
   for(size_t w=0; w<_timeline.segments.size(); ++w) {
     _timeline.segments[w].clear();
+    while(!_timeline.stacks[w].empty()) {
+      _timeline.stacks[w].pop();
+    }
   }
 }
 
@@ -293,6 +301,7 @@ class TFProfObserver : public ObserverInterface {
   struct Timeline {
     std::chrono::time_point<std::chrono::steady_clock> origin;
     std::vector<std::vector<Segment>> segments;
+    std::vector<std::stack<std::chrono::time_point<std::chrono::steady_clock>>> stacks;
   };  
 
   public:
@@ -354,6 +363,7 @@ inline TFProfObserver::Segment::Segment(
 inline void TFProfObserver::set_up(size_t num_workers) {
 
   _timeline.segments.resize(num_workers);
+  _timeline.stacks.resize(num_workers);
 
   for(size_t w=0; w<num_workers; ++w) {
     _timeline.segments[w].reserve(32);
@@ -363,22 +373,29 @@ inline void TFProfObserver::set_up(size_t num_workers) {
 }
 
 // Procedure: on_entry
-inline void TFProfObserver::on_entry(size_t w, TaskView tv) {
-  _timeline.segments[w].emplace_back(
-    tv.name(), tv.type(), std::chrono::steady_clock::now()
-  );
+inline void TFProfObserver::on_entry(size_t w, TaskView) {
+  _timeline.stacks[w].push(std::chrono::steady_clock::now());
 }
 
 // Procedure: on_exit
-inline void TFProfObserver::on_exit(size_t w, TaskView) {
-  assert(_timeline.segments[w].size() > 0);
-  _timeline.segments[w].back().end = std::chrono::steady_clock::now();
+inline void TFProfObserver::on_exit(size_t w, TaskView tv) {
+  assert(!_timeline.stacks[w].empty());
+
+  auto beg = _timeline.stacks[w].top();
+  _timeline.stacks[w].pop();
+
+  _timeline.segments[w].emplace_back(
+    tv.name(), tv.type(), beg, std::chrono::steady_clock::now()
+  );
 }
 
 // Function: clear
 inline void TFProfObserver::clear() {
   for(size_t w=0; w<_timeline.segments.size(); ++w) {
     _timeline.segments[w].clear();
+    while(!_timeline.stacks[w].empty()) {
+      _timeline.stacks[w].pop();
+    }
   }
 }
 
similarity index 99%
rename from bundled/taskflow-2.4.0/include/taskflow/utility/object_pool.hpp
rename to bundled/taskflow-2.5.0/include/taskflow/utility/object_pool.hpp
index 4272e339d538c75db462f5b18e11c9059cddb0e1..63ca9f05c9d3df61a5f561355c07817d75c18adf 100644 (file)
@@ -23,7 +23,7 @@ namespace tf {
 
 #define TF_ENABLE_POOLABLE_ON_THIS                          \
   template <typename T, size_t S> friend class ObjectPool;  \
-  void* _object_pool_block;
+  void* _object_pool_block
 
 // Class: ObjectPool
 //
similarity index 99%
rename from bundled/taskflow-2.4.0/include/taskflow/utility/traits.hpp
rename to bundled/taskflow-2.5.0/include/taskflow/utility/traits.hpp
index 90080be1a3ba93db2bdfac85ee181647ca0033c2..1250ddfa4581775af49115f6f7ecc5709786635a 100644 (file)
@@ -5,7 +5,8 @@
 #include <iostream>
 #include <fstream>
 #include <mutex>
-#include <deque>
+#include <stack>
+#include <queue>
 #include <vector>
 #include <algorithm>
 #include <memory>

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.