natural task scheduling using futures and continuations, ivan Čukić, qt developer days 2013

35
NATURAL TASK SCHEDULING Using Futures and Continuations Qt Developer Days 2013 Ivan Čukić [email protected] ivan.fomentgroup.org/blog

Upload: ivan-cukic

Post on 10-May-2015

4.090 views

Category:

Technology


1 download

DESCRIPTION

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.

TRANSCRIPT

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

NATURAL TASK SCHEDULING

Using Futures and Continuations

Qt Developer Days 2013

Ivan Čukić

[email protected]

ivan.fomentgroup.org/blog

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

THE PROBLEM

Meet Jeff

Out of Control

Reasons for Waiting

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

The Problem Continuations Schedulers Tasks Epilogue

MEET JEFF

3

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

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

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

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

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

The Problem Continuations Schedulers Tasks Epilogue

OUT OF CONTROL

6

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

The Problem Continuations Schedulers Tasks Epilogue

OUT OF CONTROL

“Spaghetti code” by George W. Hart

7

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

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

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

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

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

CONTINUATIONS

Lost in the Future

Under wraps

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

The Problem Continuations Schedulers Tasks Epilogue

LOST IN THE FUTURE

Is it about monads?

Callbacks?

Signals and slots?

11

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

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 blockcout << result.get();

#endif

result.then([] (future<int> result) {// called when the result is available// call to .get() does not block herecout << result.get();

});

12

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

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

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

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

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

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

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

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

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

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

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

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

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

SCHEDULERS

The Chains are On

The New Order

Set Your Controls for the Heart of the Sun

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

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

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

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

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

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

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

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

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

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

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

The Problem Continuations Schedulers Tasks Epilogue

LET THERE BE MORE LIGHT

while loop:

while_( | while_(condition) (condition, | bodybody |

) | )

branching:

if_( | if_(condition) (condition, | then_branchthen_branch, | ).else_(else_branch | else_branch

) | )

25

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

The Problem Continuations Schedulers Tasks Epilogue

LET THERE BE MORE LIGHT

asynchronous assignment

var<int> value;

value = 5; // immediate assignmentvalue = someFuture(); // asynchronous assignment

parallel execution

parallel_(task1,task2,...

)

parallel without waiting

detach_(task)

producer-consumer

transactions

...

26

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

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

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

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 handshakeheader = ws::client::get_header(),server_key = ws::server::create_key(header),ws::client::send_header(client, server_key),

// Sending the initial greeting messagews::client::message_write(client, "Hello, I’m Echo"),

// Connection establishedwhile_(

// getting and echoing the messagemessage = ws::client::message_read(client),ws::client::message_write(client, message)

))

}))

28

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

TASKS

Lazy Day

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

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

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

The Problem Continuations Schedulers Tasks Epilogue

LAZY DAY

So, your options are:

void someMethod(...);

serial_(std::bind(someMethod, 0, "Starting the program"),...

)

31

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

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

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

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

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

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

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

The Problem Continuations Schedulers Tasks Epilogue

ANSWERS? QUESTIONS! QUESTIONS? ANSWERS!

Kudos:

Friends at KDE

Dr Saša Malkov

KDAB

basysKom

L

A

T

E

X and Beamer developers

35