NATURAL TASK SCHEDULING
Using Futures and Continuations
Qt Developer Days 2013
Ivan Čukić
ivan.cukic@kde.org
ivan.fomentgr...
THE PROBLEM
Meet Jeff
Out of Control
Reasons for Waiting
The Problem Continuations Schedulers Tasks Epilogue
MEET JEFF
3
The Problem Continuations Schedulers Tasks Epilogue
MEET JEFF
void login()
{
user = get_username();
new_user = !check_if_u...
The Problem Continuations Schedulers Tasks Epilogue
MEET JEFF
void login() { get_username(on_got_username); }
void on_got_...
The Problem Continuations Schedulers Tasks Epilogue
OUT OF CONTROL
6
The Problem Continuations Schedulers Tasks Epilogue
OUT OF CONTROL
“Spaghetti code” by George W. Hart
7
The Problem Continuations Schedulers Tasks Epilogue
REASONS FOR WAITING
User input
Network actions
Inter-process
communica...
The Problem Continuations Schedulers Tasks Epilogue
HIDEAWAY
Wrapping it in task objects (QThread, KJob, ...)
Methods with...
CONTINUATIONS
Lost in the Future
Under wraps
The Problem Continuations Schedulers Tasks Epilogue
LOST IN THE FUTURE
Is it about monads?
Callbacks?
Signals and slots?
11
The Problem Continuations Schedulers Tasks Epilogue
LOST IN THE FUTURE
C++ standard proposal N3558, Boost.Thread 1.55.0
fu...
The Problem Continuations Schedulers Tasks Epilogue
LOST IN THE FUTURE
|
int i; | i.then(c); // ERROR!
|
future<int> futur...
The Problem Continuations Schedulers Tasks Epilogue
LOST IN THE FUTURE
|
int i; | c(i);
|
future<int> future; | future.the...
The Problem Continuations Schedulers Tasks Epilogue
UNDER WRAPS
template <typename _Job, typename _Continuation>
void cont...
The Problem Continuations Schedulers Tasks Epilogue
UNDER WRAPS
template <typename _ReturnType, typename _Continuation>
vo...
The Problem Continuations Schedulers Tasks Epilogue
UNDER WRAPS
template <typename _ReturnType, typename _Continuation>
vo...
The Problem Continuations Schedulers Tasks Epilogue
MATCHBOX
template<typename _TestType, typename _ArgType>
class has_the...
SCHEDULERS
The Chains are On
The New Order
Set Your Controls for the Heart of the Sun
The Problem Continuations Schedulers Tasks Epilogue
THE CHAINS ARE ON
getUsername().then(
[] (future<string> username) {
g...
The Problem Continuations Schedulers Tasks Epilogue
THE CHAINS ARE ON
Can it be made to look like this?
void login()
{
......
The Problem Continuations Schedulers Tasks Epilogue
THE CHAINS ARE ON
... it could look like this:
auto login = serial_
(
...
The Problem Continuations Schedulers Tasks Epilogue
THE NEW ORDER
template <typename... _Jobs>
class Serial;
template <>
c...
The Problem Continuations Schedulers Tasks Epilogue
THE NEW ORDER
template <typename _Job, typename... _Jobs>
class Serial...
The Problem Continuations Schedulers Tasks Epilogue
LET THERE BE MORE LIGHT
while loop:
while_( | while_(condition) (
cond...
The Problem Continuations Schedulers Tasks Epilogue
LET THERE BE MORE LIGHT
asynchronous assignment
var<int> value;
value ...
The Problem Continuations Schedulers Tasks Epilogue
SET YOUR CONTROLS...
var<int> wait;
serial_(
test::writeMessage(0, "St...
The Problem Continuations Schedulers Tasks Epilogue
... FOR THE HEART OF THE SUN
while_(
// Wait until we get a connection...
TASKS
Lazy Day
The Problem Continuations Schedulers Tasks Epilogue
LAZY DAY
Problem:
A method is executed while the arguments are evaluat...
The Problem Continuations Schedulers Tasks Epilogue
LAZY DAY
So, your options are:
void someMethod(...);
serial_(
std::bin...
The Problem Continuations Schedulers Tasks Epilogue
LAZY DAY
Or using a std::bind-based wrapper
namespace detail {
void so...
The Problem Continuations Schedulers Tasks Epilogue
LAZY DAY
Or using a simple wrapper:
namespace detail {
void someMethod...
The Problem Continuations Schedulers Tasks Epilogue
EPILOGUE
Benefits:
Readable code, easy to reason about
Automatic lifeti...
The Problem Continuations Schedulers Tasks Epilogue
ANSWERS? QUESTIONS! QUESTIONS? ANSWERS!
Kudos:
Friends at KDE
Dr Saša ...
Upcoming SlideShare
Loading in …5
×

Natural Task Scheduling Using Futures and Continuations, Ivan Čukić, Qt Developer Days 2013

4,598 views

Published on

We are used to think about algorithms in a procedural manner – with loops, branches and subroutines. Presenting an algorithm as an easily understandable flow between its steps.

In the real world, where we need to reduce latency and forbid the blocking API calls, these flows get broken. Due to the inversion of control (IoC) required by the introduction of asynchronous APIs (Xlib vs. XCB, iostream vs. boost::asio), the code becomes an unreadable call-callback soup.

We are presenting a way of defining the algorithm flow in a procedural manner and leaving it up to the C++ compiler to generate the necessary asynchronous code.

Published in: Technology, Business
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,598
On SlideShare
0
From Embeds
0
Number of Embeds
1,737
Actions
Shares
0
Downloads
24
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Natural Task Scheduling Using Futures and Continuations, Ivan Čukić, Qt Developer Days 2013

  1. 1. NATURAL TASK SCHEDULING Using Futures and Continuations Qt Developer Days 2013 Ivan Čukić ivan.cukic@kde.org ivan.fomentgroup.org/blog
  2. 2. THE PROBLEM Meet Jeff Out of Control Reasons for Waiting
  3. 3. The Problem Continuations Schedulers Tasks Epilogue MEET JEFF 3
  4. 4. The Problem Continuations Schedulers Tasks Epilogue MEET JEFF void login() { user = get_username(); new_user = !check_if_user_exists(user); if (new_user) { pass = get_password(); initialize_account(uame, pass); } else do { pass = get_password(); } while (!check_user(user, pass)); initialize_environment(); if (new_user) show_welcome_message(); } 4
  5. 5. The Problem Continuations Schedulers Tasks Epilogue MEET JEFF void login() { get_username(on_got_username); } void on_got_username( ::: ) { new_user = !check_if_user_exists(user); if (new_user) { get_password(on_got_password); } else { ::: } } void on_got_password( ::: ) { check_user(user, password, on_user_checked); } void on_user_checked( ::: ) { if (!user_valid) { on_got_username(user); } else { initialize_environment(on_environment_initialized); } } ::: 5
  6. 6. The Problem Continuations Schedulers Tasks Epilogue OUT OF CONTROL 6
  7. 7. The Problem Continuations Schedulers Tasks Epilogue OUT OF CONTROL “Spaghetti code” by George W. Hart 7
  8. 8. The Problem Continuations Schedulers Tasks Epilogue REASONS FOR WAITING User input Network actions Inter-process communication External process execution Communication with a slow database CPU-intensive work Heterogeneous computing ... 8
  9. 9. The Problem Continuations Schedulers Tasks Epilogue HIDEAWAY Wrapping it in task objects (QThread, KJob, ...) Methods with time-outs (select, ...) ... or with validity checks (QProcess::state, ...) Future values (future<T>, QFuture<T>, QDBusPendingReply<T>, ...) 9
  10. 10. CONTINUATIONS Lost in the Future Under wraps
  11. 11. The Problem Continuations Schedulers Tasks Epilogue LOST IN THE FUTURE Is it about monads? Callbacks? Signals and slots? 11
  12. 12. The Problem Continuations Schedulers Tasks Epilogue LOST IN THE FUTURE C++ standard proposal N3558, Boost.Thread 1.55.0 future<int> result = deepThought.meaningOfLife(); #if 0 // this would block cout << result.get(); #endif result.then([] (future<int> result) { // called when the result is available // call to .get() does not block here cout << result.get(); }); 12
  13. 13. The Problem Continuations Schedulers Tasks Epilogue LOST IN THE FUTURE | int i; | i.then(c); // ERROR! | future<int> future; | future.then(c); // ok | QFuture<int> qfuture; | qfuture.then(c); // ERROR! | | | | | | | | KJob *job; | job->then(c); // ERROR! | | | | | | 13
  14. 14. The Problem Continuations Schedulers Tasks Epilogue LOST IN THE FUTURE | int i; | c(i); | future<int> future; | future.then(c); | QFuture<int> qfuture; | auto watcher = new QFutureWatcher<int>(); | QObject::connect(watcher, | &QFutureWatcherBase::finished, | [=] { | c(watcher->result()); | watcher->deleteLater(); | }); | watcher->setFuture(qfuture); | KJob *job; | QObject::connect(job, | &KJob::finished, | [] (KJob *job) { | c(job-> ... something ...); | job->deleteLater(); | }); | 14
  15. 15. The Problem Continuations Schedulers Tasks Epilogue UNDER WRAPS template <typename _Job, typename _Continuation> void continue_with(_Job &&job, _Continuation &&continuation) { using is_nullary = typename std::is_constructible< std::function<void()>, _Continuation >::type; _continue_with_helper( job(), std::forward<_Continuation>(continuation), is_nullary() ); } 15
  16. 16. The Problem Continuations Schedulers Tasks Epilogue UNDER WRAPS template <typename _ReturnType, typename _Continuation> void _continue_with_helper(const _ReturnType &value, _Continuation &&continuation, std::true_type) { continuation(); } template <typename _ReturnType, typename _Continuation> void _continue_with_helper(const _ReturnType &value, _Continuation &&continuation, std::false_type) { using is_callable = ...; static_assert(is_callable::value, "The continuation needs to have zero or one argument"); continuation(value); } 16
  17. 17. The Problem Continuations Schedulers Tasks Epilogue UNDER WRAPS template <typename _ReturnType, typename _Continuation> void _continue_with_helper(const QFuture<_ReturnType> &future, _Continuation &&continuation, std::false_type) { if (!future.isFinished()) { auto watcher = new QFutureWatcher<_ReturnType>(); QObject::connect(watcher, &QFutureWatcherBase::finished, [=] { continuation(watcher->result()); watcher->deleteLater(); }); watcher->setFuture(future); } else continuation(future.result()); } 17
  18. 18. The Problem Continuations Schedulers Tasks Epilogue MATCHBOX template<typename _TestType, typename _ArgType> class has_then_method { private: template<typename U, void (U::*)(_ArgType)> struct test_struct {}; template<typename U> static std::true_type test(test_struct <U, &U::then> *); template<typename U> static std::false_type test(...); public: using type = decltype(test<_TestType>(nullptr)); static const bool value = std::is_same<type, std::true_type>::value; } 18
  19. 19. SCHEDULERS The Chains are On The New Order Set Your Controls for the Heart of the Sun
  20. 20. The Problem Continuations Schedulers Tasks Epilogue THE CHAINS ARE ON getUsername().then( [] (future<string> username) { getPassword().then( [=] (future<string> password) { createAccount(username, password).then( ... ); } ); } ); Localized, but still not readable. Can it be made nicer? 20
  21. 21. The Problem Continuations Schedulers Tasks Epilogue THE CHAINS ARE ON Can it be made to look like this? void login() { ... username = getUsername(); password = getPassword(); createAccount(username, password); } No, but ... 21
  22. 22. The Problem Continuations Schedulers Tasks Epilogue THE CHAINS ARE ON ... it could look like this: auto login = serial_ ( ... username = getUsername(), password = getPassword(), createAccount(username, password) ); Peculiar syntax, but much more readable. 22
  23. 23. The Problem Continuations Schedulers Tasks Epilogue THE NEW ORDER template <typename... _Jobs> class Serial; template <> class Serial<> : public QObject , protected QFutureInterface<int> { public: ~Serial() {} int operator()() { reportResult(0); reportFinished(); return 0; } }; 23
  24. 24. The Problem Continuations Schedulers Tasks Epilogue THE NEW ORDER template <typename _Job, typename... _Jobs> class Serial<_Job, _Jobs...> : public Serial<_Jobs...> { private: using tail_t = Serial<_Jobs...>; public: Serial(_Job &&job, _Jobs &&... jobs) : tail_t(std::forward<_Jobs>(jobs)...) , m_job(std::forward<_Job>(job)) {} QFuture<int> operator()() { auto future = this->future(); continue_with(std::ref(m_job), [&] { tail_t::operator()(); }); return future; } private: _Job m_job; }; 24
  25. 25. The Problem Continuations Schedulers Tasks Epilogue LET THERE BE MORE LIGHT while loop: while_( | while_(condition) ( condition, | body body | ) | ) branching: if_( | if_(condition) ( condition, | then_branch then_branch, | ).else_( else_branch | else_branch ) | ) 25
  26. 26. The Problem Continuations Schedulers Tasks Epilogue LET THERE BE MORE LIGHT asynchronous assignment var<int> value; value = 5; // immediate assignment value = someFuture(); // asynchronous assignment parallel execution parallel_( task1, task2, ... ) parallel without waiting detach_(task) producer-consumer transactions ... 26
  27. 27. The Problem Continuations Schedulers Tasks Epilogue SET YOUR CONTROLS... var<int> wait; serial_( test::writeMessage(0, "Starting the program"), wait = test::howMuchShouldIWait(7), test::writeMessageAsync(wait, "What is the answer to the Ultimate Question of Life, " "the Universe, and Everything?" ), while_(test::howMuchShouldIWait(0), test::writeMessageAsync(1, "42") ), serial_( test::writeMessageAsync(1, "We are going away..."), test::writeMessageAsync(1, "... sorry, but we have to.") ), test::writeMessage(0, "There, you have it!") )(); 27
  28. 28. The Problem Continuations Schedulers Tasks Epilogue ... FOR THE HEART OF THE SUN while_( // Wait until we get a connection. client = ws::server::accept(server), // Start a detached execution path to process the client. detach_([] { var<ws::client_header> header; var<ws::message> message; var<string> server_key; serial_( // WebSocket handshake header = ws::client::get_header(), server_key = ws::server::create_key(header), ws::client::send_header(client, server_key), // Sending the initial greeting message ws::client::message_write(client, "Hello, I’m Echo"), // Connection established while_( // getting and echoing the message message = ws::client::message_read(client), ws::client::message_write(client, message) ) ) }) ) 28
  29. 29. TASKS Lazy Day
  30. 30. The Problem Continuations Schedulers Tasks Epilogue LAZY DAY Problem: A method is executed while the arguments are evaluated. serial_( someMethod(0, "Starting the program"), ... ); someMethod must not do anything, but return a functor. 30
  31. 31. The Problem Continuations Schedulers Tasks Epilogue LAZY DAY So, your options are: void someMethod(...); serial_( std::bind(someMethod, 0, "Starting the program"), ... ) 31
  32. 32. The Problem Continuations Schedulers Tasks Epilogue LAZY DAY Or using a std::bind-based wrapper namespace detail { void someMethod(...); } auto someMethod(...) -> decltype(std::bind(detail::someMethod, std::forward arguments ...)) { return std::bind(detail::someMethod, std::forward arguments ...); } serial_( someMethod(0, "Starting the program"), ... ) 32
  33. 33. The Problem Continuations Schedulers Tasks Epilogue LAZY DAY Or using a simple wrapper: namespace detail { void someMethod(...); } auto someMethod = curry(detail::someMethod); serial_( someMethod(0, "Starting the program"), ... ) 33
  34. 34. The Problem Continuations Schedulers Tasks Epilogue EPILOGUE Benefits: Readable code, easy to reason about Automatic lifetime management Advanced control structures compared to plain C++ Things to get used to: Peculiar syntax Some functional programming constructs like purity, immutability, etc. are preferred Less expressive statements 34
  35. 35. The Problem Continuations Schedulers Tasks Epilogue ANSWERS? QUESTIONS! QUESTIONS? ANSWERS! Kudos: Friends at KDE Dr Saša Malkov KDAB basysKom LATEX and Beamer developers 35

×