Transcript
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


Recommended