45
ACCU 2005 © Schalk W. Cronjé Asynchronous Functions in C++ The Generic Approach Schalk W. Cronjé 20 April 2005

Asynchronous Functions In C++

Embed Size (px)

DESCRIPTION

Early dabble with futures in C++ using meta-programming and templates

Citation preview

Page 1: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Functions in C++

The Generic Approach

Schalk W. Cronjé

20 April 2005

Page 2: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Why asynchronous processing?

● All “real-time” systems require some form of asynchronous architecture.

● Anything that needs to do background processing whilst attending to other tasks or user input require an asynchronous architecture of some sort

Page 3: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Cost of Switching Architecture

In any standard implementation there is usually a significant cost of effort and time in switching between different asynchronous mediums.

Page 4: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

McCall's Quality Factors

● Correctness● Reliability● Usability● Maintainability● Portability● Efficiency

● Testability● Flexibility● Integrity● Reusability● Interoperability

Page 5: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Not just Threads!

Asynchronous execution extends far beyond just threads.

Threads are but a part of a much bigger picture.

Since threads are a known quantity they remain a good metaphor to explain the concepts surrounding asynchronous C++ functions

Page 6: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Prior Art

● Kevlin HenneyACCU 2003 / 2004http://www.two-sdg.demon.co.uk/curbralan/papers/accu/MoreC++Threading.pdf

● Drazen DotlicC++ Users Journal, Nov 2004

● Boost Threads

● Boost Signals & Slots

Page 7: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Design Goals

● The ability to execute a free function, member function or functor asynchronously without regard to the asynchronous medium

● The ability to change asynchronous mediums with as little code change as possible

● Keep it portable

Page 8: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

The 5 Essentials

● Launching a function asynchronously

● Knowing whether the function has completed

● Waiting for a function to complete

● Collecting the result

● Asynchronous notification

Page 9: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Primitives

● Threads (+mutexes etc.)● Async IO● C Signals

Of these three only threads can be ported easily and can play nicely with C++. Therefore threads can be used to build asynchronous mediums without being used as one itself.

Page 10: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Mediums

● Threads ● Thread Pools● Task Queues● Spawned Processes● XML-RPC & SOAP● Interprocess Message Queues

Page 11: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

To detach or not

● A detached task is one on which synchronous waits cannot be performed

● Win32 & Pthreads distinguish between detached and non-detached threads.

● Non-detached threads require a cleanup to be performed after thread has terminated

● boost::threads uses d-tor to detach a thread● It is debatable whether all tasks should

automatically be created in a detached state

Page 12: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Building a Generic Interface

Page 13: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Creating a Task

async_id create_task( Medium, Functor );async_id create_task( Medium, Functor );

template <typename Medium>create_task(

Medium const& async_medium_,

);

typename async_traits<Medium>::id_type

typename async_traits<Medium>::functor_type f_

Page 14: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Traits #1

template <typename Medium>struct async_traits {

};

template <typename Medium>struct async_traits {

};

typedef implementation-defined id_type;typedef implementation-defined functor_type;static id_type create(Medium,functor_type);

id_type must be non-native, but lightweight copyable

Page 15: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

// Simple Medium exampleclass thread_id;class SimpleThreader{

public:

// Creates a thread, runs the functionthread_id create( boost::function< int() > ) const;

};

Page 16: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Traits #1

template <>struct async_traits<SimpleThreader> {

};

template <>struct async_traits<SimpleThreader> {

};

typedef SimpleThreader::thread_id id_type;typedef boost::function< int()> functor_type;

Page 17: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

boost::function

The Boost.Function library contains a family of class templates that are function object wrappers. The notion is similar to a generalized callback.

http://www.boost.org/doc/html/function.html

Page 18: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

// boost::function makes easy work of wrapping// pointers to functionsint my_func( int,int );boost::function< int(int,int) > f1 = &my_func;std::cout << f1( 3, 4 );// and even member functionsusing std::string;boost::function< string::size_type(string const*) > f2= &string::size;string s2(“Hello, World”);std::cout << f2(&s2);

Page 19: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Traits #1

template <>struct async_traits<SimpleThreader> {

};

template <>struct async_traits<SimpleThreader> {

};

