Asynchronous Functions In C++
Upcoming SlideShare
Loading in...5
×
 

Asynchronous Functions In C++

on

  • 2,072 views

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

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

Statistics

Views

Total Views
2,072
Views on SlideShare
2,068
Embed Views
4

Actions

Likes
1
Downloads
44
Comments
0

3 Embeds 4

http://www.lmodules.com 2
http://www.slideshare.net 1
https://www.linkedin.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Asynchronous Functions In C++ Asynchronous Functions In C++ Presentation Transcript

    • Asynchronous Functions in C++ The Generic Approach Schalk W. Cronjé 20 April 2005 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 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. ACCU 2005 © Schalk W. Cronjé
    • McCall's Quality Factors ● Correctness ● Testability ● Reliability ● Flexibility ● Usability ● Integrity ● Maintainability ● Reusability ● Portability ● Interoperability ● Efficiency 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 ACCU 2005 © Schalk W. Cronjé
    • Prior Art ● Kevlin Henney ACCU 2003 / 2004 http://www.two-sdg.demon.co.uk/curbralan/papers/accu/MoreC++Threading.pdf ● Drazen Dotlic C++ Users Journal, Nov 2004 ● Boost Threads ● Boost Signals & Slots 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 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 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. ACCU 2005 © Schalk W. Cronjé
    • Asynchronous Mediums ● Threads ● Thread Pools ● Task Queues ● Spawned Processes ● XML-RPC & SOAP ● Interprocess Message Queues 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 ACCU 2005 © Schalk W. Cronjé
    • Building a Generic Interface ACCU 2005 © Schalk W. Cronjé
    • Creating a Task async_id create_task( Medium, Functor ); async_id create_task( Medium, Functor ); template <typename Medium> typename async_traits<Medium>::id_type create_task( Medium const& async_medium_, typename async_traits<Medium>::functor_type f_ ); ACCU 2005 © Schalk W. Cronjé
    • Asynchronous Traits #1 template <typename Medium> template <typename Medium> struct async_traits 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 }; }; ACCU 2005 © Schalk W. Cronjé
    • // Simple Medium example class thread_id; class SimpleThreader { public: // Creates a thread, runs the function thread_id create( boost::function< int() > ) const; }; ACCU 2005 © Schalk W. Cronjé
    • Asynchronous Traits #1 template <> template <> struct async_traits<SimpleThreader> struct async_traits<SimpleThreader> {{ typedef SimpleThreader::thread_id id_type; typedef boost::function< int()> functor_type; }; }; 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 ACCU 2005 © Schalk W. Cronjé
    • // boost::function makes easy work of wrapping // pointers to functions int my_func( int,int ); boost::function< int(int,int) > f1 = &my_func; std::cout << f1( 3, 4 ); // and even member functions using std::string; boost::function< string::size_type(string const*) > f2= &string::size; string s2(“Hello, World”); std::cout << f2(&s2); ACCU 2005 © Schalk W. Cronjé
    • Asynchronous Traits #1 template <> template <> struct async_traits<SimpleThreader> struct async_traits<SimpleThreader> {{ typedef SimpleThreader::thread_id id_type; typedef boost::function< int()> functor_type; static id_type create( SimpleThreader const& medium_, constness is not a requirement functor_type f_ ) {return medium_.create(f_);} }; }; ACCU 2005 © Schalk W. Cronjé
    • // Calculate information flow of all source files // in a directory int calc_IF4( const char* directory_ ); SimpleThreader threader; thread_id id= create_task( threader, boost::bind( &calc_IF4,”/myproj” ) ); ACCU 2005 © Schalk W. Cronjé
    • Completing create_task template <typename Medium> typename async_traits<Medium>::id_type create_task( Medium const& async_medium_, typename async_traits<Medium>::functor_type f_ ) { return async_traits<Medium>::create( async_medium_, f_ create_task can be complemented by a ); version taking a mutable } reference to the asynchronous medium ACCU 2005 © Schalk W. Cronjé
    • Restricting the return type template <typename Medium> typename async_traits<Medium>::id_type create_task( Medium const& async_medium_, typename async_traits<Medium>::functor_type f_ ) { BOOST_STATIC_ASSERT(( boost::is_object<typename async_traits<Medium>::id_type>::value )); return async_traits<Medium>::create( async_medium_, f_ ); } ACCU 2005 © Schalk W. Cronjé
    • // Example thread_id class 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*); }; ACCU 2005 © Schalk W. Cronjé
    • Waiting for a Task void wait_task( task_id ); bool task_completed( task_id ); template <typename TaskID> void wait_task( TaskID const& ); template <typename TaskID> bool task_completed( TaskID const& ); ACCU 2005 © Schalk W. Cronjé
    • Asynchronous Traits #2 template <typename Medium> template <typename Medium> struct async_traits struct async_traits {{ 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); }; }; ACCU 2005 © Schalk W. Cronjé
    • Asynchronous Traits #2 template <> template <> struct async_traits<SimpleThreader> struct async_traits<SimpleThreader> {{ typedef SimpleThreader::thread_id id_type; 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_ ); }; }; 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 complete wait_task( id ); ACCU 2005 © Schalk W. Cronjé
    • Completing wait_task template <typename TaskID> void wait_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; else traits::wait(task_id_); } } ACCU 2005 © Schalk W. Cronjé
    • async_traits_of template <typename TaskID> struct async_traits_of { typedef typename async_traits< typename TaskID::medium_type > type; }; Easy to specialise if necessary ACCU 2005 © Schalk W. Cronjé
    • Collecting the Result result_pointer task_result( task_id ); template <typename TaskID> typename async_pointer_of<TaskID>::type task_result( TaskID const& task_id_ ); If task has not completed result is null ACCU 2005 © Schalk W. Cronjé
    • Asynchronous Traits #3 template <typename Medium> template <typename Medium> struct async_traits struct async_traits {{ 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); typedef implementation-defined result_type; typedef implementation-defined result_pointer; static result_pointer get_result(id_type); }; }; ACCU 2005 © Schalk W. Cronjé
    • Asynchronous Traits #3 template <> template <> struct async_traits<SimpleThreader> struct async_traits<SimpleThreader> {{ typedef SimpleThreader::thread_id id_type; 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();} }; }; ACCU 2005 © Schalk W. Cronjé
    • Completing task_result template <typename TaskID> typename async_pointer_of<TaskID>::type task_result( TaskID const& task_id_ ) { typedef typename async_traits_of<TaskID>::type traits; BOOST_STATIC_ASSERT(( !boost::is_void< typename traits::result_type>::value )); if( !task_completed(task_id_) ) return async_pointer_of<TaskID>::type(); else return traits::get_result(task_id_); } 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 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> typename async_traits<Medium>::id_type create_task( Medium const& async_medium_, typename async_traits<Medium>::functor_type f_, boost::function<void( typename async_traits<Medium>::id_type )> notify_ ); ACCU 2005 © Schalk W. Cronjé
    • Asynchronous Traits #4 template <typename Medium> template <typename Medium> struct async_traits struct async_traits {{ 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); typedef boost::function<void(id_type)> notification_type; static id_type create_detached (Medium,functor_type,notification_type); }; }; ACCU 2005 © Schalk W. Cronjé
    • Asynchronous Traits #3 template <> template <> struct async_traits<SimpleThreader> struct async_traits<SimpleThreader> {{ typedef SimpleThreader::thread_id id_type; typedef SimpleThreader::thread_id id_type; typedef boost::function< int()> functor_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 ); }; }; ACCU 2005 © Schalk W. Cronjé
    • Asynchronous Traits Summary template <typename Medium> template <typename Medium> struct async_traits 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); 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 ); 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 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. ACCU 2005 © Schalk W. Cronjé
    • Extending Traits template <typename Medium> template <typename Medium> struct async_traits struct async_traits {{ BOOST_STATIC_CONSTANT(bool,is_detachable=true); BOOST_STATIC_CONSTANT(bool,is_detachable=true); // ... // ... }; }; template <typename Medium> template <typename Medium> typename async_traits<Medium>::task_id typename async_traits<Medium>::task_id create_task( /* parms omitted for brevity */ ) create_task( /* parms omitted for brevity */ ) {{ BOOST_STATIC_ASSERT(async_traits<Medium>::is_detachable); BOOST_STATIC_ASSERT(async_traits<Medium>::is_detachable); }} ACCU 2005 © Schalk W. Cronjé
    • Improve performance by using thread pools or task queues Start with simple threaded app Use distributed computing by going out of process or out of host ACCU 2005 © Schalk W. Cronjé
    • Concluding 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 ACCU 2005 © Schalk W. Cronjé