3 string read_string_from_file(string file); string s = read_string_from_file("myfile.txt"); cout

29
Artur Laksberg Microsoft May 8th, 2014 Asynchronous programming with futures and await

Transcript of 3 string read_string_from_file(string file); string s = read_string_from_file("myfile.txt"); cout

Artur LaksbergMicrosoft May 8th, 2014

Asynchronous programming with futures and await

Agenda• Asynchrony with callbacks• Futures and Continuations• Composition with futures• await

Hollywood Principle

Don’t call us, we’ll call you!3

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!

std::future<T>std::shared_future<T>

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);}

awaitand resumable functions

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

“Concurrent programs wait faster”

Tony Hoare