typedef SimpleThreader::thread_id id_type;typedef boost::function< int()> functor_type;

static id_type create( SimpleThreader const& medium_, functor_type f_ ){return medium_.create(f_);}

constness is not a requirement

Page 20: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

// Calculate information flow of all source files// in a directoryint calc_IF4( const char* directory_ );

SimpleThreader threader;

thread_id id= create_task( threader, boost::bind( &calc_IF4,”/myproj” )

);

Page 21: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Completing create_tasktemplate <typename Medium>typename async_traits<Medium>::id_typecreate_task(

Medium const& async_medium_,typename async_traits<Medium>::functor_type f_

){

}

return async_traits<Medium>::create(async_medium_,f_

);create_task can becomplemented by a version taking a mutable reference to the asynchronous medium

Page 22: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Restricting the return typetemplate <typename Medium>typename async_traits<Medium>::id_typecreate_task(

Medium const& async_medium_,typename async_traits<Medium>::functor_type f_

){

}

return async_traits<Medium>::create(async_medium_,f_

);

BOOST_STATIC_ASSERT(( boost::is_object<typenameasync_traits<Medium>::id_type>::value ));

Page 23: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

// Example thread_idclass thread_id{

friend class SimpleThreader;public:

typedef SimpleThreader medium_type;thread_id();thread_id(thread_id const&);bool done() const;void wait() const;int const* data() const;

private:class low_level_impl;boost:shared_ptr<low_level_impl> m_pImpl;thread_id(low_level_impl*);

};

Page 24: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Waiting for a Task

void wait_task( task_id );bool task_completed( task_id );

template <typename TaskID>voidwait_task( TaskID const& );template <typename TaskID>booltask_completed( TaskID const& );

Page 25: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Traits #2

template <typename Medium>struct async_traits {

};

template <typename Medium>struct async_traits {

};

static void wait(id_type);static bool completed(id_type);static bool detached(id_type);

typedef implementation-defined id_type;typedef implementation-defined functor_type;static id_type create(Medium,functor_type);

Page 26: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Traits #2

template <>struct async_traits<SimpleThreader> { typedef SimpleThreader::thread_id id_type;

};

template <>struct async_traits<SimpleThreader> { typedef SimpleThreader::thread_id id_type;

};

static bool completed( id_type const& id_ ) { return id_.done();} static void wait( id_type const& id_ ) { id_.wait(); } static bool detached( id_type const& id_ );

Page 27: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

// Having started a task to calculate IF4// we can check whether a task has completed

if( !task_completed( id ) ) {

// do something else first}// or just simply wait for task to completewait_task( id );

Page 28: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Completing wait_tasktemplate <typename TaskID>voidwait_task( TaskID const& task_id_ ){

}

typedef typename async_traits_of<TaskID>::type traits;

if( !task_completed(task_id_) ){

if( traits::detached(task_id_) )throw invalid_operation;

elsetraits::wait(task_id_);

}

Page 29: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

async_traits_oftemplate <typename TaskID>struct async_traits_of{

typedef typename async_traits<typename TaskID::medium_type

> type;};

Easy to specialise if necessary

Page 30: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Collecting the Result

result_pointer task_result( task_id );

template <typename TaskID>task_result( TaskID const& task_id_ );typename async_pointer_of<TaskID>::type

If task has not completedresult is null

Page 31: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Traits #3

template <typename Medium>struct async_traits {

};

template <typename Medium>struct async_traits {

};

typedef implementation-defined result_type;typedef implementation-defined result_pointer;static result_pointer get_result(id_type);

typedef implementation-defined id_type;typedef implementation-defined functor_type;static id_type create(Medium,functor_type);static void wait(id_type);static bool completed(id_type);static bool detached(id_type);

Page 32: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Traits #3

template <>struct async_traits<SimpleThreader> { typedef SimpleThreader::thread_id id_type;

};

template <>struct async_traits<SimpleThreader> { typedef SimpleThreader::thread_id id_type;

};

typedef int result_type; typedef int const* result_ptr; static result_ptr get_result( id_type const& id_ ) {return id_.data();}

Page 33: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Completing task_resulttemplate <typename TaskID>typename async_pointer_of<TaskID>::typetask_result( TaskID const& task_id_ ){

}

typedef typename async_traits_of<TaskID>::type traits;

if( !task_completed(task_id_) )return async_pointer_of<TaskID>::type();

elsereturn traits::get_result(task_id_);

BOOST_STATIC_ASSERT(( !boost::is_void<typename traits::result_type>::value ));

Page 34: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Detaching & Notification

● In order to achieve true asynchronous functions notifications must be implemented

● If a function is launched in a detached mode the only way to know when it has finished is via a callback or alternative notification

● It is important that notifications are asynchronous safe – at least for the medium on which they are applied

Page 35: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Creating a Detached Task

async_id create_task( Medium, Functor, Notifier );async_id create_task( Medium, Functor, Notifier );

template <typename Medium>create_task(

Medium const& async_medium_,

);

typename async_traits<Medium>::id_type

typename async_traits<Medium>::functor_type f_, boost::function<void( typename async_traits<Medium>::id_type )> notify_

Page 36: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Traits #4template <typename Medium>struct async_traits {

};

