Avoiding Data Races¶
The edges in a flow graph make explicit the dependence relationships that you want the library to enforce. Similarly, the concurrency limits on function_node and multifunction_node objects limit the maximum number of concurrent invocations that the runtime library will allow. These are the limits that are enforced by the library; the library does not automatically protect you from data races. You must explicitly prevent data races by using these mechanisms.
For example, the follow code has a data race because there is nothing to prevent concurrent accesses to the global count object referenced by node f:
graph g;
int src_count = 1;
int global_sum = 0;
int limit = 100000;
source_node< int > src( g, [&]( int &i ) -> bool {
if ( src_count <= limit ) {
i = src_count++;
return true;
} else {
return false;
}
} );
function_node< int, int > f( g, unlimited, [&]( int i ) -> int {
global_sum += i; // data race on global_sum
return i;
} );
make_edge( src, f );
g.wait_for_all();
cout << "global sum = " << global_sum
<< " and closed form = " << limit*(limit+1)/2 << "\n";
If you run the above example, it will likely calculate a global sum that is a bit smaller than the expected solution due to the data race. The data race could be avoided in this simple example by changing the allowed concurrency in f from unlimited to 1, forcing each value to be processed sequentially by f. You may also note that the source_node also updates a global value, src_count. However, since a source_node always executes serially, there is no race possible.