3 string read_string_from_file(string file); string s =...
TRANSCRIPT
Read From FIle: Naïve SolutionReturn a string, block on call:
string read_string_from_file(string file);
string s = read_string_from_file("myfile.txt");cout << s;
Problem: blocking call
Read From FIle: Callback SolutionDoes not return a string, takes a callback that accepts a string:template<typename Func>void read_string_from_file(string file, Func&& func);
...read_string_from_file("myfile.txt", [](string s) { cout << s;});
Concatenate Files: Naive SolutionRead from one file, then read from another:
template<typename Func>void concatenate_files(string file1, string file2, Func&& func){ read_string_from_file(file1, [func](string str1) { read_string_from_file(file2, [func](string str2) { func(str1 + str2); }); });}
Concatenate Files: A “Better” Solutiontemplate<typename Func>void concatenate_files(string file1, string file2, Func&& func) {
auto results = make_shared<result_holder>();
read_string_from_file(file1, [=](string str) { if (results->str) { func(str + *results->str); } else{ results->str = make_unique<string>(str); } });
read_string_from_file(file2, [=](string str) { if (results->str) { func(*results->str + str); } else{ results->str = make_unique<string>(str); } });}
struct result_holder{ unique_ptr<string> str;};
? Spot the defect!
Long-Running WorkCounting lines in a file
// Launch a task:future<int> work = async([] { return CountLinesInFile(…); });
// Collect the results:cout << work.get();
Problem: blocking call
Proposal: Continuation(shared_)future::then
// Launch a task:future<int> work = CountLinesInFileAsync(...);
work.then([] (future<int> f) { // Get the result cout << f.get();});
Binding Multiple Continuationsfuture<T1> t = async([](){ return func1();}).then ([](future<T1> n){ return func2(n.get());}).then ([](future<T2> n){ return func3(n.get());}).then ...
Advanced CompositionSequential Composition:
Parallel Composition (only shared_future):
f.then(A).then(B);
f.then(A);f.then(B);
Join and Choice (more on next page):
auto f = when_all(a, b);auto f = when_any(a, b);
JoinCreate a future that completes when all arguments completevector<future<int>> futures = get_futures();auto futureResult = when_all (begin(futures), end(futures)) .then([](future<vector<future<string>>> results) { for (auto& s : results.get() ) // will not block { cout << s.get(); // will not block } });
JoinNow with heterogeneous arguments
future<int> future1 = ...;future<string> future2 = ...;auto futureResult = when_all(future1, future2) .then([](future<tuple<future<int>, future<string>>> results) { auto pair = results.get(); // will not block ... }});
Concatenate Files: The Right SolutionTerse, efficient and safe
future<string> concatenate_files(string file1, string file2){ auto strings = when_all(read_string_from_file(file1), read_string_from_file(file2));
return strings.then([]( future<tuple<future<string>, future<string>>> strings) { auto pair = strings.get(); return pair.get<0>.get() + pair.get<1>.get(); });}
Concatenate Files: The Right SolutionTerse, efficient and safe (with polymorphic lambdas)future<string> concatenate_files(string file1, string file2){ auto strings = when_all(read_string_from_file(file1), read_string_from_file(file2));
return strings.then([]( auto strings) { auto pair = strings.get(); return pair.get<0>.get() + pair.get<1>.get(); });}
ChoiceCreate a future that completes when at least one of arguments completes:
vector<future<int>> futures = get_futures();auto futureResult = when_any (begin(futures), end(futures)) .then([](future<vector<future<string>>> results) { for (auto& s : results.get() ) // will not block { if(s.ready()) cout << s.get(); // will not block } });
make_ready_futurefuture<int> compute(int x) { if (x < 0) return make_ready_future<int>(-1); if (x == 0) return make_ready_future<int>(0); future<int> f1 = async([]() { return do_work(x); }); return f1;}
is_readyfuture<int> f1 = async([]() { return possibly_long_computation(); });if(!f1.is_ready()) { //if not ready, attach a continuation and avoid a blocking wait fl.then([] (future<int> f2) { int v = f2.get(); process_value(v); });}// if ready, no need to add continuation, process value right away else { int v = f1.get(); process_value(v);}
Branches and loops
22
.get .then
string read(string file){ istream fi = open(file).get(); string ret, chunk; while ((chunk = fi.read().get()).size()) ret += chunk; return ret;}
future<string> read(string file) { return open(file) .then([=](istream fi) { string ret, chunk; while (
???
Branches and loops
23
.get .then
string read(string file){ istream fi = open(file).get(); string ret, chunk; while ((chunk = fi.read().get()).size()) ret += chunk; return ret;}
future<string> read(string file){ return open(file) .then([=](istream fi) { auto ret = make_shared<string>(); auto next = make_shared<function<future<string>()>>( [=] { fi.read() .then([=](string chunk) { if (chunk.size()) { *ret += chunk; return next(); } return make_ready_future(*ret); }); }); return (*next)(); });}
Await ExampleGet a value, convert to string:
future<string> f() resumable{ future<int> f1 = async([]() { return possibly_long_computation(); });
int n = await f1; return to_string(n);}
Branches and loops, with await
26
.get await
string read(string file){ istream fi = open(file).get(); string ret, chunk; while ((chunk = fi.read().get()).size()) ret += chunk; return ret;}
String read(string file){ istream fi = await open(file); string ret, chunk; while ((chunk = (await fi.read()).size()) ret += chunk; return ret;}
Thread #2Thread #1
Resumable Side Stack Simulation
27
Main Stack
…
…
…
…
…
foo();
t = async_bar()
Side Stack
async_bar();
do_work();
…
…
…
await async_work();
somefunc();bool b = t.get()
<blocked>
More sync calls
…
return true;
<completed>
Side Stack
async_work();
do_work();await create_task();
Longrunning is done!
<suspended>
return task<void>
<suspended>
return task<bool>
return;
<completed>
void foo() { task<bool> t = async_bar();
somefunc();
bool b = t.get();}
task<bool> async_bar() resumable { do_work(); ... // sync calls
await async_work();
// More sync calls ... return true;}
task<void> async_work() resumable { do_work();
await create_task( [] { longrunning (); });
return;}
done!async_work
is done!
References• N3857: Improvements to std::future<T> and
Related APIs• N3858: Resumable Functions• N3970: Working Draft, Information technology –
Programming languages, their environments and system software interfaces – C++ Extensions for Concurrency