template <typename Medium>struct async_traits {

};

typedef boost::function<void(id_type)> notification_type;static id_type create_detached (Medium,functor_type,notification_type);

typedef implementation-defined id_type;typedef implementation-defined functor_type;typedef implementation-defined result_type;typedef implementation-defined result_pointer;static id_type create(Medium,functor_type);static void wait(id_type);static bool completed(id_type);static bool detached(id_type);static result_pointer get_result(id_type);

Page 37: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Traits #3

template <>struct async_traits<SimpleThreader> { typedef SimpleThreader::thread_id id_type; typedef boost::function< int()> functor_type;

};

template <>struct async_traits<SimpleThreader> { typedef SimpleThreader::thread_id id_type; typedef boost::function< int()> functor_type;

};

typedef boost::function< void(id_type) > notification_type; static id_type create_detached( SimpleThreader const&, function_type, notification_type );

Page 38: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Traits Summarytemplate <typename Medium>struct async_traits {

};

template <typename Medium>struct async_traits {

};

typedef implementation-defined id_type;typedef implementation-defined functor_type;typedef implementation-defined result_type;typedef implementation-defined result_pointer;typedef boost::function<void(id_type) notification_type;static id_type create(Medium,functor_type);static id_type create_detached (Medium,functor_type,notification_type);static void wait(id_type);static bool completed(id_type);static bool detached(id_type);static result_pointer get_result(id_type);

Page 39: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Asynchronous Function Summary

id_type create_task( Medium, Functor );id_type create_task( Medium, Functor, Notifier );void wait_task( id_type );bool task_completed( id_type );result_pointer task_result( id_type );

Page 40: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Additional Considerations

● Task identifiers must be lightweight copyable

● A completed task's result must be available until the last task identifier for that task has been removed.

● boost::shared_ptr generally the easiest way to accomplish both the above

Page 41: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Extending the Traits

● Some mediums might not be detachable. ● This can be handled by adding an additional

is_detachable constant.● create_task(m,f,n) can assert on this during

compile time.

Page 42: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Extending Traitstemplate <typename Medium>struct async_traits {

BOOST_STATIC_CONSTANT(bool,is_detachable=true);// ...

};template <typename Medium>typename async_traits<Medium>::task_idcreate_task( /* parms omitted for brevity */ ){

BOOST_STATIC_ASSERT(async_traits<Medium>::is_detachable);}

template <typename Medium>struct async_traits {

BOOST_STATIC_CONSTANT(bool,is_detachable=true);// ...

};template <typename Medium>typename async_traits<Medium>::task_idcreate_task( /* parms omitted for brevity */ ){

BOOST_STATIC_ASSERT(async_traits<Medium>::is_detachable);}

Page 43: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Start with simplethreaded app

Improve performanceby using thread poolsor task queues

Use distributed computing by goingout of process orout of host

Page 44: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

Concluding

Page 45: Asynchronous Functions In C++

ACCU 2005© Schalk W. Cronjé

A generic approach to asynchronous execution is not a golden solution, but it goes a long way to decoupling the asynchronous architecture from

the business logic.

It allows for selection of an architecture based upon underlying platform without having to modify the overlaying business application