commit 39ec793737f046d94a3c0d8e0312d2112bec7f99 Author: Oliver Kowalke Date: Wed Feb 9 18:41:35 2011 +0100 initial checkin diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..814bbb44 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +bin.v2/**/* +dist/**/* +**/*/doc/**/* +doc/**/* +html/**/* +stage/**/* +tools/**/* +docbook* +*~ +*.html +*.sw* +*.xml diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..54311152 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright Troy D. Straszheim 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +message(SEND_ERROR " +Boost *IS* buildable with cmake, however this is +distributed separately. + +See the boost-cmake wiki: + + https://svn.boost.org/trac/boost/wiki/CMake + +For the location of CMakeable distributions. +" +) + diff --git a/README.txt b/README.txt new file mode 100755 index 00000000..578baa36 --- /dev/null +++ b/README.txt @@ -0,0 +1,130 @@ + +Version 1.42.0 + +New Libraries + + * Uuid: A universally unique identifier, from Andy Tompkins. + +Updated Libraries + + * Asio: + + Added a new HTTP Server 4 example illustrating the use of stackless coroutines with Asio. + + Changed handler allocation and invocation to use boost::addressof to get the address of handler objects, rather than applying operator& directly (#2977). + + Restricted MSVC buffer debugging workaround to 2008, as it causes a crash with 2010 beta 2 (#3796, #3822). + + Fixed a problem with the lifetime of handler memory, where Windows needs the OVERLAPPED structure to be valid until both the initiating function call has returned and the completion packet has been delivered. + + Don't block signals while performing system calls, but instead restart the calls if they are interrupted. + + Documented the guarantee made by strand objects with respect to order of handler invocation. + + Changed strands to use a pool of implementations, to make copying of strands cheaper. + + Ensured that kqueue support is enabled for BSD platforms (#3626). + + Added a boost_ prefix to the extern "C" thread entry point function (#3809). + + In getaddrinfo emulation, only check the socket type (SOCK_STREAM or SOCK_DGRAM) if a service name has been specified. This should allow the emulation to work with raw sockets. + + Added a workaround for some broken Windows firewalls that make a socket appear bound to 0.0.0.0 when it is in fact bound to 127.0.0.1. + + Applied a fix for reported excessive CPU usage under Solaris (#3670). + + Added some support for platforms that use older compilers such as g++ 2.95 (#3743). + * Circular Buffer: + + Added methods erase_begin(size_type) and erase_end(size_type) with constant complexity for such types of stored elements which do not need an explicit destruction e.g. int or double. + + Similarly changed implementation of the clear() method and the destructor so their complexity is now constant for such types of stored elements which do not require an explicit destruction (the complexity for other types remains linear). + * Fusion: + + The accumulator is the first argument to the functor of fusion::fold and fusion::accumulate (#2355). + + Added support for associative iterators and views (#3473). + * Graph: + + Removed old interface to compressed_sparse_row_graph, making new interface the default. + * Integer: + + Reverted Trunk to release branch state (i.e. a "known good state"). + + Fixed issues: 653, 3084, 3177, 3180, 3568, 3657, 2134. + + Added long long support to boost::static_log2, boost::static_signed_min, boost::static_signed_max, boost::static_unsigned_minboost::static_unsigned_max, when available. + + The argument type and the result type of boost::static_signed_min etc are now typedef'd. Formerly, they were hardcoded as unsigned long and int respectively. Please, use the provided typedefs in new code (and update old code as soon as possible). + * Iostreams: + + Fixed many outstanding issues. Thanks to Richard Smith for his work on this. (#3612, #3311, #2094, #3010, #2894, #3011, #3352, #3505). + + For more information see the library release notes. + * Program.Options: + + Information about option name added to a few exception classes and various clean ups in exception classes (#3423). + + Description wordwrapping in presense of default parameters fixed (#2613). + + Empty value in configuration file is now permitted (#1537). + + Quotes are no longer stripped from string values (#850). + + Fix endless loop in case of long default arguments (#689). + + Fix compile warning caused by usage of boost::any (#2562). + + Fix memory bug in example/response_file.cpp (#3525). + + Most compilation warnings were fixed (#3608). + + Make column width for description text configurable. (#3703). + + Add general split function: split_unix() (#2561). + + Enable open config files from given file name (#3264). + + Additional flag for required options (#2982). + + Enable case insensitive style for command line (#3498). + * PropertyMap: + + Removed old header files (directly in the boost/ directory); they were deprecated since 1.40, replaced by headers in boost/property_map/. + * Proto: + + Fix const correctness issues with proto::flatten and friends (#3364). + + Accomodate recent change to fusion::fold, remove old support for Doxygen and pre-1.35 Fusion (#3553). + + In binary operations, when one operand has a user-specified domain and the other has the default domain, the user-specified domain trumps. + + Fix BOOST_PROTO_EXTENDS to work with elaborated types. + + Work around EDG compiler bug with function types and cv-qualification. + * Regex: + + Added support for Functors rather than strings as format expressions. + + Improved error reporting when throwing exceptions to include better more relevant information. + + Improved performance and reduced stack usage of recursive expressions. + + Fixed tickets #2802, #3425, #3507, #3546, #3631, #3632, #3715, #3718, #3763, #3764 + * Spirit: Spirit V2.2, see the 'What's New' section for details. + * Unordered: + + Support instantiating the containers with incomplete value types. + + Add erase_return_void as a temporary workaround for the current erase which can be inefficient because it has to find the next element to return an iterator (#3693). + + Add templated find overload for compatible keys. + + Improved codegear compatibility. + + Other minor changes, full details in the changelog. + * Xpressive: + + match_results no longer relies on undefined behavior in std::list (#3278). + + Do NOT copy singular iterators (#3538). + + Eliminate gcc and darwin warnings (#3734). + +Compilers Tested + + Boost's primary test compilers are: + * OS X: + + GCC 4.0.1 on Intel Leopard. + + GCC 4.0.1 on PowerPC Tiger. + * Linux: + + GCC 4.4.1 on Ubuntu Linux. + + GCC 4.4 on Debian. + * Windows: + + Visual C++ 7.1 SP1, 8.0 SP1 and 9.0 SP1 on Windows XP. + + Visual C++ 9.0 on Windows 2008, 64 bit. + + GCC 4.3.3, using Mingw + * FreeBSD: + + GCC 4.2.1, 32 and 64 bit. + + Boost's additional test compilers include: + * Linux: + + Intel 10.1 on Red Hat Enterprise Linux. + + Intel 10.1 on 64 bit Red Hat Enterprise Linux. + + Intel 11.0 on 32 bit Red Hat Enterprise Linux. + + Intel 11.0 on 64 bit Red Hat Enterprise Linux. + + Intel 11.1 on 64 bit Red Hat Enterprise Linux. + + Intel 11.1 on 64 bit Linux Redhat 5.1 Server. + + Intel 11.1 on Suse Linux 64 bit. + + GCC 3.4.6, GCC 4.2.4, GCC 4.3.4 and GCC 4.4.2 on Red Hat Enterprise Linux. + + GCC 4.3.4 and GCC 4.4.2 with C++0x extensions on Red Hat Enterprise Linux. + + GCC 4.4.1 on 64 bit Linux. + + GCC 4.4.3 on Debian unstable. + + QLogic PathScale(TM) Compiler Suite: Version 3.2 on Red Hat Enterprise Linux. + * OS X: + + Intel C++ Compiler 10.1, 11.0, 11.1 on Leopard. + + GCC 4.0.1 on Intel Leopard. + + GCC 4.0.1 on PowerPC Tiger. + * Windows: + + Visual C++ 7.1, 8,0, 9,0 on XP. + + Visual C++ 9.0 using STLport 5.2 on XP and Windows Mobile 5.0. + + Visual C++ 10.0 beta 2. + + Visual C++ 10.0 on 32-bit Vista. + + Borland/Codegear C++ 5.9.3, 6.1.3 (2009), 6.2.1 (2010). + + Intel C++ 11.1, with a Visual C++ 9.0 backend, on Vista 32-bit. + + GCC 4.4.1 on Mingw, with and without C++0x extensions. + * AIX: + + IBM XL C/C++ Enterprise Edition for AIX, V10.1.0.0, on AIX Version 5.3.0.40. + * FreeBSD: + + GCC 4.2.1 on FreeBSD 7.0, 32 bit and 64 bit. + * Solaris: + + Sun C++ 5.10 on Solaris 5.10. + +Acknowledgements + + Beman Dawes, Eric Niebler, Rene Rivera, and Daniel James managed this release. diff --git a/boost/atomic.hpp b/boost/atomic.hpp new file mode 100644 index 00000000..b05234f6 --- /dev/null +++ b/boost/atomic.hpp @@ -0,0 +1,204 @@ +#ifndef BOOST_ATOMIC_HPP +#define BOOST_ATOMIC_HPP + +// Copyright (c) 2009 Helge Bahmann +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include +#include + +namespace boost { + +template +class atomic : public detail::atomic::internal_atomic { +public: + typedef detail::atomic::internal_atomic super; + + atomic() {} + explicit atomic(T v) : super(v) {} +private: + atomic(const atomic &); + void operator=(const atomic &); +}; + + +template<> +class atomic : private detail::atomic::internal_atomic { +public: + typedef detail::atomic::internal_atomic super; + + atomic() {} + explicit atomic(bool v) : super(v) {} + + using super::load; + using super::store; + using super::compare_exchange_strong; + using super::compare_exchange_weak; + using super::exchange; + using super::is_lock_free; + + operator bool(void) const volatile {return load();} + bool operator=(bool v) volatile {store(v); return v;} +private: + atomic(const atomic &); + void operator=(const atomic &); +}; + +template<> +class atomic : private detail::atomic::internal_atomic { +public: + typedef detail::atomic::internal_atomic super; + + atomic() {} + explicit atomic(void * p) : super(p) {} + using super::load; + using super::store; + using super::compare_exchange_strong; + using super::compare_exchange_weak; + using super::exchange; + using super::is_lock_free; + + operator void *(void) const volatile {return load();} + void * operator=(void * v) volatile {store(v); return v;} + +private: + atomic(const atomic &); + void * operator=(const atomic &); +}; + +/* FIXME: pointer arithmetic still missing */ + +template +class atomic : private detail::atomic::internal_atomic { +public: + typedef detail::atomic::internal_atomic super; + + atomic() {} + explicit atomic(T * p) : super((intptr_t)p) {} + + T *load(memory_order order=memory_order_seq_cst) const volatile + { + return (T*)super::load(order); + } + void store(T *v, memory_order order=memory_order_seq_cst) volatile + { + super::store((intptr_t)v, order); + } + bool compare_exchange_strong( + T * &expected, + T * desired, + memory_order order=memory_order_seq_cst) volatile + { + return compare_exchange_strong(expected, desired, order, detail::atomic::calculate_failure_order(order)); + } + bool compare_exchange_weak( + T * &expected, + T *desired, + memory_order order=memory_order_seq_cst) volatile + { + return compare_exchange_weak(expected, desired, order, detail::atomic::calculate_failure_order(order)); + } + bool compare_exchange_weak( + T * &expected, + T *desired, + memory_order success_order, + memory_order failure_order) volatile + { + intptr_t expected_=(intptr_t)expected, desired_=(intptr_t)desired; + bool success=super::compare_exchange_weak(expected_, desired_, success_order, failure_order); + expected=(T*)expected_; + return success; + } + bool compare_exchange_strong( + T * &expected, + T *desired, + memory_order success_order, + memory_order failure_order) volatile + { + intptr_t expected_=(intptr_t)expected, desired_=(intptr_t)desired; + bool success=super::compare_exchange_strong(expected_, desired_, success_order, failure_order); + expected=(T*)expected_; + return success; + } + T *exchange(T * replacement, memory_order order=memory_order_seq_cst) volatile + { + return (T*)super::exchange((intptr_t)replacement, order); + } + using super::is_lock_free; + + operator T *(void) const volatile {return load();} + T * operator=(T * v) volatile {store(v); return v;} + + T * fetch_add(ptrdiff_t diff, memory_order order=memory_order_seq_cst) volatile + { + return (T*)super::fetch_add(diff*sizeof(T), order); + } + T * fetch_sub(ptrdiff_t diff, memory_order order=memory_order_seq_cst) volatile + { + return (T*)super::fetch_sub(diff*sizeof(T), order); + } + + T *operator++(void) volatile {return fetch_add(1)+1;} + T *operator++(int) volatile {return fetch_add(1);} + T *operator--(void) volatile {return fetch_sub(1)-1;} + T *operator--(int) volatile {return fetch_sub(1);} +private: + atomic(const atomic &); + T * operator=(const atomic &); +}; + +class atomic_flag : private atomic { +public: + typedef atomic super; + using super::is_lock_free; + + atomic_flag(bool initial_state) : super(initial_state?1:0) {} + atomic_flag() {} + + bool test_and_set(memory_order order=memory_order_seq_cst) + { + return super::exchange(1, order); + } + void clear(memory_order order=memory_order_seq_cst) + { + super::store(0, order); + } +}; + +typedef atomic atomic_char; +typedef atomic atomic_uchar; +typedef atomic atomic_schar; +typedef atomic atomic_uint8_t; +typedef atomic atomic_int8_t; +typedef atomic atomic_ushort; +typedef atomic atomic_short; +typedef atomic atomic_uint16_t; +typedef atomic atomic_int16_t; +typedef atomic atomic_uint; +typedef atomic atomic_int; +typedef atomic atomic_uint32_t; +typedef atomic atomic_int32_t; +typedef atomic atomic_ulong; +typedef atomic atomic_long; +typedef atomic atomic_uint64_t; +typedef atomic atomic_int64_t; +typedef atomic atomic_ullong; +typedef atomic atomic_llong; +typedef atomic atomic_address; +typedef atomic atomic_bool; + +static inline void atomic_thread_fence(memory_order order) +{ + detail::atomic::platform_atomic_thread_fence(order); +} + +} + +#endif diff --git a/boost/atomic/detail/base.hpp b/boost/atomic/detail/base.hpp new file mode 100644 index 00000000..72f58bf6 --- /dev/null +++ b/boost/atomic/detail/base.hpp @@ -0,0 +1,186 @@ +#ifndef BOOST_DETAIL_ATOMIC_BASE_HPP +#define BOOST_DETAIL_ATOMIC_BASE_HPP + +// Copyright (c) 2009 Helge Bahmann +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +namespace boost { +namespace detail { +namespace atomic { + +static inline memory_order calculate_failure_order(memory_order order) +{ + switch(order) { + case memory_order_acq_rel: return memory_order_acquire; + case memory_order_release: return memory_order_relaxed; + default: return order; + } +} + +template +class platform_atomic : public fallback_atomic { +public: + typedef fallback_atomic super; + + explicit platform_atomic(T v) : super(v) {} + platform_atomic() {} +protected: + typedef typename super::integral_type integral_type; +}; + +template +class platform_atomic_integral : public build_atomic_from_exchange > { +public: + typedef build_atomic_from_exchange > super; + + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral() {} +protected: + typedef typename super::integral_type integral_type; +}; + +template +static inline void platform_atomic_thread_fence(T order) +{ + /* FIXME: this does not provide + sequential consistency, need one global + variable for that... */ + platform_atomic a; + a.exchange(0, order); +} + +template::test> +class internal_atomic; + +template +class internal_atomic : private detail::atomic::platform_atomic { +public: + typedef detail::atomic::platform_atomic super; + + internal_atomic() {} + explicit internal_atomic(T v) : super(v) {} + + operator T(void) const volatile {return load();} + T operator=(T v) volatile {store(v); return v;} + + using super::is_lock_free; + using super::load; + using super::store; + using super::exchange; + + bool compare_exchange_strong( + T &expected, + T desired, + memory_order order=memory_order_seq_cst) volatile + { + return super::compare_exchange_strong(expected, desired, order, calculate_failure_order(order)); + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order order=memory_order_seq_cst) volatile + { + return super::compare_exchange_strong(expected, desired, order, calculate_failure_order(order)); + } + bool compare_exchange_strong( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + return super::compare_exchange_strong(expected, desired, success_order, failure_order); + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + return super::compare_exchange_strong(expected, desired, success_order, failure_order); + } +private: + internal_atomic(const internal_atomic &); + void operator=(const internal_atomic &); +}; + +template +class internal_atomic : private detail::atomic::platform_atomic_integral { +public: + typedef detail::atomic::platform_atomic_integral super; + typedef typename super::integral_type integral_type; + + internal_atomic() {} + explicit internal_atomic(T v) : super(v) {} + + using super::is_lock_free; + using super::load; + using super::store; + using super::exchange; + using super::fetch_add; + using super::fetch_sub; + using super::fetch_and; + using super::fetch_or; + using super::fetch_xor; + + operator integral_type(void) const volatile {return load();} + integral_type operator=(integral_type v) volatile {store(v); return v;} + + integral_type operator&=(integral_type c) volatile {return fetch_and(c)&c;} + integral_type operator|=(integral_type c) volatile {return fetch_or(c)|c;} + integral_type operator^=(integral_type c) volatile {return fetch_xor(c)^c;} + + integral_type operator+=(integral_type c) volatile {return fetch_add(c)+c;} + integral_type operator-=(integral_type c) volatile {return fetch_sub(c)-c;} + + integral_type operator++(void) volatile {return fetch_add(1)+1;} + integral_type operator++(int) volatile {return fetch_add(1);} + integral_type operator--(void) volatile {return fetch_sub(1)-1;} + integral_type operator--(int) volatile {return fetch_sub(1);} + + bool compare_exchange_strong( + integral_type &expected, + integral_type desired, + memory_order order=memory_order_seq_cst) volatile + { + return super::compare_exchange_strong(expected, desired, order, calculate_failure_order(order)); + } + bool compare_exchange_weak( + integral_type &expected, + integral_type desired, + memory_order order=memory_order_seq_cst) volatile + { + return super::compare_exchange_strong(expected, desired, order, calculate_failure_order(order)); + } + bool compare_exchange_strong( + integral_type &expected, + integral_type desired, + memory_order success_order, + memory_order failure_order) volatile + { + return super::compare_exchange_strong(expected, desired, success_order, failure_order); + } + bool compare_exchange_weak( + integral_type &expected, + integral_type desired, + memory_order success_order, + memory_order failure_order) volatile + { + return super::compare_exchange_strong(expected, desired, success_order, failure_order); + } +private: + internal_atomic(const internal_atomic &); + void operator=(const internal_atomic &); +}; + +} +} +} + +#endif diff --git a/boost/atomic/detail/builder.hpp b/boost/atomic/detail/builder.hpp new file mode 100644 index 00000000..16c68aa9 --- /dev/null +++ b/boost/atomic/detail/builder.hpp @@ -0,0 +1,405 @@ +#ifndef BOOST_DETAIL_ATOMIC_BUILDER_HPP +#define BOOST_DETAIL_ATOMIC_BUILDER_HPP + +// Copyright (c) 2009 Helge Bahmann +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +namespace boost { +namespace detail { +namespace atomic { + +/* +given a Base that implements: + +- load(memory_order order) +- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) + +generates exchange and compare_exchange_strong +*/ +template +class build_exchange : public Base { +public: + typedef typename Base::integral_type integral_type; + + using Base::load; + using Base::compare_exchange_weak; + + bool compare_exchange_strong( + integral_type &expected, + integral_type desired, + memory_order success_order, + memory_order failure_order) volatile + { + integral_type expected_save=expected; + while(true) { + if (compare_exchange_weak(expected, desired, success_order, failure_order)) return true; + if (expected_save!=expected) return false; + expected=expected_save; + } + } + + integral_type exchange(integral_type replacement, memory_order order=memory_order_seq_cst) volatile + { + integral_type o=load(memory_order_relaxed); + do {} while(!compare_exchange_weak(o, replacement, order, memory_order_relaxed)); + return o; + } + + build_exchange() {} + explicit build_exchange(integral_type i) : Base(i) {} +}; + +/* +given a Base that implements: + +- fetch_add_var(integral_type c, memory_order order) +- fetch_inc(memory_order order) +- fetch_dec(memory_order order) + +creates a fetch_add method that delegates to fetch_inc/fetch_dec if operand +is constant +1/-1, and uses fetch_add_var otherwise + +the intention is to allow optimizing the incredibly common case of +1/-1 +*/ +template +class build_const_fetch_add : public Base { +public: + typedef typename Base::integral_type integral_type; + + integral_type fetch_add( + integral_type c, + memory_order order=memory_order_seq_cst) volatile + { + if (__builtin_constant_p(c)) { + switch(c) { + case -1: return fetch_dec(order); + case 1: return fetch_inc(order); + } + } + return fetch_add_var(c, order); + } + + build_const_fetch_add() {} + explicit build_const_fetch_add(integral_type i) : Base(i) {} +protected: + using Base::fetch_add_var; + using Base::fetch_inc; + using Base::fetch_dec; +}; + +/* +given a Base that implements: + +- load(memory_order order) +- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) + +generates a -- not very efficient, but correct -- fetch_add operation +*/ +template +class build_fetch_add : public Base { +public: + typedef typename Base::integral_type integral_type; + + using Base::compare_exchange_weak; + + integral_type fetch_add( + integral_type c, memory_order order=memory_order_seq_cst) volatile + { + integral_type o=Base::load(memory_order_relaxed), n; + do {n=o+c;} while(!compare_exchange_weak(o, n, order, memory_order_relaxed)); + return o; + } + + build_fetch_add() {} + explicit build_fetch_add(integral_type i) : Base(i) {} +}; + +/* +given a Base that implements: + +- fetch_add(integral_type c, memory_order order) + +generates fetch_sub and post/pre- increment/decrement operators +*/ +template +class build_arithmeticops : public Base { +public: + typedef typename Base::integral_type integral_type; + + using Base::fetch_add; + + integral_type fetch_sub( + integral_type c, + memory_order order=memory_order_seq_cst) volatile + { + return fetch_add(-c, order); + } + + build_arithmeticops() {} + explicit build_arithmeticops(integral_type i) : Base(i) {} +}; + +/* +given a Base that implements: + +- load(memory_order order) +- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) + +generates -- not very efficient, but correct -- fetch_and, fetch_or and fetch_xor operators +*/ +template +class build_logicops : public Base { +public: + typedef typename Base::integral_type integral_type; + + using Base::compare_exchange_weak; + using Base::load; + + integral_type fetch_and(integral_type c, memory_order order=memory_order_seq_cst) volatile + { + integral_type o=load(memory_order_relaxed), n; + do {n=o&c;} while(!compare_exchange_weak(o, n, order, memory_order_relaxed)); + return o; + } + integral_type fetch_or(integral_type c, memory_order order=memory_order_seq_cst) volatile + { + integral_type o=load(memory_order_relaxed), n; + do {n=o|c;} while(!compare_exchange_weak(o, n, order, memory_order_relaxed)); + return o; + } + integral_type fetch_xor(integral_type c, memory_order order=memory_order_seq_cst) volatile + { + integral_type o=load(memory_order_relaxed), n; + do {n=o^c;} while(!compare_exchange_weak(o, n, order, memory_order_relaxed)); + return o; + } + + build_logicops() {} + build_logicops(integral_type i) : Base(i) {} +}; + +/* +given a Base that implements: + +- load(memory_order order) +- store(integral_type i, memory_order order) +- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) + +generates the full set of atomic operations for integral types +*/ +template +class build_atomic_from_minimal : public build_logicops< build_arithmeticops< build_fetch_add< build_exchange > > > { +public: + typedef build_logicops< build_arithmeticops< build_fetch_add< build_exchange > > > super; + typedef typename super::integral_type integral_type; + + build_atomic_from_minimal(void) {} + build_atomic_from_minimal(typename super::integral_type i) : super(i) {} +}; + +/* +given a Base that implements: + +- load(memory_order order) +- store(integral_type i, memory_order order) +- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) +- compare_exchange_strong(integral_type &expected, integral_type desired, memory_order order) +- exchange(integral_type replacement, memory_order order) +- fetch_add_var(integral_type c, memory_order order) +- fetch_inc(memory_order order) +- fetch_dec(memory_order order) + +generates the full set of atomic operations for integral types +*/ +template +class build_atomic_from_typical : public build_logicops< build_arithmeticops< build_const_fetch_add > > { +public: + typedef build_logicops< build_arithmeticops< build_const_fetch_add > > super; + typedef typename super::integral_type integral_type; + + build_atomic_from_typical(void) {} + build_atomic_from_typical(typename super::integral_type i) : super(i) {} +}; + +/* +given a Base that implements: + +- load(memory_order order) +- store(integral_type i, memory_order order) +- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) +- compare_exchange_strong(integral_type &expected, integral_type desired, memory_order order) +- exchange(integral_type replacement, memory_order order) +- fetch_add(integral_type c, memory_order order) + +generates the full set of atomic operations for integral types +*/ +template +class build_atomic_from_add : public build_logicops< build_arithmeticops > { +public: + typedef build_logicops< build_arithmeticops > super; + typedef typename super::integral_type integral_type; + + build_atomic_from_add(void) {} + build_atomic_from_add(typename super::integral_type i) : super(i) {} +}; + +/* +given a Base that implements: + +- load(memory_order order) +- store(integral_type i, memory_order order) +- compare_exchange_weak(integral_type &expected, integral_type desired, memory_order order) +- compare_exchange_strong(integral_type &expected, integral_type desired, memory_order order) +- exchange(integral_type replacement, memory_order order) + +generates the full set of atomic operations for integral types +*/ +template +class build_atomic_from_exchange : public build_logicops< build_arithmeticops< build_fetch_add > > { +public: + typedef build_logicops< build_arithmeticops< build_fetch_add > > super; + typedef typename super::integral_type integral_type; + + build_atomic_from_exchange(void) {} + build_atomic_from_exchange(typename super::integral_type i) : super(i) {} +}; + + +/* +given a Base that implements: + +- compare_exchange_weak() + +generates load, store and compare_exchange_weak for a smaller +data type (e.g. an atomic "byte" embedded into a temporary +and properly aligned atomic "int"). +*/ +template +class build_base_from_larger_type { +public: + typedef Type integral_type; + + build_base_from_larger_type() {} + build_base_from_larger_type(integral_type t) {store(t, memory_order_relaxed);} + + integral_type load(memory_order order=memory_order_seq_cst) const volatile + { + larger_integral_type v=get_base().load(order); + return extract(v); + } + bool compare_exchange_weak(integral_type &expected, + integral_type desired, + memory_order success_order, + memory_order failure_order) volatile + { + larger_integral_type expected_; + larger_integral_type desired_; + + expected_=get_base().load(memory_order_relaxed); + expected_=insert(expected_, expected); + desired_=insert(expected_, desired); + bool success=get_base().compare_exchange_weak(expected_, desired_, success_order, failure_order); + expected=extract(expected_); + return success; + } + void store(integral_type v, + memory_order order=memory_order_seq_cst) volatile + { + larger_integral_type expected, desired; + expected=get_base().load(memory_order_relaxed); + do { + desired=insert(expected, v); + } while(!get_base().compare_exchange_weak(expected, desired, order, memory_order_relaxed)); + } + + bool is_lock_free(void) + { + return get_base().is_lock_free(); + } +private: + typedef typename Base::integral_type larger_integral_type; + + const Base &get_base(void) const volatile + { + intptr_t address=(intptr_t)this; + address&=~(sizeof(larger_integral_type)-1); + return *reinterpret_cast(address); + } + Base &get_base(void) volatile + { + intptr_t address=(intptr_t)this; + address&=~(sizeof(larger_integral_type)-1); + return *reinterpret_cast(address); + } + unsigned int get_offset(void) const volatile + { + intptr_t address=(intptr_t)this; + address&=(sizeof(larger_integral_type)-1); + return address; + } + + unsigned int get_shift(void) const volatile + { +#if defined(BOOST_LITTLE_ENDIAN) + return get_offset()*8; +#elif defined(BOOST_BIG_ENDIAN) + return (sizeof(larger_integral_type)-sizeof(integral_type)-get_offset())*8; +#else + #error "Unknown endian" +#endif + } + + integral_type extract(larger_integral_type v) const volatile + { + return v>>get_shift(); + } + + larger_integral_type insert(larger_integral_type target, integral_type source) const volatile + { + larger_integral_type tmp=source; + larger_integral_type mask=(larger_integral_type)-1; + + mask=~(mask<<(8*sizeof(integral_type))); + + mask=mask< +class build_atomic_from_larger_type : public build_atomic_from_minimal< build_base_from_larger_type > { +public: + typedef build_atomic_from_minimal< build_base_from_larger_type > super; + //typedef typename super::integral_type integral_type; + typedef Type integral_type; + + build_atomic_from_larger_type() {} + build_atomic_from_larger_type(integral_type v) : super(v) {} +}; + +} +} +} + +#endif diff --git a/boost/atomic/detail/fallback.hpp b/boost/atomic/detail/fallback.hpp new file mode 100644 index 00000000..71a4dd2c --- /dev/null +++ b/boost/atomic/detail/fallback.hpp @@ -0,0 +1,76 @@ +#ifndef BOOST_DETAIL_ATOMIC_FALLBACK_HPP +#define BOOST_DETAIL_ATOMIC_FALLBACK_HPP + +// Copyright (c) 2009 Helge Bahmann +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +namespace boost { +namespace detail { +namespace atomic { + +template +class fallback_atomic { +public: + fallback_atomic(void) {} + explicit fallback_atomic(const T &t) {memcpy(&i, &t, sizeof(T));} + + void store(const T &t, memory_order order=memory_order_seq_cst) volatile + { + detail::spinlock_pool<0>::scoped_lock guard(const_cast(&i)); + memcpy((void*)&i, &t, sizeof(T)); + } + T load(memory_order order=memory_order_seq_cst) volatile const + { + detail::spinlock_pool<0>::scoped_lock guard(const_cast(&i)); + T tmp; + memcpy(&tmp, (T*)&i, sizeof(T)); + return tmp; + } + bool compare_exchange_strong( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + detail::spinlock_pool<0>::scoped_lock guard(const_cast(&i)); + if (memcmp((void*)&i, &expected, sizeof(T))==0) { + memcpy((void*)&i, &desired, sizeof(T)); + return true; + } else { + memcpy(&expected, (void*)&i, sizeof(T)); + return false; + } + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + return compare_exchange_strong(expected, desired, success_order, failure_order); + } + T exchange(T replacement, memory_order order=memory_order_seq_cst) volatile + { + detail::spinlock_pool<0>::scoped_lock guard(const_cast(&i)); + T tmp; + memcpy(&tmp, (void*)&i, sizeof(T)); + memcpy((void*)&i, &replacement, sizeof(T)); + return tmp; + } + bool is_lock_free(void) const volatile {return false;} +protected: + T i; + typedef T integral_type; +}; + +} +} +} + +#endif diff --git a/boost/atomic/detail/gcc-alpha.hpp b/boost/atomic/detail/gcc-alpha.hpp new file mode 100644 index 00000000..c47b367b --- /dev/null +++ b/boost/atomic/detail/gcc-alpha.hpp @@ -0,0 +1,354 @@ +#ifndef BOOST_DETAIL_ATOMIC_GCC_ALPHA_HPP +#define BOOST_DETAIL_ATOMIC_GCC_ALPHA_HPP + +// Copyright (c) 2009 Helge Bahmann +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +/* + Refer to http://h71000.www7.hp.com/doc/82final/5601/5601pro_004.html + (HP OpenVMS systems documentation) and the alpha reference manual. + */ + +/* + NB: The most natural thing would be to write the increment/decrement + operators along the following lines: + + __asm__ __volatile__( + "1: ldl_l %0,%1 \n" + "addl %0,1,%0 \n" + "stl_c %0,%1 \n" + "beq %0,1b\n" + : "=&b" (tmp) + : "m" (value) + : "cc" + ); + + However according to the comments on the HP website and matching + comments in the Linux kernel sources this defies branch prediction, + as the cpu assumes that backward branches are always taken; so + instead copy the trick from the Linux kernel, introduce a forward + branch and back again. + + I have, however, had a hard time measuring the difference between + the two versions in microbenchmarks -- I am leaving it in nevertheless + as it apparently does not hurt either. +*/ + +namespace boost { +namespace detail { +namespace atomic { + +static inline void fence_before(memory_order order) +{ + switch(order) { + case memory_order_consume: + case memory_order_release: + case memory_order_acq_rel: + case memory_order_seq_cst: + __asm__ __volatile__ ("mb" ::: "memory"); + default:; + } +} + +static inline void fence_after(memory_order order) +{ + switch(order) { + case memory_order_acquire: + case memory_order_acq_rel: + case memory_order_seq_cst: + __asm__ __volatile__ ("mb" ::: "memory"); + default:; + } +} + +template<> +static inline void platform_atomic_thread_fence(memory_order order) +{ + switch(order) { + case memory_order_acquire: + case memory_order_consume: + case memory_order_release: + case memory_order_acq_rel: + case memory_order_seq_cst: + __asm__ __volatile__ ("mb" ::: "memory"); + default:; + } +} + +template +class atomic_alpha_32 { +public: + typedef T integral_type; + explicit atomic_alpha_32(T v) : i(v) {} + atomic_alpha_32() {} + T load(memory_order order=memory_order_seq_cst) const volatile + { + T v=*reinterpret_cast(&i); + fence_after(order); + return v; + } + void store(T v, memory_order order=memory_order_seq_cst) volatile + { + fence_before(order); + *reinterpret_cast(&i)=(int)v; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + fence_before(success_order); + int current, success; + __asm__ __volatile__( + "1: ldl_l %2, %4\n" + "cmpeq %2, %0, %3\n" + "mov %2, %0\n" + "beq %3, 3f\n" + "stl_c %1, %4\n" + "2:\n" + + ".subsection 2\n" + "3: mov %3, %1\n" + "br 2b\n" + ".previous\n" + + : "+&r" (expected), "+&r" (desired), "=&r"(current), "=&r"(success) + : "m" (i) + : + ); + if (desired) fence_after(success_order); + else fence_after(failure_order); + return desired; + } + + bool is_lock_free(void) const volatile {return true;} +protected: + inline T fetch_add_var(T c, memory_order order) volatile + { + fence_before(order); + T original, modified; + __asm__ __volatile__( + "1: ldl_l %0, %2\n" + "addl %0, %3, %1\n" + "stl_c %1, %2\n" + "beq %1, 2f\n" + + ".subsection 2\n" + "2: br 1b\n" + ".previous\n" + + : "=&r" (original), "=&r" (modified) + : "m" (i), "r" (c) + : + ); + fence_after(order); + return original; + } + inline T fetch_inc(memory_order order) volatile + { + fence_before(order); + int original, modified; + __asm__ __volatile__( + "1: ldl_l %0, %2\n" + "addl %0, 1, %1\n" + "stl_c %1, %2\n" + "beq %1, 2f\n" + + ".subsection 2\n" + "2: br 1b\n" + ".previous\n" + + : "=&r" (original), "=&r" (modified) + : "m" (i) + : + ); + fence_after(order); + return original; + } + inline T fetch_dec(memory_order order) volatile + { + fence_before(order); + int original, modified; + __asm__ __volatile__( + "1: ldl_l %0, %2\n" + "subl %0, 1, %1\n" + "stl_c %1, %2\n" + "beq %1, 2f\n" + + ".subsection 2\n" + "2: br 1b\n" + ".previous\n" + + : "=&r" (original), "=&r" (modified) + : "m" (i) + : + ); + fence_after(order); + return original; + } +private: + T i; +}; + +template +class atomic_alpha_64 { +public: + typedef T integral_type; + explicit atomic_alpha_64(T v) : i(v) {} + atomic_alpha_64() {} + T load(memory_order order=memory_order_seq_cst) const volatile + { + T v=*reinterpret_cast(&i); + fence_after(order); + return v; + } + void store(T v, memory_order order=memory_order_seq_cst) volatile + { + fence_before(order); + *reinterpret_cast(&i)=v; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + fence_before(success_order); + int current, success; + __asm__ __volatile__( + "1: ldq_l %2, %4\n" + "cmpeq %2, %0, %3\n" + "mov %2, %0\n" + "beq %3, 3f\n" + "stq_c %1, %4\n" + "2:\n" + + ".subsection 2\n" + "3: mov %3, %1\n" + "br 2b\n" + ".previous\n" + + : "+&r" (expected), "+&r" (desired), "=&r"(current), "=&r"(success) + : "m" (i) + : + ); + if (desired) fence_after(success_order); + else fence_after(failure_order); + return desired; + } + + bool is_lock_free(void) const volatile {return true;} +protected: + inline T fetch_add_var(T c, memory_order order) volatile + { + fence_before(order); + T original, modified; + __asm__ __volatile__( + "1: ldq_l %0, %2\n" + "addq %0, %3, %1\n" + "stq_c %1, %2\n" + "beq %1, 2f\n" + + ".subsection 2\n" + "2: br 1b\n" + ".previous\n" + + : "=&r" (original), "=&r" (modified) + : "m" (i), "r" (c) + : + ); + fence_after(order); + return original; + } + inline T fetch_inc(memory_order order) volatile + { + fence_before(order); + T original, modified; + __asm__ __volatile__( + "1: ldq_l %0, %2\n" + "addq %0, 1, %1\n" + "stq_c %1, %2\n" + "beq %1, 2f\n" + + ".subsection 2\n" + "2: br 1b\n" + ".previous\n" + + : "=&r" (original), "=&r" (modified) + : "m" (i) + : + ); + fence_after(order); + return original; + } + inline T fetch_dec(memory_order order) volatile + { + fence_before(order); + T original, modified; + __asm__ __volatile__( + "1: ldq_l %0, %2\n" + "subq %0, 1, %1\n" + "stq_c %1, %2\n" + "beq %1, 2f\n" + + ".subsection 2\n" + "2: br 1b\n" + ".previous\n" + + : "=&r" (original), "=&r" (modified) + : "m" (i) + : + ); + fence_after(order); + return original; + } +private: + T i; +}; + +template +class platform_atomic_integral : public build_atomic_from_typical > > { +public: + typedef build_atomic_from_typical > > super; + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +template +class platform_atomic_integral : public build_atomic_from_typical > > { +public: + typedef build_atomic_from_typical > > super; + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +template +class platform_atomic_integral: public build_atomic_from_larger_type, T> { +public: + typedef build_atomic_from_larger_type, T> super; + + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +template +class platform_atomic_integral: public build_atomic_from_larger_type, T> { +public: + typedef build_atomic_from_larger_type, T> super; + + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +} +} +} + +#endif diff --git a/boost/atomic/detail/gcc-ppc.hpp b/boost/atomic/detail/gcc-ppc.hpp new file mode 100644 index 00000000..6b1879c8 --- /dev/null +++ b/boost/atomic/detail/gcc-ppc.hpp @@ -0,0 +1,351 @@ +#ifndef BOOST_DETAIL_ATOMIC_GCC_PPC_HPP +#define BOOST_DETAIL_ATOMIC_GCC_PPC_HPP + +// Copyright (c) 2009 Helge Bahmann +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +/* + Refer to: Motorola: "Programming Environments Manual for 32-Bit + Implementations of the PowerPC Architecture", Appendix E: + "Synchronization Programming Examples" for an explanation of what is + going on here (can be found on the web at various places by the + name "MPCFPE32B.pdf", Google is your friend...) + */ + +namespace boost { +namespace detail { +namespace atomic { + +static inline void fence_before(memory_order order) +{ + switch(order) { + case memory_order_release: + case memory_order_acq_rel: +#if defined(__powerpc64__) + __asm__ __volatile__ ("lwsync" ::: "memory"); + break; +#endif + case memory_order_seq_cst: + __asm__ __volatile__ ("sync" ::: "memory"); + default:; + } +} + +/* Note on the barrier instructions used by fence_after and +atomic_thread_fence: the "isync" instruction normally does +not wait for memory-accessing operations to complete, the +"trick" is to introduce a conditional branch that formally +depends on the memory-accessing instruction -- isync waits +until the branch can be resolved and thus implicitly until +the memory access completes. + +This means that the load(memory_order_relaxed) instruction +includes this branch, even though no barrier would be required +here, but as a consequence atomic_thread_fence(memory_order_acquire) +would have to be implemented using "sync" instead of "isync". +The following simple cost-analysis provides the rationale +for this decision: + +- isync: about ~12 cycles +- sync: about ~50 cycles +- "spurious" branch after load: 1-2 cycles +- making the right decision: priceless + +*/ + +static inline void fence_after(memory_order order) +{ + switch(order) { + case memory_order_acquire: + case memory_order_acq_rel: + case memory_order_seq_cst: + __asm__ __volatile__ ("isync"); + case memory_order_consume: + __asm__ __volatile__ ("" ::: "memory"); + default:; + } +} + +template<> +static inline void platform_atomic_thread_fence(memory_order order) +{ + switch(order) { + case memory_order_acquire: + __asm__ __volatile__ ("isync" ::: "memory"); + break; + case memory_order_release: + case memory_order_acq_rel: +#if defined(__powerpc64__) + __asm__ __volatile__ ("lwsync" ::: "memory"); + break; +#endif + case memory_order_seq_cst: + __asm__ __volatile__ ("sync" ::: "memory"); + default:; + } +} + + +/* note: the __asm__ constraint "b" instructs gcc to use any register +except r0; this is required because r0 is not allowed in +some places. Since I am sometimes unsure if it is allowed +or not just play it safe and avoid r0 entirely -- ppc isn't +exactly register-starved, so this really should not matter :) */ + +template +class atomic_ppc_32 { +public: + typedef T integral_type; + explicit atomic_ppc_32(T v) : i(v) {} + atomic_ppc_32() {} + T load(memory_order order=memory_order_seq_cst) const volatile + { + T v=*reinterpret_cast(&i); + __asm__ __volatile__ ( + "cmpw %0, %0\n" + "bne- 1f\n" + "1f:\n" + : "+b"(v)); + fence_after(order); + return v; + } + void store(T v, memory_order order=memory_order_seq_cst) volatile + { + fence_before(order); + *reinterpret_cast(&i)=v; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + fence_before(success_order); + int success; + __asm__ __volatile__( + "lwarx %0,0,%2\n" + "cmpw %0, %3\n" + "bne- 2f\n" + "stwcx. %4,0,%2\n" + "bne- 2f\n" + "addi %1,0,1\n" + "1:" + + ".subsection 2\n" + "2: addi %1,0,0\n" + "b 1b\n" + ".previous\n" + : "=&b" (expected), "=&b" (success) + : "b" (&i), "b" (expected), "b" ((int)desired) + ); + if (success) fence_after(success_order); + else fence_after(failure_order); + return success; + } + + bool is_lock_free(void) const volatile {return true;} +protected: + inline T fetch_add_var(T c, memory_order order) volatile + { + fence_before(order); + T original, tmp; + __asm__ __volatile__( + "1: lwarx %0,0,%2\n" + "add %1,%0,%3\n" + "stwcx. %1,0,%2\n" + "bne- 1b\n" + : "=&b" (original), "=&b" (tmp) + : "b" (&i), "b" (c) + : "cc"); + fence_after(order); + return original; + } + inline T fetch_inc(memory_order order) volatile + { + fence_before(order); + T original, tmp; + __asm__ __volatile__( + "1: lwarx %0,0,%2\n" + "addi %1,%0,1\n" + "stwcx. %1,0,%2\n" + "bne- 1b\n" + : "=&b" (original), "=&b" (tmp) + : "b" (&i) + : "cc"); + fence_after(order); + return original; + } + inline T fetch_dec(memory_order order) volatile + { + fence_before(order); + T original, tmp; + __asm__ __volatile__( + "1: lwarx %0,0,%2\n" + "addi %1,%0,-1\n" + "stwcx. %1,0,%2\n" + "bne- 1b\n" + : "=&b" (original), "=&b" (tmp) + : "b" (&i) + : "cc"); + fence_after(order); + return original; + } +private: + T i; +}; + +#if defined(__powerpc64__) + +#warning Untested code -- please inform me if it works + +template +class atomic_ppc_64 { +public: + typedef T integral_type; + explicit atomic_ppc_64(T v) : i(v) {} + atomic_ppc_64() {} + T load(memory_order order=memory_order_seq_cst) const volatile + { + T v=*reinterpret_cast(&i); + __asm__ __volatile__ ( + "cmpw %0, %0\n" + "bne- 1f\n" + "1f:\n" + : "+b"(v)); + fence_after(order); + return v; + } + void store(T v, memory_order order=memory_order_seq_cst) volatile + { + fence_before(order); + *reinterpret_cast(&i)=v; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + fence_before(success_order); + int success; + __asm__ __volatile__( + "ldarx %0,0,%2\n" + "cmpw %0, %3\n" + "bne- 2f\n" + "stdcx. %4,0,%2\n" + "bne- 2f\n" + "addi %1,0,1\n" + "1:" + + ".subsection 2\n" + "2: addi %1,0,0\n" + "b 1b\n" + ".previous\n" + : "=&b" (expected), "=&b" (success) + : "b" (&i), "b" (expected), "b" ((int)desired) + ); + if (success) fence_after(success_order); + else fence_after(failure_order); + fence_after(order); + return success; + } + + bool is_lock_free(void) const volatile {return true;} +protected: + inline T fetch_add_var(T c, memory_order order) volatile + { + fence_before(order); + T original, tmp; + __asm__ __volatile__( + "1: ldarx %0,0,%2\n" + "add %1,%0,%3\n" + "stdcx. %1,0,%2\n" + "bne- 1b\n" + : "=&b" (original), "=&b" (tmp) + : "b" (&i), "b" (c) + : "cc"); + fence_after(order); + return original; + } + inline T fetch_inc(memory_order order) volatile + { + fence_before(order); + T original, tmp; + __asm__ __volatile__( + "1: ldarx %0,0,%2\n" + "addi %1,%0,1\n" + "stdcx. %1,0,%2\n" + "bne- 1b\n" + : "=&b" (original), "=&b" (tmp) + : "b" (&i) + : "cc"); + fence_after(order); + return original; + } + inline T fetch_dec(memory_order order) volatile + { + fence_before(order); + T original, tmp; + __asm__ __volatile__( + "1: ldarx %0,0,%2\n" + "addi %1,%0,-1\n" + "stdcx. %1,0,%2\n" + "bne- 1b\n" + : "=&b" (original), "=&b" (tmp) + : "b" (&i) + : "cc"); + fence_after(order); + return original; + } +private: + T i; +}; +#endif + +template +class platform_atomic_integral : public build_atomic_from_typical > > { +public: + typedef build_atomic_from_typical > > super; + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +template +class platform_atomic_integral: public build_atomic_from_larger_type, T> { +public: + typedef build_atomic_from_larger_type, T> super; + + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +template +class platform_atomic_integral: public build_atomic_from_larger_type, T> { +public: + typedef build_atomic_from_larger_type, T> super; + + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +#if defined(__powerpc64__) +template +class platform_atomic_integral : public build_atomic_from_typical > > { +public: + typedef build_atomic_from_typical > > super; + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; +#endif + +} +} +} + +#endif diff --git a/boost/atomic/detail/gcc-x86.hpp b/boost/atomic/detail/gcc-x86.hpp new file mode 100644 index 00000000..425a4f50 --- /dev/null +++ b/boost/atomic/detail/gcc-x86.hpp @@ -0,0 +1,432 @@ +#ifndef BOOST_DETAIL_ATOMIC_GCC_X86_HPP +#define BOOST_DETAIL_ATOMIC_GCC_X86_HPP + +// Copyright (c) 2009 Helge Bahmann +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +namespace boost { +namespace detail { +namespace atomic { + +static inline void fence_before(memory_order order) +{ + switch(order) { + case memory_order_consume: + case memory_order_release: + case memory_order_acq_rel: + case memory_order_seq_cst: + __asm__ __volatile__ ("" ::: "memory"); + default:; + } +} + +static inline void fence_after(memory_order order) +{ + switch(order) { + case memory_order_acquire: + case memory_order_acq_rel: + case memory_order_seq_cst: + __asm__ __volatile__ ("" ::: "memory"); + default:; + } +} + +static inline void full_fence(void) +{ +#if defined(__amd64__) + __asm__ __volatile__("mfence" ::: "memory"); +#else + /* could use mfence iff i686, but it does not appear to matter much */ + __asm__ __volatile__("lock addl $0, (%%esp)" ::: "memory"); +#endif +} + +static inline void fence_after_load(memory_order order) +{ + switch(order) { + case memory_order_seq_cst: + full_fence(); + case memory_order_acquire: + case memory_order_acq_rel: + __asm__ __volatile__ ("" ::: "memory"); + default:; + } +} + +static inline void platform_atomic_thread_fence(memory_order order) +{ + switch(order) { + case memory_order_seq_cst: + full_fence(); + case memory_order_acquire: + case memory_order_consume: + case memory_order_acq_rel: + case memory_order_release: + __asm__ __volatile__ ("" ::: "memory"); + default:; + } +} + +template +class atomic_x86_8 { +public: + explicit atomic_x86_8(T v) : i(v) {} + atomic_x86_8() {} + T load(memory_order order=memory_order_seq_cst) const volatile + { + T v=*reinterpret_cast(&i); + fence_after_load(order); + return v; + } + void store(T v, memory_order order=memory_order_seq_cst) volatile + { + if (order!=memory_order_seq_cst) { + fence_before(order); + *reinterpret_cast(&i)=v; + } else { + exchange(v); + } + } + bool compare_exchange_strong( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + fence_before(success_order); + T prev=expected; + __asm__ __volatile__("lock cmpxchgb %1, %2\n" : "=a" (prev) : "q" (desired), "m" (i), "a" (expected) : "memory"); + bool success=(prev==expected); + if (success) fence_after(success_order); + else fence_after(failure_order); + expected=prev; + return success; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + return compare_exchange_strong(expected, desired, success_order, failure_order); + } + T exchange(T r, memory_order order=memory_order_seq_cst) volatile + { + __asm__ __volatile__("xchgb %0, %1\n" : "=r" (r) : "m"(i), "0" (r) : "memory"); + return r; + } + T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile + { + __asm__ __volatile__("lock xaddb %0, %1" : "+r" (c), "+m" (i) :: "memory"); + return c; + } + + bool is_lock_free(void) const volatile {return true;} +protected: + typedef T integral_type; +private: + T i; +}; + +template +class platform_atomic_integral : public build_atomic_from_add > { +public: + typedef build_atomic_from_add > super; + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +template +class atomic_x86_16 { +public: + explicit atomic_x86_16(T v) : i(v) {} + atomic_x86_16() {} + T load(memory_order order=memory_order_seq_cst) const volatile + { + T v=*reinterpret_cast(&i); + fence_after_load(order); + return v; + } + void store(T v, memory_order order=memory_order_seq_cst) volatile + { + if (order!=memory_order_seq_cst) { + fence_before(order); + *reinterpret_cast(&i)=v; + } else { + exchange(v); + } + } + bool compare_exchange_strong( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + fence_before(success_order); + T prev=expected; + __asm__ __volatile__("lock cmpxchgw %1, %2\n" : "=a" (prev) : "q" (desired), "m" (i), "a" (expected) : "memory"); + bool success=(prev==expected); + if (success) fence_after(success_order); + else fence_after(failure_order); + expected=prev; + return success; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + return compare_exchange_strong(expected, desired, success_order, failure_order); + } + T exchange(T r, memory_order order=memory_order_seq_cst) volatile + { + __asm__ __volatile__("xchgw %0, %1\n" : "=r" (r) : "m"(i), "0" (r) : "memory"); + return r; + } + T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile + { + __asm__ __volatile__("lock xaddw %0, %1" : "+r" (c), "+m" (i) :: "memory"); + return c; + } + + bool is_lock_free(void) const volatile {return true;} +protected: + typedef T integral_type; +private: + T i; +}; + +template +class platform_atomic_integral : public build_atomic_from_add > { +public: + typedef build_atomic_from_add > super; + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +template +class atomic_x86_32 { +public: + explicit atomic_x86_32(T v) : i(v) {} + atomic_x86_32() {} + T load(memory_order order=memory_order_seq_cst) const volatile + { + T v=*reinterpret_cast(&i); + fence_after_load(order); + return v; + } + void store(T v, memory_order order=memory_order_seq_cst) volatile + { + if (order!=memory_order_seq_cst) { + fence_before(order); + *reinterpret_cast(&i)=v; + } else { + exchange(v); + } + } + bool compare_exchange_strong( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + fence_before(success_order); + T prev=expected; + __asm__ __volatile__("lock cmpxchgl %1, %2\n" : "=a" (prev) : "q" (desired), "m" (i), "a" (expected) : "memory"); + bool success=(prev==expected); + if (success) fence_after(success_order); + else fence_after(failure_order); + expected=prev; + return success; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + return compare_exchange_strong(expected, desired, success_order, failure_order); + } + T exchange(T r, memory_order order=memory_order_seq_cst) volatile + { + __asm__ __volatile__("xchgl %0, %1\n" : "=r" (r) : "m"(i), "0" (r) : "memory"); + return r; + } + T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile + { + __asm__ __volatile__("lock xaddl %0, %1" : "+r" (c), "+m" (i) :: "memory"); + return c; + } + + bool is_lock_free(void) const volatile {return true;} +protected: + typedef T integral_type; +private: + T i; +}; + +template +class platform_atomic_integral : public build_atomic_from_add > { +public: + typedef build_atomic_from_add > super; + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +#if defined(__amd64__) +template +class atomic_x86_64 { +public: + explicit atomic_x86_64(T v) : i(v) {} + atomic_x86_64() {} + T load(memory_order order=memory_order_seq_cst) const volatile + { + T v=*reinterpret_cast(&i); + fence_after_load(order); + return v; + } + void store(T v, memory_order order=memory_order_seq_cst) volatile + { + if (order!=memory_order_seq_cst) { + fence_before(order); + *reinterpret_cast(&i)=v; + } else { + exchange(v); + } + } + bool compare_exchange_strong( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + fence_before(success_order); + T prev=expected; + __asm__ __volatile__("lock cmpxchgq %1, %2\n" : "=a" (prev) : "q" (desired), "m" (i), "a" (expected) : "memory"); + bool success=(prev==expected); + if (success) fence_after(success_order); + else fence_after(failure_order); + expected=prev; + return success; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + return compare_exchange_strong(expected, desired, success_order, failure_order); + } + T exchange(T r, memory_order order=memory_order_seq_cst) volatile + { + __asm__ __volatile__("xchgq %0, %1\n" : "=r" (r) : "m"(i), "0" (r) : "memory"); + return r; + } + T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile + { + __asm__ __volatile__("lock xaddq %0, %1" : "+r" (c), "+m" (i) :: "memory"); + return c; + } + + bool is_lock_free(void) const volatile {return true;} +protected: + typedef T integral_type; +private: + T i; +} __attribute__((aligned(8))); + +#elif defined(__i686__) + +template +class atomic_x86_64 { +private: + typedef atomic_x86_64 this_type; +public: + explicit atomic_x86_64(T v) : i(v) {} + atomic_x86_64() {} + + bool compare_exchange_strong( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + fence_before(success_order); + T prev=expected; + __asm__ __volatile__("lock cmpxchg8b %3\n" : + "=A" (prev) : "b" ((long)desired), "c" ((long)(desired>>32)), "m" (i), "0" (prev) : "memory"); + bool success=(prev==expected); + if (success) fence_after(success_order); + else fence_after(failure_order); + expected=prev; + return success; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + return compare_exchange_strong(expected, desired, success_order, failure_order); + } + T exchange(T r, memory_order order=memory_order_seq_cst) volatile + { + T prev=i; + do {} while(!compare_exchange_strong(prev, r, order, memory_order_relaxed)); + return prev; + } + + T load(memory_order order=memory_order_seq_cst) const volatile + { + /* this is a bit problematic -- there is no other + way to atomically load a 64 bit value, but of course + compare_exchange requires write access to the memory + area */ + T expected=i; + do { } while(!const_cast(this)->compare_exchange_strong(expected, expected, order, memory_order_relaxed)); + return expected; + } + void store(T v, memory_order order=memory_order_seq_cst) volatile + { + exchange(v, order); + } + T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile + { + T expected=i, desired;; + do { + desired=expected+c; + } while(!compare_exchange_strong(expected, desired, order, memory_order_relaxed)); + return expected; + } + + bool is_lock_free(void) const volatile {return true;} +protected: + typedef T integral_type; +private: + T i; +} __attribute__((aligned(8))) ; + +#endif + +#if defined(__amd64__) || defined(__i686__) +template +class platform_atomic_integral : public build_atomic_from_add >{ +public: + typedef build_atomic_from_add > super; + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; +#endif + +} +} +} + +#endif diff --git a/boost/atomic/detail/generic-cas.hpp b/boost/atomic/detail/generic-cas.hpp new file mode 100644 index 00000000..6ed322b4 --- /dev/null +++ b/boost/atomic/detail/generic-cas.hpp @@ -0,0 +1,192 @@ +#ifndef BOOST_DETAIL_ATOMIC_GENERIC_CAS_HPP +#define BOOST_DETAIL_ATOMIC_GENERIC_CAS_HPP + +// Copyright (c) 2009 Helge Bahmann +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include + +/* fallback implementation for various compilation targets; +this is *not* efficient, particularly because all operations +are fully fenced (full memory barriers before and after +each operation) */ + +#if defined(__GNUC__) + namespace boost { namespace detail { namespace atomic { + static inline int32_t + fenced_compare_exchange_strong_32(volatile int32_t *ptr, int32_t expected, int32_t desired) + { + return __sync_val_compare_and_swap_4(ptr, expected, desired); + } + #define BOOST_ATOMIC_HAVE_CAS32 1 + + #if defined(__amd64__) || defined(__i686__) + static inline int64_t + fenced_compare_exchange_strong_64(int64_t *ptr, int64_t expected, int64_t desired) + { + return __sync_val_compare_and_swap_8(ptr, expected, desired); + } + #define BOOST_ATOMIC_HAVE_CAS64 1 + #endif + }}} + +#elif defined(__ICL) || defined(_MSC_VER) + + #if defined(_MSC_VER) + #include + #include + #endif + + namespace boost { namespace detail { namespace atomic { + static inline int32_t + fenced_compare_exchange_strong(int32_t *ptr, int32_t expected, int32_t desired) + { + return _InterlockedCompareExchange(reinterpret_cast(ptr), desired, expected); + } + #define BOOST_ATOMIC_HAVE_CAS32 1 + #if defined(_WIN64) + static inline int64_t + fenced_compare_exchange_strong(int64_t *ptr, int64_t expected, int64_t desired) + { + return _InterlockedCompareExchange64(ptr, desired, expected); + } + #define BOOST_ATOMIC_HAVE_CAS64 1 + #endif + }}} + +#elif (defined(__ICC) || defined(__ECC)) + namespace boost { namespace detail { namespace atomic { + static inline int32_t + fenced_compare_exchange_strong_32(int32_t *ptr, int32_t expected, int32_t desired) + { + return _InterlockedCompareExchange((void*)ptr, desired, expected); + } + #define BOOST_ATOMIC_HAVE_CAS32 1 + #if defined(__x86_64) + static inline int64_t + fenced_compare_exchange_strong(int64_t *ptr, int64_t expected, int64_t desired) + { + return cas64(ptr, expected, desired); + } + #define BOOST_ATOMIC_HAVE_CAS64 1 + #elif defined(__ECC) //IA-64 version + static inline int64_t + fenced_compare_exchange_strong(int64_t *ptr, int64_t expected, int64_t desired) + { + return _InterlockedCompareExchange64((void*)ptr, desired, expected); + } + #define BOOST_ATOMIC_HAVE_CAS64 1 + #endif + }}} + +#elif (defined(__SUNPRO_CC) && defined(__sparc)) + #include + namespace boost { namespace detail { namespace atomic { + static inline int32_t + fenced_compare_exchange_strong_32(int32_t *ptr, int32_t expected, int32_t desired) + { + return atomic_cas_32((volatile unsigned int*)ptr, expected, desired); + } + #define BOOST_ATOMIC_HAVE_CAS32 1 + + /* FIXME: check for 64 bit mode */ + static inline int64_t + fenced_compare_exchange_strong_64(int64_t *ptr, int64_t expected, int64_t desired) + { + return atomic_cas_64((volatile unsigned long long*)ptr, expected, desired); + } + #define BOOST_ATOMIC_HAVE_CAS64 1 + }}} +#endif + + +namespace boost { namespace detail { namespace atomic { + +#ifdef BOOST_ATOMIC_HAVE_CAS32 +template +class atomic_generic_cas32 { +private: + typedef atomic_generic_cas32 this_type; +public: + explicit atomic_generic_cas32(T v) : i((int32_t)v) {} + atomic_generic_cas32() {} + T load(memory_order order=memory_order_seq_cst) const volatile + { + T expected=(T)i; + do { } while(!const_cast(this)->compare_exchange_weak(expected, expected, order, memory_order_relaxed)); + return expected; + } + void store(T v, memory_order order=memory_order_seq_cst) volatile + { + exchange(v); + } + bool compare_exchange_strong( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + T found; + found=(T)fenced_compare_exchange_strong_32(&i, (int32_t)expected, (int32_t)desired); + bool success=(found==expected); + expected=found; + return success; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + return compare_exchange_strong(expected, desired, success_order, failure_order); + } + T exchange(T r, memory_order order=memory_order_seq_cst) volatile + { + T expected=(T)i; + do { } while(!compare_exchange_weak(expected, r, order, memory_order_relaxed)); + return expected; + } + + bool is_lock_free(void) const volatile {return true;} + typedef T integral_type; +private: + mutable int32_t i; +}; + +template +class platform_atomic_integral : public build_atomic_from_exchange > { +public: + typedef build_atomic_from_exchange > super; + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +template +class platform_atomic_integral: public build_atomic_from_larger_type, T> { +public: + typedef build_atomic_from_larger_type, T> super; + + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +template +class platform_atomic_integral: public build_atomic_from_larger_type, T> { +public: + typedef build_atomic_from_larger_type, T> super; + + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; +#endif + +} } } + +#endif diff --git a/boost/atomic/detail/integral-casts.hpp b/boost/atomic/detail/integral-casts.hpp new file mode 100644 index 00000000..d90ce59a --- /dev/null +++ b/boost/atomic/detail/integral-casts.hpp @@ -0,0 +1,296 @@ +#ifndef BOOST_DETAIL_ATOMIC_INTEGRAL_CASTS_HPP +#define BOOST_DETAIL_ATOMIC_INTEGRAL_CASTS_HPP + +// Copyright (c) 2009 Helge Bahmann +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +namespace boost { namespace detail { namespace atomic { + +template +class platform_atomic : private platform_atomic_integral { +public: + typedef platform_atomic_integral super; + typedef union { T e; uint8_t i;} conv; + + platform_atomic() {} + explicit platform_atomic(T t) : super(to_integral(t)) + { + } + + void store(T t, memory_order order=memory_order_seq_cst) volatile + { + super::store(to_integral(t), order); + } + T load(memory_order order=memory_order_seq_cst) volatile const + { + return from_integral(super::load(order)); + } + bool compare_exchange_strong( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + uint8_t _expected, _desired; + _expected=to_integral(expected); + _desired=to_integral(desired); + bool success=super::compare_exchange_strong(_expected, _desired, success_order, failure_order); + expected=from_integral(_expected); + return success; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + uint8_t _expected, _desired; + _expected=to_integral(expected); + _desired=to_integral(desired); + bool success=super::compare_exchange_weak(_expected, _desired, success_order, failure_order); + expected=from_integral(_expected); + return success; + } + + T exchange(T replacement, memory_order order=memory_order_seq_cst) volatile + { + return from_integral(super::exchange(to_integral(replacement), order)); + } + + operator T(void) const volatile {return load();} + T operator=(T v) volatile {store(v); return v;} + + using super::is_lock_free; +protected: + static inline uint8_t to_integral(T &t) + { + uint8_t tmp; + memcpy(&tmp, &t, sizeof(t)); + return tmp; + } + static inline T from_integral(uint8_t t) + { + T tmp; + memcpy(&tmp, &t, sizeof(t)); + return tmp; + } +}; + +template +class platform_atomic : private platform_atomic_integral { +public: + typedef platform_atomic_integral super; + typedef union { T e; uint16_t i;} conv; + + platform_atomic() {} + explicit platform_atomic(T t) : super(to_integral(t)) + { + } + + void store(T t, memory_order order=memory_order_seq_cst) volatile + { + super::store(to_integral(t), order); + } + T load(memory_order order=memory_order_seq_cst) volatile const + { + return from_integral(super::load(order)); + } + bool compare_exchange_strong( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + uint16_t _expected, _desired; + _expected=to_integral(expected); + _desired=to_integral(desired); + bool success=super::compare_exchange_strong(_expected, _desired, success_order, failure_order); + expected=from_integral(_expected); + return success; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + uint16_t _expected, _desired; + _expected=to_integral(expected); + _desired=to_integral(desired); + bool success=super::compare_exchange_weak(_expected, _desired, success_order, failure_order); + expected=from_integral(_expected); + return success; + } + + T exchange(T replacement, memory_order order=memory_order_seq_cst) volatile + { + return from_integral(super::exchange(to_integral(replacement), order)); + } + + operator T(void) const volatile {return load();} + T operator=(T v) volatile {store(v); return v;} + + using super::is_lock_free; +protected: + static inline uint16_t to_integral(T &t) + { + uint16_t tmp; + memcpy(&tmp, &t, sizeof(t)); + return tmp; + } + static inline T from_integral(uint16_t t) + { + T tmp; + memcpy(&tmp, &t, sizeof(t)); + return tmp; + } +}; + +template +class platform_atomic : private platform_atomic_integral { +public: + typedef platform_atomic_integral super; + typedef union { T e; uint32_t i;} conv; + + platform_atomic() {} + explicit platform_atomic(T t) : super(to_integral(t)) + { + } + + void store(T t, memory_order order=memory_order_seq_cst) volatile + { + super::store(to_integral(t), order); + } + T load(memory_order order=memory_order_seq_cst) volatile const + { + return from_integral(super::load(order)); + } + bool compare_exchange_strong( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + uint32_t _expected, _desired; + _expected=to_integral(expected); + _desired=to_integral(desired); + bool success=super::compare_exchange_strong(_expected, _desired, success_order, failure_order); + expected=from_integral(_expected); + return success; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + uint32_t _expected, _desired; + _expected=to_integral(expected); + _desired=to_integral(desired); + bool success=super::compare_exchange_weak(_expected, _desired, success_order, failure_order); + expected=from_integral(_expected); + return success; + } + + T exchange(T replacement, memory_order order=memory_order_seq_cst) volatile + { + return from_integral(super::exchange(to_integral(replacement), order)); + } + + operator T(void) const volatile {return load();} + T operator=(T v) volatile {store(v); return v;} + + using super::is_lock_free; +protected: + static inline uint32_t to_integral(T &t) + { + uint32_t tmp; + memcpy(&tmp, &t, sizeof(t)); + return tmp; + } + static inline T from_integral(uint32_t t) + { + T tmp; + memcpy(&tmp, &t, sizeof(t)); + return tmp; + } +}; + +template +class platform_atomic : private platform_atomic_integral { +public: + typedef platform_atomic_integral super; + typedef union { T e; uint64_t i;} conv; + + platform_atomic() {} + explicit platform_atomic(T t) : super(to_integral(t)) + { + } + + void store(T t, memory_order order=memory_order_seq_cst) volatile + { + super::store(to_integral(t), order); + } + T load(memory_order order=memory_order_seq_cst) volatile const + { + return from_integral(super::load(order)); + } + bool compare_exchange_strong( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + uint64_t _expected, _desired; + _expected=to_integral(expected); + _desired=to_integral(desired); + bool success=super::compare_exchange_strong(_expected, _desired, success_order, failure_order); + expected=from_integral(_expected); + return success; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + uint64_t _expected, _desired; + _expected=to_integral(expected); + _desired=to_integral(desired); + bool success=super::compare_exchange_weak(_expected, _desired, success_order, failure_order); + expected=from_integral(_expected); + return success; + } + + T exchange(T replacement, memory_order order=memory_order_seq_cst) volatile + { + return from_integral(super::exchange(to_integral(replacement), order)); + } + + operator T(void) const volatile {return load();} + T operator=(T v) volatile {store(v); return v;} + + using super::is_lock_free; +protected: + static inline uint64_t to_integral(T &t) + { + uint64_t tmp; + memcpy(&tmp, &t, sizeof(t)); + return tmp; + } + static inline T from_integral(uint64_t t) + { + T tmp; + memcpy(&tmp, &t, sizeof(t)); + return tmp; + } +}; + +} } } + +#endif diff --git a/boost/atomic/detail/interlocked.hpp b/boost/atomic/detail/interlocked.hpp new file mode 100644 index 00000000..91865108 --- /dev/null +++ b/boost/atomic/detail/interlocked.hpp @@ -0,0 +1,131 @@ +#ifndef BOOST_DETAIL_ATOMIC_INTERLOCKED_HPP +#define BOOST_DETAIL_ATOMIC_INTERLOCKED_HPP + +// Copyright (c) 2009 Helge Bahmann +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include + +namespace boost { +namespace detail { +namespace atomic { + +static inline void full_fence(void) +{ + long tmp; + BOOST_INTERLOCKED_EXCHANGE(&tmp, 0); +} + +template<> +static inline void platform_atomic_thread_fence(memory_order order) +{ + switch(order) { + case memory_order_seq_cst: + full_fence(); + default:; + } +} + +static inline void fence_after_load(memory_order order) +{ + switch(order) { + case memory_order_seq_cst: + full_fence(); + case memory_order_acquire: + case memory_order_acq_rel: + default:; + } +} + + +template +class atomic_interlocked_32 { +public: + explicit atomic_interlocked_32(T v) : i(v) {} + atomic_interlocked_32() {} + T load(memory_order order=memory_order_seq_cst) const volatile + { + T v=*reinterpret_cast(&i); + fence_after_load(order); + return v; + } + void store(T v, memory_order order=memory_order_seq_cst) volatile + { + if (order!=memory_order_seq_cst) { + *reinterpret_cast(&i)=v; + } else { + exchange(v); + } + } + bool compare_exchange_strong( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + T prev=expected; + expected=(T)BOOST_INTERLOCKED_COMPARE_EXCHANGE((long *)(&i), (long)desired, (long)expected); + bool success=(prev==expected); + return success; + } + bool compare_exchange_weak( + T &expected, + T desired, + memory_order success_order, + memory_order failure_order) volatile + { + return compare_exchange_strong(expected, desired, success_order, failure_order); + } + T exchange(T r, memory_order order=memory_order_seq_cst) volatile + { + return (T)BOOST_INTERLOCKED_EXCHANGE((long *)&i, (long)r); + } + T fetch_add(T c, memory_order order=memory_order_seq_cst) volatile + { + return (T)BOOST_INTERLOCKED_EXCHANGE_ADD((long *)&i, c); + } + + bool is_lock_free(void) const volatile {return true;} + + typedef T integral_type; +private: + T i; +}; + +template +class platform_atomic_integral : public build_atomic_from_add > { +public: + typedef build_atomic_from_add > super; + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +template +class platform_atomic_integral: public build_atomic_from_larger_type, T> { +public: + typedef build_atomic_from_larger_type, T> super; + + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +template +class platform_atomic_integral: public build_atomic_from_larger_type, T> { +public: + typedef build_atomic_from_larger_type, T> super; + + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} +}; + +} +} +} + +#endif diff --git a/boost/atomic/detail/valid_integral_types.hpp b/boost/atomic/detail/valid_integral_types.hpp new file mode 100644 index 00000000..7b0efb1d --- /dev/null +++ b/boost/atomic/detail/valid_integral_types.hpp @@ -0,0 +1,37 @@ +#ifndef BOOST_DETAIL_ATOMIC_VALID_INTEGRAL_TYPES_HPP +#define BOOST_DETAIL_ATOMIC_VALID_INTEGRAL_TYPES_HPP + +// Copyright (c) 2009 Helge Bahmann +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +namespace boost { +namespace detail { +namespace atomic { + +template struct is_integral_type {typedef void test;}; + +template<> struct is_integral_type {typedef int test;}; + +template<> struct is_integral_type {typedef int test;}; +template<> struct is_integral_type {typedef int test;}; +template<> struct is_integral_type {typedef int test;}; +template<> struct is_integral_type {typedef int test;}; +template<> struct is_integral_type {typedef int test;}; +template<> struct is_integral_type {typedef int test;}; +template<> struct is_integral_type {typedef int test;}; +template<> struct is_integral_type {typedef int test;}; +#ifdef BOOST_HAS_LONG_LONG +template<> struct is_integral_type {typedef int test;}; +template<> struct is_integral_type {typedef int test;}; +#endif + +} +} +} + +#endif diff --git a/boost/atomic/platform.hpp b/boost/atomic/platform.hpp new file mode 100644 index 00000000..d6bfdd39 --- /dev/null +++ b/boost/atomic/platform.hpp @@ -0,0 +1,29 @@ +// Copyright (c) 2009 Helge Bahmann +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__)) + + #include + +#elif defined(__GNUC__) && defined(__alpha__) + + #include + +#elif defined(__GNUC__) && (defined(__POWERPC__) || defined(__PPC__)) + + #include + +#elif defined(BOOST_USE_WINDOWS_H) || defined(_WIN32_CE) || defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) + + #include + +#else + + #include + +#endif diff --git a/boost/extension/adaptable_factory.hpp b/boost/extension/adaptable_factory.hpp new file mode 100644 index 00000000..9ca45911 --- /dev/null +++ b/boost/extension/adaptable_factory.hpp @@ -0,0 +1,156 @@ +/* + * Boost.Extension / adaptable_factory: + * factory to register the implementations and create them, + * without binding the parameter types into the factory type. + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_ADAPTABLE_FACTORY_HPP +#define BOOST_EXTENSION_ADAPTABLE_FACTORY_HPP + +#include +#include + +#include +#include +#include +#include +#include + +namespace boost { +namespace extensions { +namespace impl { +#ifndef BOOST_EXTENSION_DOXYGEN_INVOKED +# define BOOST_PP_ITERATION_LIMITS \ + (0, BOOST_PP_INC(BOOST_EXTENSION_MAX_FUNCTOR_PARAMS) - 1) +# define BOOST_PP_FILENAME_1 +# include BOOST_PP_ITERATE() +#endif // BOOST_EXTENSION_DOXYGEN_INVOKED +} // namespace impl + +/** This class is a function object that returns + * new instances of type Interface, using factories that + * take parameters described in the variable length + * list Params... + */ +template +class adaptable_factory { +public: + /** \brief Default constructor. + * On creation, this adaptable_factory is empty. + */ + adaptable_factory() : functor_func_(0), func_(0), check_func_(0) {} + + /** \brief Standard copy constructor. + */ + adaptable_factory(adaptable_factory const& first) : func_(first.func_) {} + + /** \brief Standard assignment operator. + */ + adaptable_factory& operator=(adaptable_factory const& first) { + this->func_ = first->func_; + return *this; + } + + /** Returns an instance of Interface (but does NOT retain ownership of the instance). + * \param map A parameter map to search for the parameters for this function. + * \return An instance of Interface, if all of the needed parameters are found in map. + * Otherwise, it returns NULL. + * \pre is_valid() == true. + * \post None. + */ + Interface* create(boost::reflections::parameter_map& map) const { + return (*func_)(map, parameter_names_); + } + + /** Generate a `boost::function` object that can be reused to create + * instances of `Interface` based on the data in the _parameter_map_. + * If the _parameter_map_ does not contain all of the needed parameters, + * then calling `empty()` on the returned function object will return + * true. + * \return An instance of Interface, if all of the needed parameters are found in map. + * Otherwise, it returns NULL. + * \pre is_valid() == true. + * \post None. + */ + function get_function( + boost::reflections::parameter_map& map) const { + return (*functor_func_)(map, parameter_names_); + } + + /** Returns a map of the TypeInfo/Info pairs describing any parameters still + * needed before this function can be called. + * \param map A parameter map to search for the parameters for this function. + * \return TypeInfo/Info pairs for any missing parameters. + * \pre is_valid() == true. + * \post None. + */ + std::vector > get_missing_params( + const boost::reflections::parameter_map& map) const { + return (*check_func_)(map, parameter_names_); + } + + /** \brief Returns true if set has been called. + * + * Until set is called, a adaptable_factory cannot be used. This + * function can be used to determine if set has been called. + * \pre None. + * \post None. + * \return True if the adaptable_factory is initialized (ie, set has been called). + */ + bool is_valid() const { return this->func_ != 0; } + +/* For Doxygen, and for easier readability by users, a + * simplified version of this class's set function is provided, but never + * compiled. + */ +#ifdef BOOST_EXTENSION_DOXYGEN_INVOKED + + /** \brief Set the factory function for this adaptable_factory. + * + * This sets the factory function + * to the constructor for type D. + * It takes as arguments Info about each parameter + * in the constructor. + * Example: + * \code + * adaptable_factory f; + * f.set(); + * \endcode + * \param parameter_names A variable length list of Info + * to describe the parameters of the constructor. + * \pre None. + * \post None. + */ + template + void set(Info parameter_names...) {} +#else + +# define BOOST_PP_ITERATION_LIMITS \ + (0, BOOST_PP_INC(BOOST_EXTENSION_MAX_FUNCTOR_PARAMS) - 1) +# define BOOST_PP_FILENAME_1 +# include BOOST_PP_ITERATE() +private: + function (*functor_func_)( + boost::reflections::basic_parameter_map& map, + const std::vector& names); + Interface* (*func_)( + boost::reflections::basic_parameter_map& map, + const std::vector& names); + std::vector > (*check_func_)( + const boost::reflections::basic_parameter_map& map, + const std::vector& names); + std::vector parameter_names_; +#endif // BOOST_EXTENSION_DOXYGEN_INVOKED +}; +} // namespace extensions +} // namespace boost + +#endif // BOOST_EXTENSION_FACTORY_HPP diff --git a/boost/extension/common.hpp b/boost/extension/common.hpp new file mode 100644 index 00000000..3b9fc5f1 --- /dev/null +++ b/boost/extension/common.hpp @@ -0,0 +1,30 @@ +/* + * Boost.Extension / common: + * common include files + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_COMMON_HPP +#define BOOST_EXTENSION_COMMON_HPP + +#include +#include +#include +#include +#include +#include +/** This determines the maximum number of parameters that a constructor + * or exported shared library function can have. 10 is the same default + * as Boost.Function. + */ +#ifndef BOOST_EXTENSION_MAX_FUNCTOR_PARAMS +#define BOOST_EXTENSION_MAX_FUNCTOR_PARAMS 10 +#endif + +#endif // BOOST_EXTENSION_COMMON_HPP diff --git a/boost/extension/convenience.hpp b/boost/extension/convenience.hpp new file mode 100644 index 00000000..a8ace12d --- /dev/null +++ b/boost/extension/convenience.hpp @@ -0,0 +1,76 @@ +/* + * Boost.Extension / convenience functions: + * for now only one to load a library and register it in the factory + * map. + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_CONVENIENCE_HPP +#define BOOST_EXTENSION_CONVENIENCE_HPP + +#include +#include + +namespace boost { +namespace extensions { +/** \brief Load factories from the given library and function. + * + * Add any exported factories from the given library with the + * given function name. This uses shared_library::get internally. + * The function must have the signature void (factory_map&). + * For more general loading of shared libraries, use the shared_library + * class directly. + * If the function is not found, false is returned. + * + * \param current_factory_map The factory map to load classes into. It is + * not required to be empty. + * \param library_path The relative or absolute path to the library to load. + * \param external_function_name The name of an exported function in the library + * with the signature void (factory_map&). + * \return True on success. + * \pre None. + * \post None. + */ +inline bool load_single_library(factory_map& current_factory_map, + const std::string& library_path, + const std::string& external_function_name) { + shared_library lib(library_path); + if (!lib.open()) { + return false; + } + void (*func)(factory_map&) = + lib.shared_library::get(external_function_name); + if (!func) { + return false; + } + (*func)(current_factory_map); + return true; +} + +inline bool load_single_library(type_map& current_type_map, + const std::string& library_path) { + shared_library lib(library_path); + if (!lib.open()) { + return false; + } + void (*func)(type_map&) = + lib.shared_library::get + ("boost_extension_exported_type_map_function"); + if (!func) { + return false; + } + (*func)(current_type_map); + return true; +} +} // namespace extensions +} // namespace boost + + + +#endif diff --git a/boost/extension/extension.hpp b/boost/extension/extension.hpp new file mode 100644 index 00000000..e77dbd41 --- /dev/null +++ b/boost/extension/extension.hpp @@ -0,0 +1,31 @@ +/* + * Boost.Extension / main header: + * main header for extensions + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_EXTENSION_HPP +#define BOOST_EXTENSION_EXTENSION_HPP +#ifdef BOOST_EXTENSION_DOXYGEN_INVOKED +/** Macro to place in a function definition to cause it + * to be exported, if necessary on the given platform and + * with the current compiler settings. This is always required + * for MSVC and other compilers, but only required depending on + * compiler settings for GCC and other compilers. + */ +#define BOOST_EXTENSION_EXPORT_DECL +#else +#include +#define BOOST_EXTENSION_TYPE_MAP_FUNCTION \ +extern "C" \ +void BOOST_EXTENSION_EXPORT_DECL \ +boost_extension_exported_type_map_function \ + (boost::extensions::type_map& types) +#endif // BOOST_EXTENSION_EXPORT_DECL +#endif // BOOST_EXTENSION_EXTENSION_HPP diff --git a/boost/extension/factory.hpp b/boost/extension/factory.hpp new file mode 100644 index 00000000..e76ba2f1 --- /dev/null +++ b/boost/extension/factory.hpp @@ -0,0 +1,114 @@ +/* + * Boost.Extension / factory: + * factory to register the implementations and create them + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_FACTORY_HPP +#define BOOST_EXTENSION_FACTORY_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace extensions { +/* For Doxygen, and for easier readability by users, a + * simplified version of this class is provided, but never + * compiled. The actual class definition is in impl/factory.hpp. + */ +#ifdef BOOST_EXTENSION_DOXYGEN_INVOKED +/** This class is a function object that returns + * new instances of type T, using factories that + * take parameters described in the variable length + * list Params... + */ +template +class factory { +public: + /** \brief Set the factory function for this factory. + * + * This sets the factory function + * to the constructor for type D. + * Example: factory f; f.set(); + */ + template + void set() { + this->func = &impl::create_function< + T, D BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_PARAMS(N,Param) + >::create; + } + + /** \brief Default constructor. + * On creation, this factory is empty. + */ + factory() : func(0) {} + + /** \brief Standard copy constructor. + */ + factory(factory const& first) : func(first.func) {} + + /** \brief Standard assignment operator. + */ + factory& operator=(factory const& first) { + this->func = first->func; + return *this; + } + + /** \brief Returns true if set has been called. + * + * Until set is called, a factory cannot be used. This + * function can be used to determine if set has been called. + * \pre None. + * \post None. + * \return True if the factory is initialized (ie, set has been called). + */ + bool is_valid() const { return this->func != 0; } + + /** Returns an instance of T (but does NOT retain ownership of the instance). + * \param Params... The parameters described in the type of this factory. + * \return An instance of T. + * \pre is_valid() == true. + * \post None. + */ + T* create(Params...) const { + if (this->func) { + return this->func(BOOST_PP_ENUM_PARAMS(N, p)); + } + else { + return 0; + } + } +}; + +#else + +#define N BOOST_EXTENSION_MAX_FUNCTOR_PARAMS + +template +class factory; + +#undef N + +// generate specializations of factory +# define BOOST_PP_ITERATION_LIMITS \ + (0, BOOST_PP_INC(BOOST_EXTENSION_MAX_FUNCTOR_PARAMS) - 1) +# define BOOST_PP_FILENAME_1 +# include BOOST_PP_ITERATE() +#endif +} // namespace extensions +} // namespace boost + +#endif // BOOST_EXTENSION_FACTORY_HPP diff --git a/boost/extension/factory_map.hpp b/boost/extension/factory_map.hpp new file mode 100644 index 00000000..2aae4d75 --- /dev/null +++ b/boost/extension/factory_map.hpp @@ -0,0 +1,103 @@ +/* + * Boost.Extension / factory map: + * map of factories (for the implementations) + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_FACTORY_MAP_HPP +#define BOOST_EXTENSION_FACTORY_MAP_HPP + +#include +#include +#include + +namespace boost { +namespace extensions { + +/** \brief A collection of factories of various types. + * \tparam TypeInfo The type used for TypeInfo. By default, + * RTTI is used, but users can define their own TypeInfo. + * See impl/typeinfo.hpp. + */ +template +class basic_factory_map { +public: + ~basic_factory_map() { + for (typename std::map::iterator + it =maps_.begin(); it != maps_.end(); ++it) { + delete it->second; + } + } + +/* Include simplified versions of the get and conversion member + * functions for Doxygen, and to make it easier for readers of + * this file. + */ +#ifdef BOOST_EXTENSION_DOXYGEN_INVOKED + /** \brief Return a map of the factories that match the given interface. + * \tparam Interface The type of the interface returned by factories in + * the requested map. + * \tparam Info An arbitrary type that is stored with each factory, + * to differentiate them. By default, strings are used. + * \tparam Params The constructor params for the requested factories. + * \return A map of the requested factory type. + * + * This returns a map of the given type of factories. It can return + * an empty map if no such factories are found. + */ + template + std::map >& get() { + // EMPTY - THIS IS ONLY HERE FOR DOXYGEN. + } + + /** \brief A conversion operator that calls get(). + * + * A conversion operator for convenience in calling functions + * that take a map of factories. + * This is identical to the get() function. + */ + template + operator + std::map< + Info, + factory + >& + () { + // EMPTY - THIS IS ONLY HERE FOR DOXYGEN. + } + +#else + // generate get and conversion template member functions from the + // specification in impl/ +# define BOOST_PP_ITERATION_LIMITS (0, \ + BOOST_PP_INC(BOOST_EXTENSION_MAX_FUNCTOR_PARAMS) - 1) +# define BOOST_PP_FILENAME_1 +# include BOOST_PP_ITERATE() + +private: + + struct generic_map_holder { + virtual ~generic_map_holder() {} + }; + + template + struct map_holder : generic_map_holder, T {}; + + std::map maps_; +#endif +}; +/** A typedef for convenience - provides the most common + * type of basic_factory_map. + */ +typedef basic_factory_map factory_map; + +} // namespace extensions +} // namespace boost + +#endif // BOOST_EXTENSION_FACTORY_MAP_HPP diff --git a/boost/extension/filesystem.hpp b/boost/extension/filesystem.hpp new file mode 100644 index 00000000..1abc6b19 --- /dev/null +++ b/boost/extension/filesystem.hpp @@ -0,0 +1,55 @@ +/* + * Boost.Extension / filesystem functions: + * functions to navigate folders/directories and get the libraries + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_FILESYSTEM_HPP +#define BOOST_EXTENSION_FILESYSTEM_HPP +// These functions require the boost.filesystem library +#include +#include +#include +#include +#include + +namespace boost {namespace extensions { + +inline void load_all_libraries(factory_map & current_zone, + const char * directory, + const char * external_function_name, + int max_depth = 0) +{ + if (max_depth < 0) return; // Recursion base case + filesystem::directory_iterator end_file_iter; + boost::filesystem::path dir_path(directory); + filesystem::directory_iterator file_iter(dir_path); + for( ;file_iter!=end_file_iter; ++file_iter) + { + if (is_directory(*file_iter)) + { + load_all_libraries(current_zone, directory, file_iter->string().c_str(), + max_depth - 1); + } + else if (is_library(filesystem::extension(*file_iter).c_str())) + { + load_single_library(current_zone, file_iter->string().c_str(), + external_function_name); + } + } +} + + + + +}} + + + +#endif diff --git a/boost/extension/impl/adaptable_factory.hpp b/boost/extension/impl/adaptable_factory.hpp new file mode 100644 index 00000000..e0af16ee --- /dev/null +++ b/boost/extension/impl/adaptable_factory.hpp @@ -0,0 +1,72 @@ +/* + * Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +// No header guard - this file is intended to be included multiple times. + +# define N BOOST_PP_ITERATION() + +public: +template + +private: +template +static Interface* create_func() { + + +} + +template +static void check_func() { + +} + + +template +class factory +{ +public: + + template + void set() { + this->func = &impl::create_function< + T, D BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_PARAMS(N,Param) + >::create; + } + + factory() : func(0) {} + + factory(factory const& first) : func(first.func) {} + + factory& operator=(factory const& first) { + this->func = first->func; + return *this; + } + + bool is_valid() const { return this->func != 0; } + + T* create(BOOST_PP_ENUM_BINARY_PARAMS(N, Param, p)) const { + if (this->func) { + return this->func(BOOST_PP_ENUM_PARAMS(N, p)); + } + else { + return 0; + } + } + +private: + typedef T* (*func_ptr_type)(BOOST_PP_ENUM_PARAMS(N, Param)); + func_ptr_type func; +}; + +#undef N + diff --git a/boost/extension/impl/adaptable_factory_free_functions.hpp b/boost/extension/impl/adaptable_factory_free_functions.hpp new file mode 100644 index 00000000..bd5176b1 --- /dev/null +++ b/boost/extension/impl/adaptable_factory_free_functions.hpp @@ -0,0 +1,71 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +// No header guard - this file is intended to be included multiple times. + +#define N BOOST_PP_ITERATION() +template +inline Interface* create_func( + boost::reflections::basic_parameter_map& map, + const std::vector& names) { +#if N + reflections::generic_parameter* gen; +#define BOOST_EXTENSION_GET_FROM_LIST(z, n, data) \ + gen = map.template get_first(names[n]); \ + if (!gen) return 0; \ + BOOST_PP_CAT(Param, n) BOOST_PP_CAT(p, n) = \ + gen->template cast(); + BOOST_PP_REPEAT(N, BOOST_EXTENSION_GET_FROM_LIST, ) +#undef BOOST_EXTENSION_GET_FROM_LIST +#endif // N + return new Derived(BOOST_PP_ENUM_PARAMS(N, p)); +} + +template +inline boost::function get_functor_func( + boost::reflections::basic_parameter_map& map, + const std::vector& names) { +#if N + reflections::generic_parameter* gen; +#define BOOST_EXTENSION_GET_FROM_LIST(z, n, data) \ + gen = map.template get_first(names[n]); \ + if (!gen) return boost::function(); \ + BOOST_PP_CAT(Param, n) BOOST_PP_CAT(p, n) = \ + gen->template cast(); + BOOST_PP_REPEAT(N, BOOST_EXTENSION_GET_FROM_LIST, ) +#undef BOOST_EXTENSION_GET_FROM_LIST +#endif // N + Interface* (*f)(BOOST_PP_ENUM_PARAMS(N, Param)) = + impl::create_derived; + return bind(f + BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, p)); +} + +template +inline std::vector > check_func( + const boost::reflections::basic_parameter_map& map, + const std::vector& names) { + std::vector > needed_parameters; +#define BOOST_EXTENSION_CHECK_IN_LIST(z, n, data) \ +if (!map.template has(names[n])) \ + needed_parameters.push_back(std::make_pair(\ + type_info_handler::get_class_type(), \ + names[n])); + BOOST_PP_REPEAT(N, BOOST_EXTENSION_CHECK_IN_LIST, ) +#undef BOOST_EXTENSION_CHECK_IN_LIST + return needed_parameters; +} +#undef N + diff --git a/boost/extension/impl/adaptable_factory_set.hpp b/boost/extension/impl/adaptable_factory_set.hpp new file mode 100644 index 00000000..4941f55e --- /dev/null +++ b/boost/extension/impl/adaptable_factory_set.hpp @@ -0,0 +1,34 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +// No header guard - this file is intended to be included multiple times. + +#define N BOOST_PP_ITERATION() +template +void set(BOOST_PP_ENUM_PARAMS(N, Info i)) { + parameter_names_.resize(N); +#define BOOST_EXTENSION_ADD_TO_LIST(z, n, data) \ + parameter_names_[n] = BOOST_PP_CAT(i, n); + BOOST_PP_REPEAT(N, BOOST_EXTENSION_ADD_TO_LIST, ); +#undef BOOST_EXTENSION_ADD_TO_LIST + func_ = &impl::create_func + ; + functor_func_ = &impl::get_functor_func + ; + check_func_ = &impl::check_func + ; +} +#undef N + diff --git a/boost/extension/impl/create.hpp b/boost/extension/impl/create.hpp new file mode 100644 index 00000000..35d35f0b --- /dev/null +++ b/boost/extension/impl/create.hpp @@ -0,0 +1,36 @@ +/* + * Boost.Extension / factory: + * factory to register the implementations and create them + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_IMPL_CREATE_HPP +#define BOOST_EXTENSION_IMPL_CREATE_HPP +namespace boost { +namespace extensions { +namespace impl { + +#define N BOOST_EXTENSION_MAX_FUNCTOR_PARAMS +template +struct create_function; +#undef N + +// generate specializations of create_func +# define BOOST_PP_ITERATION_LIMITS \ + (0, BOOST_PP_INC(BOOST_EXTENSION_MAX_FUNCTOR_PARAMS) - 1) +# define BOOST_PP_FILENAME_1 +# include BOOST_PP_ITERATE() +} // namespace impl +} // namespace extensions +} // namespace boost + +#endif // BOOST_EXTENSION_IMPL_CREATE_HPP diff --git a/boost/extension/impl/create_func.hpp b/boost/extension/impl/create_func.hpp new file mode 100644 index 00000000..af863122 --- /dev/null +++ b/boost/extension/impl/create_func.hpp @@ -0,0 +1,38 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +// No header guard - this file is intended to be included multiple times. + +# define N BOOST_PP_ITERATION() +template < + class T, + class D + BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_PARAMS(N, class Param) +> +struct create_function< + T, + D + BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_PARAMS(N, Param) +> { + static T * create(BOOST_PP_ENUM_BINARY_PARAMS(N, Param, p) ) { + return new D(BOOST_PP_ENUM_PARAMS(N, p)); + } +}; + +template < + class T, + class D + BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_PARAMS(N, class Param) +> +static T* create_derived(BOOST_PP_ENUM_BINARY_PARAMS(N, Param, p)) { + return new D(BOOST_PP_ENUM_PARAMS(N, p)); +} +#undef N + diff --git a/boost/extension/impl/decl.hpp b/boost/extension/impl/decl.hpp new file mode 100644 index 00000000..18ce0bdd --- /dev/null +++ b/boost/extension/impl/decl.hpp @@ -0,0 +1,24 @@ +/* + * Boost.Extension / main header: + * main header for extensions + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_DECL_HPP +#define BOOST_EXTENSION_DECL_HPP + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(MSC_VER) +# define BOOST_EXTENSION_EXPORT_DECL __declspec(dllexport) +# define BOOST_EXTENSION_IMPORT_DECL __declspec(dllimport) +#else +# define BOOST_EXTENSION_EXPORT_DECL +# define BOOST_EXTENSION_IMPORT_DECL +#endif + +#endif diff --git a/boost/extension/impl/factory.hpp b/boost/extension/impl/factory.hpp new file mode 100644 index 00000000..9b03222c --- /dev/null +++ b/boost/extension/impl/factory.hpp @@ -0,0 +1,63 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +// No header guard - this file is intended to be included multiple times. + +# define N BOOST_PP_ITERATION() +template +class factory +{ +public: + // This assertion will fire if a factory type is instantiated + // with something that is not a class. + BOOST_STATIC_ASSERT((is_class::value)); + // This assertion will fire if T is const. + BOOST_STATIC_ASSERT((!is_const::value)); + + template + void set() { + // This assertion will fire if a factory type is + // set to a class from which it doesn't inherit. + BOOST_STATIC_ASSERT((is_base_of::value)); + // This assertion will fire if D is const. + BOOST_STATIC_ASSERT((!is_const::value)); + this->func = &impl::create_function< + T, D BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_PARAMS(N,Param) + >::create; + } + + factory() : func(0) { + } + + factory(factory const& first) : func(first.func) {} + + factory& operator=(factory const& first) { + this->func = first->func; + return *this; + } + + bool is_valid() const { return this->func != 0; } + + T* create(BOOST_PP_ENUM_BINARY_PARAMS(N, Param, p)) const { + if (this->func) { + return this->func(BOOST_PP_ENUM_PARAMS(N, p)); + } + else { + return 0; + } + } + +private: + typedef T* (*func_ptr_type)(BOOST_PP_ENUM_PARAMS(N, Param)); + func_ptr_type func; +}; + +#undef N + diff --git a/boost/extension/impl/factory_map.hpp b/boost/extension/impl/factory_map.hpp new file mode 100644 index 00000000..e4e2ac3e --- /dev/null +++ b/boost/extension/impl/factory_map.hpp @@ -0,0 +1,62 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +// No header guard - this file is intended to be included multiple times. + +# define N BOOST_PP_ITERATION() + +template +std::map< + Info, + factory + > & +get() { + typedef Interface* (* func_ptr_type )(BOOST_PP_ENUM_PARAMS(N, Param)); + typedef type_info_handler handler_type; + + TypeInfo t = handler_type::get_class_type(); + + typename std::map::iterator + it = maps_.find(t); + + typedef factory< + Interface + BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_PARAMS(N, Param) + > factory_type; + typedef std::map map_type; + + map_holder* holder; + if (it == maps_.end()) + { + holder = new map_holder; + it = maps_.insert(std::make_pair(t, holder)).first; + } + else { + holder = static_cast* > (it->second); + } + + return *(static_cast(holder)); +} + +template +operator + std::map< + Info, + factory + >& + () +{ + return get< Interface, Info + BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_PARAMS(N, Param)>(); +} + +#undef N diff --git a/boost/extension/impl/function.hpp b/boost/extension/impl/function.hpp new file mode 100644 index 00000000..c1aba667 --- /dev/null +++ b/boost/extension/impl/function.hpp @@ -0,0 +1,21 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + * Note: + * The code to determine whether or not function type syntax + * is allowed in template declarations is based off of code + * written for Boost.Function. + */ + +#include + +// The following is based on code from Boost.Function +#if defined (BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ + || defined(BOOST_BCB_PARTIAL_SPECIALIZATION_BUG) \ + || !(BOOST_STRICT_CONFIG || !defined(__SUNPRO_CC) || __SUNPRO_CC > 0x540) +# define BOOST_EXTENSION_NO_FUNCTION_TYPE_SYNTAX +#endif diff --git a/boost/extension/impl/library_impl.hpp b/boost/extension/impl/library_impl.hpp new file mode 100644 index 00000000..3e4ae3f6 --- /dev/null +++ b/boost/extension/impl/library_impl.hpp @@ -0,0 +1,75 @@ +/* + * Boost.Extension / libraries management: + * low-level platform specific dynamic library management + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_LIBRARY_IMPL_HPP +#define BOOST_EXTENSION_LIBRARY_IMPL_HPP + +#include + +#if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) \ + && !defined(BOOST_DISABLE_WIN32) && !defined(__GNUC__) + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif + +#ifndef BOOST_EXTENSION_NO_LEAN_WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#endif + +#include +namespace boost { +namespace extensions { +namespace impl { + typedef HMODULE library_handle; + typedef FARPROC generic_function_ptr; + inline library_handle load_shared_library(const char* library_name) { + return LoadLibraryA(library_name); + } + inline generic_function_ptr get_function(library_handle handle, + const char* function_name) { + return GetProcAddress(handle, function_name); + } + inline bool close_shared_library(library_handle handle) { + return FreeLibrary(handle) != 0; + } +} // namespace impl +} // namespace extensions +} // namespace boost + +# pragma comment(lib, "kernel32.lib") +#else +#include +namespace boost { +namespace extensions { +namespace impl { + typedef void * library_handle; + typedef void * generic_function_ptr; + inline library_handle load_shared_library(const char* library_name) { + return dlopen(library_name, RTLD_LAZY); + } + inline generic_function_ptr get_function(library_handle handle, + const char* function_name) { + return dlsym(handle, function_name); + } + inline bool close_shared_library(library_handle handle) { + return dlclose(handle)==0; + } +} // namespace impl +} // namespace extensions +} // namespace boost + +#endif + +#endif diff --git a/boost/extension/impl/shared_library.hpp b/boost/extension/impl/shared_library.hpp new file mode 100644 index 00000000..d146fdb6 --- /dev/null +++ b/boost/extension/impl/shared_library.hpp @@ -0,0 +1,23 @@ +/* + * Boost.Extension / implementation header for Boost.PreProcessor + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ +# define N BOOST_PP_ITERATION() +// No ifndef headers - this is meant to be included multiple times. +template +ReturnValue (*get(const std::string& name)) + (BOOST_PP_ENUM_PARAMS(N, Param)) { + // Cast the handle or pointer to the function to the correct type. + // This is NOT typesafe. See the documentation of shared_library::get + return reinterpret_cast + (impl::get_function(handle_, name.c_str())); +} +#undef N diff --git a/boost/extension/impl/typeinfo.hpp b/boost/extension/impl/typeinfo.hpp new file mode 100644 index 00000000..f5a042d0 --- /dev/null +++ b/boost/extension/impl/typeinfo.hpp @@ -0,0 +1,114 @@ +/* + * Boost.Extension / typeinfo: + * implementations name management with RTTI + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#ifndef BOOST_EXTENSION_TYPEINFO_HPP +#define BOOST_EXTENSION_TYPEINFO_HPP +#include +namespace boost { namespace extensions { +template +struct type_info_handler { + static TypeInfo get_class_type(); +}; + +class default_type_info { +public: + default_type_info() : type_(&typeid(void)) { + } + default_type_info(const std::type_info& new_type) : type_(&new_type) { + } + default_type_info(const default_type_info& first) : type_(first.type_) { + } + const std::type_info& type() const { + return *type_; + } + default_type_info& operator=(const default_type_info& first) { + type_ = first.type_; + return *this; + } +private: + const std::type_info* type_; +}; +template +struct type_info_handler +{ + static default_type_info get_class_type() { + return default_type_info(typeid(ClassType)); + } +}; +}} + + +// This list should be expanded to all platforms that successfully +// compare type_info across shared library boundaries. +#if defined(__APPLE__) || defined(__GNUC__) || \ + defined(BOOST_EXTENSION_FORCE_FAST_TYPEINFO) +namespace boost { +namespace extensions { +inline bool operator<(const default_type_info& first, + const default_type_info& second) { + return &first.type() < &second.type(); +} + +inline bool operator==(const default_type_info& first, + const default_type_info& second) { + return &first.type() == &second.type(); +} + +inline bool operator>(const default_type_info& first, + const default_type_info& second) { + return &first.type() > &second.type(); +} +}} +#else +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +#include +namespace boost { namespace extensions { +inline bool operator<(const default_type_info& first, + const default_type_info& second) { + return std::strcmp(first.type().raw_name(), second.type().raw_name()) < 0; +} + +inline bool operator==(const default_type_info& first, + const default_type_info& second) { + return std::strcmp(first.type().raw_name(), second.type().raw_name()) == 0; +} + +inline bool operator>(const default_type_info& first, + const default_type_info& second) { + return std::strcmp(first.type().raw_name(), second.type().raw_name()) > 0; +} +} // namespace extensions +} // namespace boost +#else // OTHER OS +#include +namespace boost { namespace extensions { +inline bool operator<(const default_type_info& first, + const default_type_info& second) { + return std::strcmp(first.type().name(), second.type().name()) < 0; +} + +inline bool operator==(const default_type_info& first, + const default_type_info& second) { + return std::strcmp(first.type().name(), second.type().name()) == 0; +} + +inline bool operator>(const default_type_info& first, + const default_type_info& second) { + return std::strcmp(first.type().name(), second.type().name()) > 0; +} +} // namespace extensions +} // namespace boost + +#endif // WINDOWS +#endif // OS X +#endif // BOOST_EXTENSION_TYPEINFO_HPP diff --git a/boost/extension/registry.hpp b/boost/extension/registry.hpp new file mode 100644 index 00000000..e69de29b diff --git a/boost/extension/shared_library.hpp b/boost/extension/shared_library.hpp new file mode 100644 index 00000000..9f125fb5 --- /dev/null +++ b/boost/extension/shared_library.hpp @@ -0,0 +1,151 @@ +/* + * Boost.Extension / shared_library: + * Functions for shared_library loading. + * A basic wrapper around the OS-specific calls. + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#ifndef BOOST_EXTENSION_SHARED_LIBRARY_HPP +#define BOOST_EXTENSION_SHARED_LIBRARY_HPP + +#include + +#include +#include +#include + +namespace boost { +namespace extensions { +template +class basic_type_map; + +/** \brief A wrapper around OS-specific shared library functions. + +\note This class is inherently not type-safe. Carefully +check the signature of the function or type in the shared library +against the template arguments of the get functions. + */ +class shared_library { +public: + + /** shared_library constructor + * \param location The relative or absolute path of the shared library. + * \param auto_close An optional parameter which defaults to false. + * If set to true, the destructor will close this shared library + * if necessary. + */ + shared_library(const std::string& location, bool auto_close = false) + : location_(location), handle_(0), auto_close_(auto_close) { + } + + /** shared_library destructor + * If auto_close_ was set to true in the constructor, this closes + * the library if it is currently open. + */ + ~shared_library() { + if (handle_ && auto_close_) + close(); + } + + /** \return true if the shared library is currently open + * and referenced by this object. + * \pre None. + * \post None. + */ + bool is_open() const { return handle_ != 0; } + + /** \brief Open the shared library. + * \return true if the shared library was opened successfully. + * \pre None. + * \post If true is returned, the shared library is opened and + * get() can be called. + */ + bool open() { + if (handle_) + close(); + return (handle_ = impl::load_shared_library(location_.c_str())) != 0; + } + + /** \brief Close the shared library. + * This calls the OS specific close function for shared libraries. + * Usually, this decrements the reference count of the shared library. + * Once a shared library has a reference count of 0, it can be actually + * unloaded at any time. + * \return true if the close function was successful. + * \pre is_open() == true. + * \post is_open() == false. + */ + bool close() { + return impl::close_shared_library(handle_); + } + + /** \brief Call a special Extension function in the library. + * + * There is a special function called + * boost_extension_exported_type_map_function which is commonly + * used by shared libraries. The call function attempts to find + * and call that function, given a type_map. + * + * \return true on success. + * \param types A type_map that will be sent to the function. + * \pre is_open() == true + * \post None. + */ + template + bool call(basic_type_map& types) { + void (*func)(basic_type_map&); + func = get&> + ("boost_extension_exported_type_map_function"); + if (func) { + (*func)(types); + return true; + } else { + return false; + } + } +// If Doxygen is being run, use more readable definitions for the +// documentation. +#ifdef BOOST_EXTENSION_DOXYGEN_INVOKED + /** \brief Get a function reference. + * + * A templated function taking as template arguments the + * type of the return value and parameters of a function + * to look up in the shared library. + * + * This function must have been declared with the same + * parameters and return type and marked as extern "C". + * + * Depending on platform and compiler settings, it may also + * be necessary to prefix the function with BOOST_EXTENSION_DECL, + * to make it externally visible. + * + * \warning If the function signature does not match, strange errors + * can occur. + * \pre is_open() == true. + * \post None. + */ + template + FunctionPtr + get(const std::string& name) const { + } +#else +#define BOOST_PP_ITERATION_LIMITS (0, \ + BOOST_PP_INC(BOOST_EXTENSION_MAX_FUNCTOR_PARAMS) - 1) +#define BOOST_PP_FILENAME_1 +#include BOOST_PP_ITERATE() +#endif +protected: + std::string location_; + impl::library_handle handle_; + bool auto_close_; +}; +} // namespace extensions +} // namespace boost +#endif // BOOST_EXTENSION_SHARED_LIBRARY_HPP diff --git a/boost/extension/type_map.hpp b/boost/extension/type_map.hpp new file mode 100644 index 00000000..b36ef8ff --- /dev/null +++ b/boost/extension/type_map.hpp @@ -0,0 +1,129 @@ +/* + * Boost.Extension / factory map: + * map of factories (for the implementations) + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_TYPE_MAP_HPP +#define BOOST_EXTENSION_TYPE_MAP_HPP + +#include +#include +#include +#include + +namespace boost { +namespace extensions { + +/** \brief A collection of types. + * \tparam TypeInfo The type used for TypeInfo. By default, + * RTTI is used, but users can define their own TypeInfo. + * See impl/typeinfo.hpp. + * + * The `type_map` class is used for holding an arbitrary collection + * of types - no more than one of each type. + * In general, standard usage is as follows: + * + * \code + * type_map types; + * // This will add an integer to the type_map, or retrieve + * // one if it already exists. + * int& first_int(types.get()); + * first_int = 5; + * // This will make second_int point to the same value + * // as first_int. + * int& second_int(types.get()); + * second_int = 10; + * // Now first_int is 10. + * // It is also possible to use arbitrary types in the map, + * // as long as they are default constructible. + * std::set& my_string(types.get()); + * \endcode + */ +template +class basic_type_map { +public: + +#ifndef BOOST_EXTENSION_DOXYGEN_INVOKED + class type_map_convertible { + public: + ~type_map_convertible() { + for (typename std::map::iterator + it =instances_.begin(); it != instances_.end(); ++it) { + delete it->second; + } + } + template + operator Type&() { + typedef typename remove_const::type StoredType; + TypeInfo t = + type_info_handler + ::get_class_type(); + typename std::map::iterator + it = instances_.find(t); + + type_holder* holder; + if (it == instances_.end()) { + holder = new type_holder; + it = instances_.insert(std::make_pair(t, holder)).first; + } + else { + holder = static_cast*>(it->second); + } + return holder->val; + } + + private: + struct generic_type_holder { + virtual ~generic_type_holder() {} + }; + + // T must be default constructible. + template + struct type_holder : generic_type_holder { + T val; + }; + std::map instances_; + }; + + type_map_convertible& get() { + return convertible_; + } +#endif + /** \brief Retrieve a given type from the type_map. + * + * This is the only method users should ever need to use. + * By calling it with a template argument `Type`, a reference + * to the single object of that type will be returned, after + * being created if necessary. + * It is possible to omit the template parameter if it is clear + * from context: + * \code + * type_map types; + * int& my_int(types.get()); + * \endcode + * \tparam Type The type of the object to return a reference to. + */ + template + Type& get() { + return convertible_; + } +private: + type_map_convertible convertible_; + +}; +/** A typedef for convenience - provides the most common + * type of basic_factory_map. + */ +typedef basic_type_map type_map; + +} // namespace extensions +} // namespace boost + +#endif // BOOST_EXTENSION_TYPE_MAP_HPP diff --git a/boost/fiber/all.hpp b/boost/fiber/all.hpp new file mode 100644 index 00000000..4613fb18 --- /dev/null +++ b/boost/fiber/all.hpp @@ -0,0 +1,14 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_FIBER_H +#define BOOST_FIBER_H + +#include +#include +#include + +#endif // BOOST_FIBER_H diff --git a/boost/fiber/asym_fiber.hpp b/boost/fiber/asym_fiber.hpp new file mode 100644 index 00000000..e5e65052 --- /dev/null +++ b/boost/fiber/asym_fiber.hpp @@ -0,0 +1,226 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_FIBERS_ASYM_FIBER_H +#define BOOST_FIBERS_ASYM_FIBER_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251) +# endif + +namespace boost { +namespace fibers { + +class BOOST_FIBER_DECL asym_fiber +{ +private: + struct dummy {}; + + BOOST_COPYABLE_AND_MOVABLE( asym_fiber); + + detail::asym_fiber_base::ptr impl_; + + static detail::asym_fiber_base::ptr make_fiber_( + void( * fn)(), std::size_t stacksize, bool do_return) + { + return detail::asym_fiber_base::ptr( + do_return + ? new detail::asym_fiber_object< void(*)() >( fn, stacksize, detail::asym_fiber_base::do_return) + : new detail::asym_fiber_object< void(*)() >( fn, stacksize, detail::asym_fiber_base::do_not_return) ); + } + +#ifdef BOOST_MSVC + template< typename Fn > + static detail::asym_fiber_base::ptr make_fiber_( + Fn & fn, std::size_t stacksize, bool do_return) + { + return detail::asym_fiber_base::ptr( + do_return + ? new detail::asym_fiber_object< Fn >( fn, stacksize, detail::asym_fiber_base::do_return) + : new detail::asym_fiber_object< Fn >( fn, stacksize, detail::asym_fiber_base::do_not_return) ); + } +#else + template< typename Fn > + static detail::asym_fiber_base::ptr make_fiber_( + Fn fn, std::size_t stacksize, bool do_return) + { + return detail::asym_fiber_base::ptr( + do_return + ? new detail::asym_fiber_object< Fn >( fn, stacksize, detail::asym_fiber_base::do_return) + : new detail::asym_fiber_object< Fn >( fn, stacksize, detail::asym_fiber_base::do_not_return) ); + } +#endif + + template< typename Fn > + static detail::asym_fiber_base::ptr make_fiber__( + BOOST_RV_REF( Fn) fn, std::size_t stacksize, bool do_return) + { + return detail::asym_fiber_base::ptr( + do_return + ? new detail::asym_fiber_object< Fn >( fn, stacksize, detail::asym_fiber_base::do_return) + : new detail::asym_fiber_object< Fn >( fn, stacksize, detail::asym_fiber_base::do_not_return) ); + } + +public: + static std::size_t default_stacksize; + + class id; + + asym_fiber(); + +#ifdef BOOST_MSVC + template< typename Fn > + asym_fiber( Fn & fn, std::size_t stacksize, bool do_return = true) : + impl_( make_fiber_( fn, stacksize, do_return) ) + {} +#else + template< typename Fn > + asym_fiber( Fn fn, std::size_t stacksize, bool do_return = true) : + impl_( make_fiber_( fn, stacksize, do_return) ) + {} +#endif + + template< typename Fn > + asym_fiber( BOOST_RV_REF( Fn) fn, std::size_t stacksize, bool do_return = true) : + impl_( make_fiber__( fn, stacksize, do_return) ) + {} + +#define BOOST_FIBERS_ASYM_FIBER_ARG(z, n, unused) \ + BOOST_PP_CAT(A, n) BOOST_PP_CAT(a, n) +#define BOOST_ENUM_FIBERS_ASYM_FIBER_ARGS(n) BOOST_PP_ENUM(n, BOOST_FIBERS_ASYM_FIBER_ARG, ~) + +#define BOOST_FIBERS_ASYM_FIBER_CTOR(z, n, unused) \ + template< typename Fn, BOOST_PP_ENUM_PARAMS(n, typename A) > \ + asym_fiber( Fn fn, BOOST_ENUM_FIBERS_ASYM_FIBER_ARGS(n), std::size_t stacksize, bool do_return = true) : \ + impl_( \ + make_fiber_( \ + boost::bind( boost::type< void >(), fn, BOOST_PP_ENUM_PARAMS(n, a) ), \ + stacksize, do_return) ) \ + {} \ + +#ifndef BOOST_FIBERS_ASYM_MAX_ARITY +#define BOOST_FIBERS_ASYM_MAX_ARITY 10 +#endif + +BOOST_PP_REPEAT_FROM_TO( 1, BOOST_FIBERS_ASYM_MAX_ARITY, BOOST_FIBERS_ASYM_FIBER_CTOR, ~) + +#undef BOOST_FIBERS_ASYM_MAY_ARITY +#undef BOOST_FIBERS_ASYM_FIBER_ARG +#undef BOOST_FIBERS_ASYM_FIBER_ARGS +#undef BOOST_FIBERS_ASYM_FIBER_CTOR + + asym_fiber( asym_fiber const& other); + + asym_fiber & operator=( BOOST_COPY_ASSIGN_REF( asym_fiber) other); + + asym_fiber( BOOST_RV_REF( asym_fiber) other); + + asym_fiber & operator=( BOOST_RV_REF( asym_fiber) other); + + typedef detail::asym_fiber_base::ptr::unspecified_bool_type unspecified_bool_type; + + operator unspecified_bool_type() const; + + bool operator!() const; + + void swap( asym_fiber & other); + + id get_id() const; + + bool operator==( asym_fiber const& other) const; + bool operator!=( asym_fiber const& other) const; + + void run(); + + void yield(); + + bool finished() const; +}; + +class BOOST_FIBER_DECL asym_fiber::id +{ +private: + friend class asym_fiber; + + boost::uint64_t id_; + + explicit id( detail::asym_fiber_base::ptr const& info) : + id_( reinterpret_cast< boost::uint64_t >( info.get() ) ) + {} + +public: + id() : + id_( 0) + {} + + bool operator==( id const& other) const + { return id_ == other.id_; } + + bool operator!=( id const& other) const + { return id_ != other.id_; } + + bool operator<( id const& other) const + { return id_ < other.id_; } + + bool operator>( id const& other) const + { return other.id_ < id_; } + + bool operator<=( id const& other) const + { return !( other.id_ < id_); } + + bool operator>=( id const& other) const + { return ! ( id_ < other.id_); } + + template< typename charT, class traitsT > + friend std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream< charT, traitsT > & os, id const& other) + { + if ( 0 != other.id_) return os << other.id_; + else return os << "{not-a-fiber}"; + } +}; + +} + +using fibers::asym_fiber; + +inline +void swap( asym_fiber & l, asym_fiber & r) +{ return l.swap( r); } + +} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_ASYM_FIBER_H diff --git a/boost/fiber/detail/asym_fiber_base.hpp b/boost/fiber/detail/asym_fiber_base.hpp new file mode 100644 index 00000000..9f336308 --- /dev/null +++ b/boost/fiber/detail/asym_fiber_base.hpp @@ -0,0 +1,92 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_FIBERS_DETAIL_ASYM_FIBER_BASE_H +#define BOOST_FIBERS_DETAIL_ASYM_FIBER_BASE_H + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4251) +#pragma warning(disable:4275) +#endif + +namespace boost { +namespace fibers { + +namespace detail { + +BOOST_FIBER_DECL void trampoline_asym( void *); + +class BOOST_FIBER_DECL asym_fiber_base : private noncopyable +{ +public: + typedef intrusive_ptr< asym_fiber_base > ptr; + + struct do_not_return_t {}; + + struct do_return_t {}; + + static do_not_return_t do_not_return; + static do_return_t do_return; + + friend BOOST_FIBER_DECL void trampoline_asym( void * vp); + + friend inline void intrusive_ptr_add_ref( asym_fiber_base * p) + { ++p->use_count_; } + + friend inline void intrusive_ptr_release( asym_fiber_base * p) + { if ( --p->use_count_ == 0) delete p; } + + asym_fiber_base( std::size_t stacksize, do_not_return_t); + + asym_fiber_base( std::size_t stacksize, do_return_t); + + virtual ~asym_fiber_base() {} + + void run(); + + void yield(); + + void set_finished(); + + bool get_finished() const; + +protected: + virtual void exec() = 0; + +private: + unsigned int use_count_; + bool finished_; + context<> caller_; + context<> callee_; +}; + +}}} + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_DETAIL_ASYM_FIBER_BASE_H diff --git a/boost/fiber/detail/asym_fiber_object.hpp b/boost/fiber/detail/asym_fiber_object.hpp new file mode 100644 index 00000000..96ce2a7e --- /dev/null +++ b/boost/fiber/detail/asym_fiber_object.hpp @@ -0,0 +1,125 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_FIBERS_DETAIL_ASYM_FIBER_OBJECT_H +#define BOOST_FIBERS_DETAIL_ASYM_FIBER_OBJECT_H + +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace detail { + +template< typename Fn > +class asym_fiber_object : public asym_fiber_base +{ +private: + Fn fn_; + + asym_fiber_object( asym_fiber_object &); + asym_fiber_object & operator=( asym_fiber_object const&); + +public: + asym_fiber_object( Fn fn, std::size_t stacksize, do_return_t v) : + asym_fiber_base( stacksize, v), + fn_( fn) + {} + + asym_fiber_object( Fn fn, std::size_t stacksize, do_not_return_t v) : + asym_fiber_base( stacksize, v), + fn_( fn) + {} + + void exec() + { fn_(); } +}; + +template< typename Fn > +class asym_fiber_object< BOOST_RV_REF( Fn) > : public asym_fiber_base +{ +private: + Fn fn_; + + asym_fiber_object( asym_fiber_object &); + asym_fiber_object & operator=( asym_fiber_object const&); + +public: + asym_fiber_object( BOOST_RV_REF( Fn) fn, std::size_t stacksize, do_return_t v) : + asym_fiber_base( stacksize, v), + fn_( fn) + {} + + asym_fiber_object( BOOST_RV_REF( Fn) fn, std::size_t stacksize, do_not_return_t v) : + asym_fiber_base( stacksize, v), + fn_( fn) + {} + + void exec() + { fn_(); } +}; + +template< typename Fn > +class asym_fiber_object< reference_wrapper< Fn > > : public asym_fiber_base +{ +private: + Fn & fn_; + + asym_fiber_object( asym_fiber_object &); + asym_fiber_object & operator=( asym_fiber_object const&); + +public: + asym_fiber_object( reference_wrapper< Fn > fn, std::size_t stacksize, do_return_t v) : + asym_fiber_base( stacksize, v), + fn_( fn) + {} + + asym_fiber_object( reference_wrapper< Fn > fn, std::size_t stacksize, do_not_return_t v) : + asym_fiber_base( stacksize, v), + fn_( fn) + {} + + void exec() + { fn_(); } +}; + +template< typename Fn > +class asym_fiber_object< const reference_wrapper< Fn > > : public asym_fiber_base +{ +private: + Fn & fn_; + + asym_fiber_object( asym_fiber_object &); + asym_fiber_object & operator=( asym_fiber_object const&); + +public: + asym_fiber_object( const reference_wrapper< Fn > fn, std::size_t stacksize, do_return_t v) : + asym_fiber_base( stacksize, v), + fn_( fn) + {} + + void exec() + { fn_(); } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_DETAIL_ASYM_FIBER_OBJECT_H diff --git a/boost/fiber/detail/config.hpp b/boost/fiber/detail/config.hpp new file mode 100644 index 00000000..a218f834 --- /dev/null +++ b/boost/fiber/detail/config.hpp @@ -0,0 +1,33 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// this file is based on config.hpp of boost.thread + +#ifndef BOOST_FIBERS_DETAIL_CONFIG_H +#define BOOST_FIBERS_DETAIL_CONFIG_H + +#include +#include + +#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_FIBER_DYN_LINK) +# if defined(BOOST_FIBER_SOURCE) +# define BOOST_FIBER_DECL BOOST_SYMBOL_EXPORT +# else +# define BOOST_FIBER_DECL BOOST_SYMBOL_IMPORT +# endif +#else +# define BOOST_FIBER_DECL +#endif + +#if ! defined(BOOST_FIBER_SOURCE) && !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_FIBER_NO_LIB) +# define BOOST_LIB_NAME boost_fiber +# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_FIBER_DYN_LINK) +# define BOOST_DYN_LINK +# endif +# include +#endif + +#endif // BOOST_FIBERS_DETAIL_CONFIG_H diff --git a/boost/fiber/detail/sym_fiber_base.hpp b/boost/fiber/detail/sym_fiber_base.hpp new file mode 100644 index 00000000..a8cd44c0 --- /dev/null +++ b/boost/fiber/detail/sym_fiber_base.hpp @@ -0,0 +1,84 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_FIBERS_DETAIL_SYM_FIBER_BASE_H +#define BOOST_FIBERS_DETAIL_SYM_FIBER_BASE_H + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4251) +#pragma warning(disable:4275) +#endif + +namespace boost { +namespace fibers { + +namespace detail { + +BOOST_FIBER_DECL void trampoline_sym( void *); + +class BOOST_FIBER_DECL sym_fiber_base : private noncopyable +{ +public: + typedef intrusive_ptr< sym_fiber_base > ptr; + + friend BOOST_FIBER_DECL void trampoline_sym( void * vp); + + friend inline void intrusive_ptr_add_ref( sym_fiber_base * p) + { ++p->use_count_; } + + friend inline void intrusive_ptr_release( sym_fiber_base * p) + { if ( --p->use_count_ == 0) delete p; } + + sym_fiber_base(); + + sym_fiber_base( std::size_t stacksize); + + sym_fiber_base( std::size_t stacksize, sym_fiber_base & nxt); + + virtual ~sym_fiber_base() {} + + void switch_to( sym_fiber_base & other); + + void set_finished(); + + bool get_finished() const; + +protected: + virtual void exec() = 0; + +private: + unsigned int use_count_; + bool finished_; + context<> ctx_; +}; + +}}} + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_DETAIL_SYM_FIBER_BASE_H diff --git a/boost/fiber/detail/sym_fiber_object.hpp b/boost/fiber/detail/sym_fiber_object.hpp new file mode 100644 index 00000000..7f3fcc9b --- /dev/null +++ b/boost/fiber/detail/sym_fiber_object.hpp @@ -0,0 +1,145 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software Licenseersion 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_FIBERS_DETAIL_SYM_FIBER_OBJECT_H +#define BOOST_FIBERS_DETAIL_SYM_FIBER_OBJECT_H + +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { +namespace detail { + +class sym_fiber_dummy : public sym_fiber_base +{ +private: + sym_fiber_dummy( sym_fiber_dummy &); + sym_fiber_dummy & operator=( sym_fiber_dummy const&); + +public: + sym_fiber_dummy() : + sym_fiber_base() + {} + + void exec() + { BOOST_ASSERT( false && "should not be invoked"); } +}; + +template< typename Fn > +class sym_fiber_object : public sym_fiber_base +{ +private: + Fn fn_; + + sym_fiber_object( sym_fiber_object &); + sym_fiber_object & operator=( sym_fiber_object const&); + +public: + sym_fiber_object( Fn fn, std::size_t stacksize) : + sym_fiber_base( stacksize), + fn_( fn) + {} + + sym_fiber_object( Fn fn, std::size_t stacksize, sym_fiber_base & nxt) : + sym_fiber_base( stacksize, nxt), + fn_( fn) + {} + + void exec() + { fn_(); } +}; + +template< typename Fn > +class sym_fiber_object< BOOST_RV_REF( Fn) > : public sym_fiber_base +{ +private: + Fn fn_; + + sym_fiber_object( sym_fiber_object &); + sym_fiber_object & operator=( sym_fiber_object const&); + +public: + sym_fiber_object( BOOST_RV_REF( Fn) fn, std::size_t stacksize) : + sym_fiber_base( stacksize), + fn_( fn) + {} + + sym_fiber_object( BOOST_RV_REF( Fn) fn, std::size_t stacksize, sym_fiber_base & nxt) : + sym_fiber_base( stacksize, nxt), + fn_( fn) + {} + + void exec() + { fn_(); } +}; + +template< typename Fn > +class sym_fiber_object< reference_wrapper< Fn > > : public sym_fiber_base +{ +private: + Fn & fn_; + + sym_fiber_object( sym_fiber_object &); + sym_fiber_object & operator=( sym_fiber_object const&); + +public: + sym_fiber_object( reference_wrapper< Fn > fn, std::size_t stacksize) : + sym_fiber_base( stacksize), + fn_( fn) + {} + + sym_fiber_object( reference_wrapper< Fn > fn, std::size_t stacksize, sym_fiber_base & nxt) : + sym_fiber_base( stacksize, nxt), + fn_( fn) + {} + + void exec() + { fn_(); } +}; + +template< typename Fn > +class sym_fiber_object< const reference_wrapper< Fn > > : public sym_fiber_base +{ +private: + Fn & fn_; + + sym_fiber_object( sym_fiber_object &); + sym_fiber_object & operator=( sym_fiber_object const&); + +public: + sym_fiber_object( const reference_wrapper< Fn > fn, std::size_t stacksize) : + sym_fiber_base( stacksize), + fn_( fn) + {} + + sym_fiber_object( const reference_wrapper< Fn > fn, std::size_t stacksize, sym_fiber_base & nxt) : + sym_fiber_base( stacksize, nxt), + fn_( fn) + {} + + void exec() + { fn_(); } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_DETAIL_SYM_FIBER_OBJECT_H diff --git a/boost/fiber/exceptions.hpp b/boost/fiber/exceptions.hpp new file mode 100644 index 00000000..b763551f --- /dev/null +++ b/boost/fiber/exceptions.hpp @@ -0,0 +1,46 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_FIBERS_EXCEPTIONS_H +#define BOOST_FIBERS_EXCEPTIONS_H + +#include +#include + +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +class fiber_moved : public std::logic_error +{ +public: + fiber_moved() : + std::logic_error("fiber moved") + {} +}; + +class jumpable_moved : public std::logic_error +{ +public: + jumpable_moved() : + std::logic_error("jumpable moved") + {} +}; + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_EXCEPTIONS_H diff --git a/boost/fiber/sym_fiber.hpp b/boost/fiber/sym_fiber.hpp new file mode 100644 index 00000000..86295bb5 --- /dev/null +++ b/boost/fiber/sym_fiber.hpp @@ -0,0 +1,274 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_FIBERS_SYM_FIBER_H +#define BOOST_FIBERS_SYM_FIBER_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251) +# endif + +namespace boost { +namespace fibers { + +class BOOST_FIBER_DECL sym_fiber +{ +private: + struct dummy {}; + + BOOST_COPYABLE_AND_MOVABLE( sym_fiber); + + detail::sym_fiber_base::ptr impl_; + + sym_fiber( detail::sym_fiber_base::ptr const& impl); + + static detail::sym_fiber_base::ptr make_fiber_( + void( * fn)(), std::size_t stacksize) + { + return detail::sym_fiber_base::ptr( + new detail::sym_fiber_object< void(*)() >( fn, stacksize) ); + } + + static detail::sym_fiber_base::ptr make_fiber_( void( * fn)(), + std::size_t stacksize, detail::sym_fiber_base::ptr & nxt) + { + return detail::sym_fiber_base::ptr( + new detail::sym_fiber_object< void(*)() >( fn, stacksize, * nxt) ); + } + +#ifdef BOOST_MSVC + template< typename Fn > + static detail::sym_fiber_base::ptr make_fiber_( + Fn & fn, std::size_t stacksize) + { + return detail::sym_fiber_base::ptr( + new detail::sym_fiber_object< Fn >( fn, stacksize) ); + } + + template< typename Fn > + static detail::sym_fiber_base::ptr make_fiber_( + Fn & fn, std::size_t stacksize, detail::sym_fiber_base::ptr & nxt) + { + return detail::sym_fiber_base::ptr( + new detail::sym_fiber_object< Fn >( fn, stacksize, * nxt) ); + } +#else + template< typename Fn > + static detail::sym_fiber_base::ptr make_fiber_( + Fn fn, std::size_t stacksize) + { + return detail::sym_fiber_base::ptr( + new detail::sym_fiber_object< Fn >( fn, stacksize) ); + } + + template< typename Fn > + static detail::sym_fiber_base::ptr make_fiber_( + Fn fn, std::size_t stacksize, detail::sym_fiber_base::ptr & nxt) + { + return detail::sym_fiber_base::ptr( + new detail::sym_fiber_object< Fn >( fn, stacksize, * nxt) ); + } +#endif + + template< typename Fn > + static detail::sym_fiber_base::ptr make_fiber__( + BOOST_RV_REF( Fn) fn, std::size_t stacksize) + { + return detail::sym_fiber_base::ptr( + new detail::sym_fiber_object< Fn >( fn, stacksize) ); + } + + template< typename Fn > + static detail::sym_fiber_base::ptr make_fiber__( BOOST_RV_REF( Fn) fn, + std::size_t stacksize, detail::sym_fiber_base::ptr & nxt) + { + return detail::sym_fiber_base::ptr( + new detail::sym_fiber_object< Fn >( fn, stacksize, * nxt) ); + } + +public: + static std::size_t default_stacksize; + + static sym_fiber from_current_context(); + + class id; + + sym_fiber(); + +#ifdef BOOST_MSVC + template< typename Fn > + sym_fiber( Fn & fn, std::size_t stacksize) : + impl_( make_fiber_( fn, stacksize) ) + {} + + template< typename Fn > + sym_fiber( Fn & fn, std::size_t stacksize, sym_fiber & nxt) : + impl_( make_fiber_( fn, stacksize, nxt.impl_) ) + {} +#else + template< typename Fn > + sym_fiber( Fn fn, std::size_t stacksize) : + impl_( make_fiber_( fn, stacksize) ) + {} + + template< typename Fn > + sym_fiber( Fn fn, std::size_t stacksize, sym_fiber & nxt) : + impl_( make_fiber_( fn, stacksize, nxt.impl_) ) + {} +#endif + + template< typename Fn > + sym_fiber( BOOST_RV_REF( Fn) fn, std::size_t stacksize) : + impl_( make_fiber__( fn, stacksize) ) + {} + + template< typename Fn > + sym_fiber( BOOST_RV_REF( Fn) fn, std::size_t stacksize, sym_fiber & nxt) : + impl_( make_fiber__( fn, stacksize, nxt.impl_) ) + {} + +#define BOOST_FIBERS_SYM_FIBER_ARG(z, n, unused) \ + BOOST_PP_CAT(A, n) BOOST_PP_CAT(a, n) +#define BOOST_ENUM_FIBERS_SYM_FIBER_ARGS(n) BOOST_PP_ENUM(n, BOOST_FIBERS_SYM_FIBER_ARG, ~) + +#define BOOST_FIBERS_SYM_FIBER_CTOR(z, n, unused) \ + template< typename Fn, BOOST_PP_ENUM_PARAMS(n, typename A) > \ + sym_fiber( Fn fn, BOOST_ENUM_FIBERS_SYM_FIBER_ARGS(n), std::size_t stacksize) : \ + impl_( \ + make_fiber_( \ + boost::bind( boost::type< void >(), fn, BOOST_PP_ENUM_PARAMS(n, a) ), \ + stacksize) ) \ + {} \ +\ + template< typename Fn, BOOST_PP_ENUM_PARAMS(n, typename A) > \ + sym_fiber( Fn fn, BOOST_ENUM_FIBERS_SYM_FIBER_ARGS(n), std::size_t stacksize, sym_fiber & nxt) : \ + impl_( \ + make_fiber_( \ + boost::bind( boost::type< void >(), fn, BOOST_PP_ENUM_PARAMS(n, a) ), \ + stacksize, nxt.impl_) ) \ + {} \ + +#ifndef BOOST_FIBERS_SYM_MAX_ARITY +#define BOOST_FIBERS_SYM_MAX_ARITY 10 +#endif + +BOOST_PP_REPEAT_FROM_TO( 1, BOOST_FIBERS_SYM_MAX_ARITY, BOOST_FIBERS_SYM_FIBER_CTOR, ~) + +#undef BOOST_FIBERS_SYM_MAY_ARITY +#undef BOOST_FIBERS_SYM_FIBER_ARG +#undef BOOST_FIBERS_SYM_FIBER_ARGS +#undef BOOST_FIBERS_SYM_FIBER_CTOR + + sym_fiber( sym_fiber const& other); + + sym_fiber & operator=( BOOST_COPY_ASSIGN_REF( sym_fiber) other); + + sym_fiber( BOOST_RV_REF( sym_fiber) other); + + sym_fiber & operator=( BOOST_RV_REF( sym_fiber) other); + + typedef detail::sym_fiber_base::ptr::unspecified_bool_type unspecified_bool_type; + + operator unspecified_bool_type() const; + + bool operator!() const; + + void swap( sym_fiber & other); + + id get_id() const; + + bool operator==( sym_fiber const& other) const; + bool operator!=( sym_fiber const& other) const; + + void switch_to( sym_fiber & other); + + bool finished() const; +}; + +class BOOST_FIBER_DECL sym_fiber::id +{ +private: + friend class sym_fiber; + + boost::uint64_t id_; + + explicit id( detail::sym_fiber_base::ptr const& info) : + id_( reinterpret_cast< boost::uint64_t >( info.get() ) ) + {} + +public: + id() : + id_( 0) + {} + + bool operator==( id const& other) const + { return id_ == other.id_; } + + bool operator!=( id const& other) const + { return id_ != other.id_; } + + bool operator<( id const& other) const + { return id_ < other.id_; } + + bool operator>( id const& other) const + { return other.id_ < id_; } + + bool operator<=( id const& other) const + { return !( other.id_ < id_); } + + bool operator>=( id const& other) const + { return ! ( id_ < other.id_); } + + template< typename charT, class traitsT > + friend std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream< charT, traitsT > & os, id const& other) + { + if ( 0 != other.id_) return os << other.id_; + else return os << "{not-a-fiber}"; + } +}; + +} + +using fibers::sym_fiber; + +inline +void swap( sym_fiber & l, sym_fiber & r) +{ return l.swap( r); } + +} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_SYM_FIBER_H diff --git a/boost/reflection/adapter.hpp b/boost/reflection/adapter.hpp new file mode 100644 index 00000000..a17fc045 --- /dev/null +++ b/boost/reflection/adapter.hpp @@ -0,0 +1,117 @@ +/* + * Boost.Reflection / adapter (call functions using a parameter map) + * + * (C) Copyright Mariano G. Consoni 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#ifndef BOOST_REFLECTION_PARAMETER_MAP_HPP +#define BOOST_REFLECTION_PARAMETER_MAP_HPP +#include +#include +#include +#include +#include +#include + +namespace boost { namespace reflections { +class parameter_unavailable_exception : public std::exception { +public: + virtual const char * what() { + return "Type not found in parameter_map"; + } +}; + +/** \brief A container for generic_parameters. + * + * The basic_parameter_map is designed to hold arbitrary data indexed + * by the value of the Info parameter, and can return all items that + * match a certain type and Info parameter, ie all integers called "foobar". + */ +template +class basic_parameter_map + : protected std::multimap*> { +public: + /** The destructor deletes all of the parameters that it references. + */ + ~basic_parameter_map() { + for (typename map_type::iterator it = begin(); it != end(); ++it) { + delete it->second; + } + } + + typedef std::multimap*> map_type; + using map_type::equal_range; + using map_type::begin; + using map_type::end; + using map_type::insert; + + /** \brief Return a vector of all parameters matching the given + * type and Info. + * + * Given an Info value and a type, this will return all parameters + * that either match the given type or are convertible to the given + * type. + * \return A vector of all parameters that match the given type and Info. + * \param info The index or name of the parameters to return. + * \tparam D The type of parameters to return. + * \pre None. + * \post None. + */ + template + std::vector*> get(Info info) { + std::vector*> parameters; + std::pair its + = equal_range(info); + for (typename map_type::iterator current = its->first; + current != its->second; ++current) { + generic_parameter& p = *current->second; + if (p.template can_cast()) { + parameters.push_back(current->second); + } + } + return parameters; + } + + /** \brief Return a vector of all parameters matching the given + * type and Info. + * + * Given an Info value and a type, this will return all parameters + * that either match the given type or are convertible to the given + * type. This function is const. + * \return A vector of all parameters that match the given type and Info. + * \param info The index or name of the parameters to return. + * \tparam D The type of parameters to return. + * \pre None. + * \post None. + */ + template + std::vector*> get(Info info) const { + std::vector*> parameters; + std::pair its + = equal_range(info); + for (typename map_type::const_iterator current = its->first; + current != its->second; ++current) { + const generic_parameter& p = *current->second; + if (p.template can_cast()) { + parameters.push_back(current->second); + } + } + return parameters; + } +}; + +/** The most common version of basic_parameter_map. + */ +typedef basic_parameter_map<> parameter_map; +}} + +#endif // BOOST_REFLECTION_PARAMETER_MAP_HPP diff --git a/boost/reflection/common.hpp b/boost/reflection/common.hpp new file mode 100644 index 00000000..4fb2ac9e --- /dev/null +++ b/boost/reflection/common.hpp @@ -0,0 +1,29 @@ +/* + * Boost.Reflection / common: + * common include files + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_REFLECTION_COMMON_HPP +#define BOOST_REFLECTION_COMMON_HPP + +#include +#include +#include +#include +#include + +/** This determines the maximum number of parameters that a reflected + * function can have. 10 is the same default as Boost.Function. + */ +#ifndef BOOST_REFLECTION_MAX_FUNCTOR_PARAMS +#define BOOST_REFLECTION_MAX_FUNCTOR_PARAMS 10 +#endif // BOOST_REFLECTION_MAX_FUNCTOR_PARAMS + +#endif // BOOST_REFLECTION_COMMON_HPP diff --git a/boost/reflection/constructor.hpp b/boost/reflection/constructor.hpp new file mode 100644 index 00000000..86810d4e --- /dev/null +++ b/boost/reflection/constructor.hpp @@ -0,0 +1,51 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_REFLECTION_CONSTRUCTOR_HPP +#define BOOST_REFLECTION_CONSTRUCTOR_HPP +#include +namespace boost { +namespace reflections { +#ifdef BOOST_EXTENSION_DOXYGEN_INVOKED +// TODO: Write in a fake implementation of this class +// for readability and doxygen. +#else +#define BOOST_REFLECTION_CONSTRUCT_FUNCTION(Z, N, _) \ +template \ +Actual * construct(BOOST_PP_ENUM_BINARY_PARAMS(N, Param, p)) { \ + return new Actual(BOOST_PP_ENUM_PARAMS(N, p)); \ +} + +#define BOOST_REFLECTION_CONSTRUCTI_FUNCTION(Z, N, _) \ +template \ +Interface * construct_interface(BOOST_PP_ENUM_BINARY_PARAMS(N, Param, p)) { \ + return new Actual(BOOST_PP_ENUM_PARAMS(N, p)); \ +} +BOOST_PP_REPEAT(BOOST_PP_INC(BOOST_REFLECTION_MAX_FUNCTOR_PARAMS), \ + BOOST_REFLECTION_CONSTRUCT_FUNCTION, _) +BOOST_PP_REPEAT(BOOST_PP_INC(BOOST_REFLECTION_MAX_FUNCTOR_PARAMS), \ + BOOST_REFLECTION_CONSTRUCTI_FUNCTION, _) + +template +class instance_constructor; + +#define BOOST_PP_ITERATION_LIMITS (0, \ + BOOST_PP_INC(BOOST_REFLECTION_MAX_FUNCTOR_PARAMS) - 1) +#define BOOST_PP_FILENAME_1 +#include BOOST_PP_ITERATE() +#endif + +} // namespace reflections +} // namespace boost +#endif diff --git a/boost/reflection/constructor_info.hpp b/boost/reflection/constructor_info.hpp new file mode 100644 index 00000000..98f4d5df --- /dev/null +++ b/boost/reflection/constructor_info.hpp @@ -0,0 +1,65 @@ +/* + * Boost.Reflection / constructor information header + * + * (C) Copyright Mariano G. Consoni and Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +// No header guards, as this header is intended to be included multiple times. + +/** The basic_constructor_info class is used as a key in the map + * of constructors available for the current reflection. + * There are two types - those with ParameterInfo defined, and + * those without. + */ +#ifdef BOOST_REFLECTION_WITH_PARAMETER_INFO +template +struct basic_constructor_info { +/** A description for each parameter of the function. + * If Info=void, this does not appear. + * This member variable only occurs for reflections with + * parameter info. + */ + std::vector parameter_info_; + private: + function (*functor_func_)( + boost::reflections::basic_parameter_map& map, + const std::vector& names); + instance (*func_)( + boost::reflections::basic_parameter_map& map, + const std::vector& names); + std::map (*check_func_)( + const boost::reflections::basic_parameter_map& map, + const std::vector& names); + std::vector parameter_names_; + public: +#else +template +struct basic_constructor_info { +#endif + // The type of the function pointer used to construct + // the object this constructor_info is for. + TypeInfo type_info_; + + // Constructors. + explicit basic_constructor_info(TypeInfo t) : type_info_(t) { + } + + basic_constructor_info(const basic_constructor_info & s) + : type_info_(s.type_info_) { + } + + basic_constructor_info& operator=(basic_constructor_info & s) { + type_info_ = s.type_info_; + } + + // Less than operator - for maps. + friend inline bool operator<(const basic_constructor_info & t, + const basic_constructor_info & s) { + return t.type_info_ < s.type_info_; + } +}; diff --git a/boost/reflection/data.hpp b/boost/reflection/data.hpp new file mode 100644 index 00000000..3ebfc567 --- /dev/null +++ b/boost/reflection/data.hpp @@ -0,0 +1,52 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_REFLECTION_DATA_HPP +#define BOOST_REFLECTION_DATA_HPP + +#include + +namespace boost { +namespace reflections { +namespace impl { +// This is used to store, indexed by type information, +// any member data pointers. +typedef void* instance::*MemberPtr; + +template +Data& get_data_from_ptr(void* inst, MemberPtr ptr) { + Data Object::*data = reinterpret_cast(ptr); + Object* obj = reinterpret_cast(inst); + return (obj->*data); +} +} // namespace impl + +template +class data { +public: + data(impl::MemberPtr data_ptr = 0, + T& (*conversion_function)(void*, impl::MemberPtr) = 0) + : data_ptr_(data_ptr), + conversion_function_(conversion_function) { + } + T& operator()(instance & inst) const { + return (*conversion_function_)(inst.val_, data_ptr_); + } + bool valid() const { + return conversion_function_ != 0 && data_ptr_ != 0; + } +private: + impl::MemberPtr data_ptr_; + T& (*conversion_function_)(void*, impl::MemberPtr); +}; + + +} // namespace reflections +} // namespace boost +#endif // BOOST_REFLECTION_DATA_HPP diff --git a/boost/reflection/data_info.hpp b/boost/reflection/data_info.hpp new file mode 100644 index 00000000..df914ed3 --- /dev/null +++ b/boost/reflection/data_info.hpp @@ -0,0 +1,56 @@ +/* + * Boost.Reflection / data info + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_REFLECTION_DATA_INFO_HPP +#define BOOST_REFLECTION_DATA_INFO_HPP + +#include +#include + +namespace boost { +namespace reflections { + +// The basic_data_info class is used as a key in the map +// of data available for the current reflection. +template +struct basic_data_info { + BOOST_CONCEPT_ASSERT((LessThanComparable)); + // The type of the function pointer in the map. + TypeInfo type_info_; + // A description of the function pointer. + Info info_; + + // Constructors. + basic_data_info(TypeInfo t, Info i) + : type_info_(t), info_(i) { + } + + basic_data_info(const basic_data_info & s) + : type_info_(s.type_info_), info_(s.info_) { + } + + basic_data_info & operator=(basic_data_info & s) { + type_info_ = s.type_info_; + info_ = s.info_; + } + + // Less-than operator - for maps. + friend inline bool operator<(const basic_data_info & t, + const basic_data_info & s) { + return t.type_info_ < s.type_info_ || + (t.type_info_ == s.type_info_ && + t.info_ < s.info_); + } +}; + +} // namespace reflections +} // namespace boost +#endif // BOOST_REFLECTION_DATA_INFO_HPP diff --git a/boost/reflection/function.hpp b/boost/reflection/function.hpp new file mode 100644 index 00000000..d8c1264e --- /dev/null +++ b/boost/reflection/function.hpp @@ -0,0 +1,36 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_REFLECTION_FUNCTION_HPP +#define BOOST_REFLECTION_FUNCTION_HPP + +#include + +namespace boost { +namespace reflections { +namespace impl { +#ifdef BOOST_EXTENSION_DOXYGEN_INVOKED + +#else +typedef void (instance::*MemberFunctionPtr)(); +} // namespace impl +template +class function; + +#define BOOST_PP_ITERATION_LIMITS (0, \ + BOOST_PP_INC(BOOST_REFLECTION_MAX_FUNCTOR_PARAMS) - 1) +#define BOOST_PP_FILENAME_1 +#include BOOST_PP_ITERATE() +#endif +} // namespace reflections +} // namespace boost +#endif diff --git a/boost/reflection/function_info.hpp b/boost/reflection/function_info.hpp new file mode 100644 index 00000000..8a9402b4 --- /dev/null +++ b/boost/reflection/function_info.hpp @@ -0,0 +1,59 @@ +/* + * Boost.Reflection / main header + * + * (C) Copyright Mariano G. Consoni and Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +// No header guard, as this is intended to be included multiple times +// by reflection.hpp. + +// The basic_function_info class is used as a key in the map +// of functions available for the current reflection. +// There are two types - those with ParameterInfo defined, and +// those without. +#ifdef BOOST_REFLECTION_WITH_PARAMETER_INFO +template +struct basic_function_info { + // A description for each parameter of the function. + // If ParameterInfo=void, this does not appear. + std::vector parameter_info_; +#else +// Same as the above, but without ParameterInfo. +template +struct basic_function_info { +#endif + // The type of the function pointer in the map. + TypeInfo type_info_; + // A description of the function pointer. + Info info_; + + bool has_return_; + + + // Constructors + basic_function_info(TypeInfo t, Info i, bool has_return = true) + : type_info_(t), info_(i), has_return_(has_return) { + } + + basic_function_info(const basic_function_info & s) + : type_info_(s.type_info_), info_(s.info_) { + } + + basic_function_info & operator=(basic_function_info & s) { + type_info_ = s.type_info_; + info_ = s.info_; + } + + // Less-than operator - for maps. + friend inline bool operator<(const basic_function_info & t, + const basic_function_info & s) { + return t.type_info_ < s.type_info_ || + (t.type_info_ == s.type_info_ && + t.info_ < s.info_); + } +}; diff --git a/boost/reflection/generic_constructor.hpp b/boost/reflection/generic_constructor.hpp new file mode 100644 index 00000000..fee2415a --- /dev/null +++ b/boost/reflection/generic_constructor.hpp @@ -0,0 +1,23 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_GENERIC_CONSTRUCTOR_HPP +#define BOOST_EXTENSION_GENERIC_CONSTRUCTOR_HPP + +namespace boost { +namespace reflections { +template +class generic_constructor { +public: + virtual ~generic_constructor() {} + virtual T * create(void ** params) const = 0; +}; +} // namespace reflections +} // namespace boost +#endif diff --git a/boost/reflection/impl/constructor.hpp b/boost/reflection/impl/constructor.hpp new file mode 100644 index 00000000..0667047c --- /dev/null +++ b/boost/reflection/impl/constructor.hpp @@ -0,0 +1,32 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +// No header guard - this file is intended to be included multiple times. + +# define N BOOST_PP_ITERATION() + +template +class instance_constructor { +public: + instance_constructor(instance (*func)(BOOST_PP_ENUM_PARAMS(N, Param)) = 0) + : func_(func) { + } + instance call(BOOST_PP_ENUM_BINARY_PARAMS(N, Param, p)) { + return (*func_)(BOOST_PP_ENUM_PARAMS(N, p)); + } + instance operator()(BOOST_PP_ENUM_BINARY_PARAMS(N, Param, p)) { + return (*func_)(BOOST_PP_ENUM_PARAMS(N, p)); + } + bool valid() {return func_ != 0;} +private: + instance (*func_)(BOOST_PP_ENUM_PARAMS(N, Param)); +}; + +#undef N diff --git a/boost/reflection/impl/function.hpp b/boost/reflection/impl/function.hpp new file mode 100644 index 00000000..77d71feb --- /dev/null +++ b/boost/reflection/impl/function.hpp @@ -0,0 +1,47 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +// No header guard - this file is intended to be included multiple times. + +# define N BOOST_PP_ITERATION() + +template +class function { +public: + function(ReturnValue (*func)(void *, impl::MemberFunctionPtr + BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, Param)) = 0, + impl::MemberFunctionPtr member_function = 0) + : func_(func), + member_function_(member_function) { + } + ReturnValue call(instance & inst BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_BINARY_PARAMS(N, Param, p)) const { + return (*func_)(inst.val_, member_function_ BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, p)); + } + ReturnValue operator()(instance & inst BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_BINARY_PARAMS(N, Param, p)) const { + return (*func_)(inst.val_, member_function_ BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, p)); + } + bool valid() const { + return member_function_ != 0 && func_ != 0; + } +private: + ReturnValue (*func_)(void *, impl::MemberFunctionPtr + BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, Param)); + impl::MemberFunctionPtr member_function_; +}; + +#undef N diff --git a/boost/reflection/impl/reflection.hpp b/boost/reflection/impl/reflection.hpp new file mode 100644 index 00000000..dcd15283 --- /dev/null +++ b/boost/reflection/impl/reflection.hpp @@ -0,0 +1,148 @@ +/* + * Boost.Reflection / main header + * + * (C) Copyright Mariano G. Consoni and Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +// No header guard, as this file is intended to be included multiple times. + +// By default, ParameterInfo is not used. Note that using adapters +// requires ParameterInfo. +#ifdef BOOST_REFLECTION_WITH_PARAMETER_INFO +template +class basic_reflection { +#else +template +class basic_reflection { +#endif +public: +#ifndef BOOST_REFLECTION_WITH_PARAMETER_INFO +typedef void ParameterInfo; +#endif + // A reflector is used to add functions and constructors to + // a reflected class. + + template + class reflector + { + public: + // Initialize with a pointer to the reflection + // this reflector will be reflecting into + reflector(basic_reflection* + current_reflection) + : reflection_(current_reflection) { + } + + // Typedefs for the specific instantiations used by this class. + typedef basic_function_info function_info; + typedef basic_constructor_info constructor_info; + + reflector& constructor() { + instance (*ctor_func)()(&impl::construct_instance); + reflection_->constructors_.insert(std::make_pair( + reflections::type_info_handler + ::get_class_type(), reinterpret_cast(ctor_func))); + return *this; + } + + template + reflector& data(Data T::*data_ptr, Info info) { + data_info f(reflections::type_info_handler + ::get_class_type(), info); + Data& (*func)(void*, impl::MemberPtr) = &impl::get_data_from_ptr; + std::pair + p(reinterpret_cast(data_ptr), + reinterpret_cast(func)); + std::pair > + p2(f, p); + reflection_->data_.insert(p2); + return *this; + } + #define BOOST_PP_ITERATION_LIMITS (0, \ + BOOST_PP_INC(BOOST_REFLECTION_MAX_FUNCTOR_PARAMS) - 1) + #define BOOST_PP_FILENAME_1 \ + + #include BOOST_PP_ITERATE() + template + reflector& function(void (A::*func)(), Info info) { +#ifdef BOOST_REFLECTION_WITH_PARAMETER_INFO + function_info f(reflections::type_info_handler::get_class_type(), info, false); +#else + function_info f(reflections::type_info_handler::get_class_type(), info); +#endif + void (*f2)(void *, impl::MemberFunctionPtr) = &impl::call_member; + std::pair + in_pair(reinterpret_cast(func), + reinterpret_cast(f2)); + std::pair > + out_pair(f, in_pair); + reflection_->functions_.insert(out_pair); + return *this; + } + private: + basic_reflection* reflection_; + }; +#define BOOST_PP_ITERATION_LIMITS (0, \ + BOOST_PP_INC(BOOST_REFLECTION_MAX_FUNCTOR_PARAMS) - 1) +#define BOOST_PP_FILENAME_1 +#include BOOST_PP_ITERATE() + instance_constructor<> get_constructor() const { + constructor_info t(reflections::type_info_handler::get_class_type()); + typename std::map::const_iterator it = + constructors_.find(t); + if (it == constructors_.end()) { + return instance_constructor<>(); + } else { + return reinterpret_cast(it->second); + } + } + + template + data get_data(Info info) const { + // Construct a data_info structure to look up the function in the map. + data_info d(reflections::type_info_handler + ::get_class_type(), info); + + // Look up the function. + typename std::map >::const_iterator it = + data_.find(d); + + if (it == data_.end()) { + // If it does not exist, return an empty function object. + return data(); + } else { + return data + // reinterpret_cast is safe, because we looked it up by its type. + (it->second.first, + reinterpret_cast + (it->second.second)); + } + } + + template + reflector reflect() { + return reflector(this); + } +private: + typedef basic_function_info function_info; + typedef basic_constructor_info constructor_info; + typedef basic_data_info data_info; + + std::map constructors_; + std::map > functions_; + std::map > data_; +}; diff --git a/boost/reflection/impl/reflection_functions.hpp b/boost/reflection/impl/reflection_functions.hpp new file mode 100644 index 00000000..26441adc --- /dev/null +++ b/boost/reflection/impl/reflection_functions.hpp @@ -0,0 +1,76 @@ +/* + * Boost.Reflection / implementation header for Boost.PreProcessor + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ +# define N BOOST_PP_ITERATION() +// No ifndef headers - this is meant to be included multiple times. + +// Search for a constructor of the given type. instance_constructor +// has a method to determine if a suitable constructor was found. +template +instance_constructor get_constructor() const { + // Create a constructor_info structure to use for looking up + // a constructor in the constructor map. Initialize it with the + // function type requested. + constructor_info ctr_info(reflections::type_info_handler + ::get_class_type()); + + // Determine whether or not such a constructor exists. + typename std::map::const_iterator it = + constructors_.find(ctr_info); + + if (it == constructors_.end()) { + // If none exists, return an empty instance_constructor. + return instance_constructor(); + } else { + // reinterpret_cast is safe, because we looked it up by its type. + return reinterpret_cast + (it->second); + } +} + +// Search for a member function matching the given signature and Info. +template +function get_function(Info info) const { + // Construct a function_info structure to look up the function in the map. + // has_return is set to true here because it makes no difference when doing + // a lookup in the map. + function_info func_info(reflections::type_info_handler + ::get_class_type(), info); + + // Look up the function. + typename std::map >::const_iterator it = + functions_.find(func_info); + + if (it == functions_.end()) { + // If it does not exist, return an empty function object. + return function(); + } else { + return function + // reinterpret_cast is safe, because we looked it up by its type. + (reinterpret_cast + (it->second.second), it->second.first); + } +} + +#undef N diff --git a/boost/reflection/impl/reflector_free_functions.hpp b/boost/reflection/impl/reflector_free_functions.hpp new file mode 100644 index 00000000..addccc72 --- /dev/null +++ b/boost/reflection/impl/reflector_free_functions.hpp @@ -0,0 +1,109 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +// No header guard - this file is intended to be included multiple times. + +# define N BOOST_PP_ITERATION() +// Free functions. These should only be used internally by the +// reflector class to generate function pointers. +// +// This is a generic factory function to construct an instance of +// a given class using a constructor with the given signature. +template +static instance construct_instance(BOOST_PP_ENUM_BINARY_PARAMS(N, Param, p)) { + // An instance is similar to boost::any. Initialize it with + // a void ptr. + return instance(static_cast( + construct + (BOOST_PP_ENUM_PARAMS(N, p))), + &destruct); +} + +// This auxiliary function is used to call a member function on +// a given instance, assuming the instance is of type T. +template +static ReturnValue call_member(void * val, + impl::MemberFunctionPtr member_function + BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_BINARY_PARAMS(N, Param, p)) { + // Convert to a T*. + T * actual = static_cast(val); + + // Convert the MemberFunctionPtr to the requested type. + ReturnValue (T::*func)(BOOST_PP_ENUM_PARAMS(N, Param)) = + reinterpret_cast + (member_function); + + // Call the function and return the result. + return (actual->*func)(BOOST_PP_ENUM_PARAMS(N, p)); +} + +// The following are versions of the above that don't require +// knowing their parameters. +/* +template +instance create_func( + boost::reflections::basic_parameter_map& map, + const std::vector& names) { +#if N + reflections::generic_parameter* gen; +#define BOOST_REFLECTION_GET_FROM_LIST(z, n, data) \ + gen = map.template get_first(names[n]); \ + if (!gen) return 0; \ + BOOST_PP_CAT(Param, n) BOOST_PP_CAT(p, n) = \ + gen->template cast(); + BOOST_PP_REPEAT(N, BOOST_REFLECTION_GET_FROM_LIST, ) +#undef BOOST_REFLECTION_GET_FROM_LIST +#endif // N + return new Derived(BOOST_PP_ENUM_PARAMS(N, p)); +} + +template +boost::function get_functor_func( + boost::reflections::basic_parameter_map& map, + const std::vector& names) { +#if N + reflections::generic_parameter* gen; +#define BOOST_REFLECTION_GET_FROM_LIST(z, n, data) \ + gen = map.template get_first(names[n]); \ + if (!gen) return boost::function(); \ + BOOST_PP_CAT(Param, n) BOOST_PP_CAT(p, n) = \ + gen->template cast(); + BOOST_PP_REPEAT(N, BOOST_REFLECTION_GET_FROM_LIST, ) +#undef BOOST_REFLECTION_GET_FROM_LIST +#endif // N + Interface* (*f)(BOOST_PP_ENUM_PARAMS(N, Param)) = + impl::create_derived; + return bind(f + BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, p)); +} + +template +inline std::map check_func( + const boost::reflections::basic_parameter_map& map, + const std::vector& names) { + std::map needed_parameters; +#define BOOST_REFLECTION_CHECK_IN_LIST(z, n, data) \ +if (!map.template has(names[n])) \ + needed_parameters.insert(std::make_pair(\ + type_info_handler::template get_class_type(), \ + names[n])); + BOOST_PP_REPEAT(N, BOOST_REFLECTION_CHECK_IN_LIST, ) +#undef BOOST_REFLECTION_CHECK_IN_LIST + return needed_parameters; +}*/ +#undef N diff --git a/boost/reflection/impl/reflector_functions.hpp b/boost/reflection/impl/reflector_functions.hpp new file mode 100644 index 00000000..ef6400ce --- /dev/null +++ b/boost/reflection/impl/reflector_functions.hpp @@ -0,0 +1,136 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy atÄ + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +// No header guard - this file is intended to be included multiple times. + +# define N BOOST_PP_ITERATION() +// Versions with included info about parameters +// An auxiliary macro to add a single parameter to a list +// of parameter information. +#define BOOST_REFLECTION_PUSH_PARAMETER_INFO_SINGLE(f, N) \ +(f).parameter_info_.push_back(BOOST_PP_CAT(i, N)); + +// An auxiliary macro to add a series of parameters to a list +// of parameter information. +#define BOOST_REFLECTION_PUSH_PARAMETER_INFO(f, N) \ +BOOST_PP_IF(N, BOOST_REFLECTION_PUSH_PARAMETER_INFO_SINGLE(f, BOOST_PP_DEC(N)),) \ +BOOST_PP_IF(BOOST_PP_DEC(N), BOOST_REFLECTION_PUSH_PARAMETER_INFO(f, BOOST_PP_DEC(N)),) + +// Reflect a constructor with the given signature. +public: +template +#ifdef BOOST_REFLECTION_WITH_PARAMETER_INFO +reflector& constructor(BOOST_PP_ENUM_PARAMS(N, ParameterInfo i)) { +#else +reflector& constructor() { +#endif + instance (*ctor_func)( + ParamFirst BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_PARAMS(N, Param)) + (&impl::construct_instance); + constructor_info f(reflections::type_info_handler + + ::get_class_type()); +#ifdef BOOST_REFLECTION_WITH_PARAMETER_INFO + BOOST_REFLECTION_PUSH_PARAMETER_INFO(f, N); +#endif + reflection_->constructors_.insert( + std::make_pair( + f, reinterpret_cast(ctor_func))); + return *this; +} + +// This version of the function is for reflecting functions that have +// return values - so that the name of the return value can be set. +template +#ifdef BOOST_REFLECTION_WITH_PARAMETER_INFO +reflector& function(ReturnValue (A::*func)(BOOST_PP_ENUM_PARAMS(N, Param)), + Info info, ParameterInfo i_return BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, ParameterInfo i)) { + // Create the function_info for this function. + function_info f(reflections::type_info_handler + ::get_class_type(), info, true); + // Add the ParameterInfo for each parameter to the function_info. + BOOST_REFLECTION_PUSH_PARAMETER_INFO(f, N); + // Add the ParameterInfo for the return type. + f.parameter_info_.push_back(i_return); +#else +reflector& function(ReturnValue (A::*func)(BOOST_PP_ENUM_PARAMS(N, Param)), + Info info) { + // Create the function_info for this function. + function_info f(reflections::type_info_handler + ::get_class_type(), info); +#endif + + // Get a function pointer to a function that calls this member + // function when given a void* that actually points to an instance + // of this class. + ReturnValue (*f2)(void *, impl::MemberFunctionPtr BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, Param)) = + &impl::call_member; + + // Create the pair objects to insert into the map. + std::pair + in_pair(reinterpret_cast(func), + reinterpret_cast(f2)); + std::pair > + out_pair(f, in_pair); + reflection_->functions_.insert(out_pair); + return *this; +} + +// This version of the function is for reflecting functions that have +// no return value. +template +#ifdef BOOST_REFLECTION_WITH_PARAMETER_INFO +reflector& function(void (A::*func)(ParamFirst p_first BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, Param)), + Info info, ParameterInfo i_first BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, ParameterInfo i)) { + function_info f(reflections::type_info_handler + ::get_class_type(), info, false); + f.parameter_info_.push_back(i_first); + BOOST_REFLECTION_PUSH_PARAMETER_INFO(f, N); +#else +reflector& function(void (A::*func)(ParamFirst p_first BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, Param)), + Info info) { + function_info f(reflections::type_info_handler + ::get_class_type(), info); +#endif + void (*f2)(void *, impl::MemberFunctionPtr BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, Param)) = + &impl::call_member; + std::pair + in_pair(reinterpret_cast(func), + reinterpret_cast(f2)); + std::pair > + out_pair(f, in_pair); + reflection_->functions_.insert(out_pair); + return *this; +} + +#undef BOOST_REFLECTION_PUSH_PARAMETER_INFO_SINGLE +#undef BOOST_REFLECTION_PUSH_PARAMETER_INFO + +#undef N diff --git a/boost/reflection/impl/reflector_parameter_functions.hpp b/boost/reflection/impl/reflector_parameter_functions.hpp new file mode 100644 index 00000000..29f985e1 --- /dev/null +++ b/boost/reflection/impl/reflector_parameter_functions.hpp @@ -0,0 +1,123 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy atÄ + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +// No header guard - this file is intended to be included multiple times. + +# define N BOOST_PP_ITERATION() + +// Versions with included info about parameters +// An auxiliary macro to add a single parameter to a list +// of parameter information. +#define BOOST_REFLECTION_PUSH_PARAMETER_INFO_SINGLE(f, N) \ +(f).parameter_info_.push_back(BOOST_PP_CAT(i, N)); + +// An auxiliary macro to add a series of parameters to a list +// of parameter information. +#define BOOST_REFLECTION_PUSH_PARAMETER_INFO(f, N) \ +BOOST_PP_IF(N, BOOST_REFLECTION_PUSH_PARAMETER_INFO_SINGLE(f, BOOST_PP_DEC(N)),) \ +BOOST_PP_IF(BOOST_PP_DEC(N), BOOST_REFLECTION_PUSH_PARAMETER_INFO(f, BOOST_PP_DEC(N)),) + +// Reflect a constructor with the given signature. +public: +template +reflector& reflect_constructor(BOOST_PP_ENUM_PARAMS(N, ParameterInfo i)) { + instance (*ctor_func)( + ParamFirst BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_PARAMS(N, Param)) + (&impl::construct_instance); + constructor_info f(reflections::type_info_handler + + ::get_class_type()); + BOOST_REFLECTION_PUSH_PARAMETER_INFO(f, N); + reflection_->constructors_.insert(std::make_pair( + f, reinterpret_cast(ctor_func))); + return *this; +} + +// This version of the function is for reflecting functions that have +// return values - so that the name of the return value can be set. +template +reflector& function(ReturnValue (A::*func)(BOOST_PP_ENUM_PARAMS(N, Param)), + Info info, ParameterInfo i_return BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, ParameterInfo i)) { + // Create the function_info for this function. + function_info f(reflections::type_info_handler + ::get_class_type(), info, true); + + // Add the ParameterInfo for each parameter to the function_info. + BOOST_REFLECTION_PUSH_PARAMETER_INFO(f, N); + // Add the ParameterInfo for the return type. + f.parameter_info_.push_back(i_return); + + // Get a function pointer to a function that calls this member + // function when given a void* that actually points to an instance + // of this class. + ReturnValue (*f2)(void *, impl::MemberFunctionPtr BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, Param)) = + &impl::call_member; + + // Create the pair objects to insert into the map. + std::pair + in_pair(reinterpret_cast(func), + reinterpret_cast(f2)); + std::pair > + out_pair(f, in_pair); + reflection_->functions_.insert(out_pair); + return *this; +} + +// This version of the function is for reflecting functions that have +// no return value. +template +reflector& function(void (A::*func)(ParamFirst p_first BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, Param)), + Info info, ParameterInfo i_first BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, ParameterInfo i)) { + function_info f(reflections::type_info_handler + ::get_class_type(), info, false); + f.parameter_info_.push_back(i_first); + BOOST_REFLECTION_PUSH_PARAMETER_INFO(f, N); + void (*f2)(void *, impl::MemberFunctionPtr BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, Param)) = + &impl::call_member; + std::pair + in_pair(reinterpret_cast(func), + reinterpret_cast(f2)); + std::pair > + out_pair(f, in_pair); + reflection_->functions_.insert(out_pair); + return *this; +} +/* +template +void add_constructor(instance (*func)(ParamFirst BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, Param)) + BOOST_PP_COMMA_IF(N) + BOOST_PP_ENUM_PARAMS(N, ParameterInfo i)) { + constructor_info f(reflections::type_info_handler + + ::get_class_type()); + BOOST_REFLECTION_PUSH_PARAMETER_INFO(f, N); + reflection_->constructors_.insert(std::make_pair( + f, reinterpret_cast(func))); +}*/ + +#undef N diff --git a/boost/reflection/instance.hpp b/boost/reflection/instance.hpp new file mode 100644 index 00000000..b5ea991f --- /dev/null +++ b/boost/reflection/instance.hpp @@ -0,0 +1,54 @@ +/* + * Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_REFLECTION_INSTANCE_HPP +#define BOOST_REFLECTION_INSTANCE_HPP + +#include +namespace boost {namespace reflections { +class instance { +public: + instance(void* val = 0, void (*destructor)(void* val) = 0) + : val_(val), + destructor_(destructor) {} + ~instance() { + if (val_) + (*destructor_)(val_); + } + instance(const instance & first) + : val_(first.val_), + destructor_(first.destructor_) { + if (this != &first) { + // Check for self assignment + first.val_ = 0; + } + } + instance & operator=(instance & first) { + if (this != &first) { + // Check for self assignment + this->val_ = first.val_; + this->destructor_ = first.destructor_; + first.val_ = 0; + } + return *this; + } + +private: + template + friend class function; + template + friend class data; + mutable void* val_; + void (*destructor_)(void* object); +}; +}} +#endif diff --git a/boost/reflection/parameter.hpp b/boost/reflection/parameter.hpp new file mode 100644 index 00000000..c35e5119 --- /dev/null +++ b/boost/reflection/parameter.hpp @@ -0,0 +1,176 @@ +/* + * Boost.Reflection / paramater map (store parameter information for calls) + * + * (C) Copyright Mariano G. Consoni 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#ifndef BOOST_REFLECTION_PARAMETER_HPP +#define BOOST_REFLECTION_PARAMETER_HPP +#include +#include +#include +#include +namespace boost { namespace reflections { +using extensions::type_info_handler; + +class conversion_not_found_exception : public std::exception { +public: + virtual const char* what() { + return "Cannot convert types"; + } +}; +/** \brief A container for a single item - similar to boost::any. + * + * The primary difference between generic_parameter and boost::any + * is that a generic_parameter can be declared to be convertible + * to arbitrary types, in addition to the base type that it holds. + * This allows an object to also be accessible through pointers to + * its base types, for example. + */ +template +class generic_parameter { +public: + typedef void (*FunctionPtr)(); + + /** The destructor cleans up the converters contained + * in this generic_parameter. + */ + virtual ~generic_parameter() { + for (typename std::map::iterator + it = converters_.begin(); + it != converters_.end(); ++it) { + delete it->second; + } + } + + /** Return the TypeInfo for the primary type of this generic_parameter. + */ + virtual TypeInfo type() const = 0; + + /** \brief Returns true if the parameter can convert to T. + * + * Given a type T, this function returns true if the generic_parameter + * can convert its value to T. + * \tparam T The type to check for conversions for. + * \returns true if the conversion is possible. + */ + template + bool can_cast() const { + TypeInfo i = type_info_handler::get_class_type(); + return (converters_.find(i) != converters_.end()); + } + + /** \brief Returns a type S, converted from the type in the parameter. + * + * This will attempt to convert the generic_parameter to type T. + * If it fails, it will throw an exception. To avoid the exception, + * the can_cast function can be called first. + * \tparam T + * \returns A value of T that was converted from the generic_parameter. + * \pre can_cast() == true + * \post None. + */ + template + T cast() const { + T dest; + TypeInfo i = type_info_handler::get_class_type(); + typename std::map::const_iterator it = + converters_.find(i); + if (it != converters_.end()) { + it->second->convert(value_, reinterpret_cast(&dest)); + return dest; + } + throw conversion_not_found_exception(); + } + + /** \brief Another form of cast. + * + * Identical to T cast(), but takes a pointer to T instead. + */ + template + void cast(T* dest) { + *dest = cast(); + } +protected: + generic_parameter(void* value) : value_(value) { + } + class basic_converter { + public: + virtual void convert(void* src, void* dest) const = 0; + virtual ~basic_converter() {} + }; + std::map converters_; +private: + void* value_; +}; + +template + class parameter : public generic_parameter { +public: + template + friend class basic_parameter_map; + + virtual TypeInfo type() const { + return reflections::type_info_handler::get_class_type(); + } + + explicit parameter(T value) + : generic_parameter(reinterpret_cast(&value_)), + value_(value) { + // Add converter for current type. + generic_parameter::converters_.insert + (std::make_pair(reflections::type_info_handler + ::get_class_type(), + new default_converter())); + } + template + void converts_to_with_func(void (*convert_func)(T*, S*)) { + generic_parameter::converters_.insert + (std::make_pair(reflections::type_info_handler + ::get_class_type(), + new specialized_converter(convert_func))); + } + template + void converts_to() { + generic_parameter::converters_.insert + (std::make_pair(reflections::type_info_handler + ::get_class_type(), + new default_converter())); + } +private: + + template + class default_converter : + public generic_parameter::basic_converter { + public: + virtual void convert(void* val, void* dest) const { + S* s = reinterpret_cast(dest); + *s = static_cast(*reinterpret_cast(val)); + } + }; + template + class specialized_converter : + public generic_parameter::basic_converter { + public: + explicit specialized_converter(void (*convert_function)(T*, S*)) + : convert_function_(convert_function) { + } + virtual void convert(void* val, void* dest) const { + S* s = reinterpret_cast(dest); + (*convert_function_)(reinterpret_cast(val), s); + } + private: + void (*convert_function_)(T*, S*); + }; + T value_; +}; +} +} + +#endif // BOOST_REFLECTION_PARAMETER_HPP diff --git a/boost/reflection/parameter_map.hpp b/boost/reflection/parameter_map.hpp new file mode 100644 index 00000000..555b1141 --- /dev/null +++ b/boost/reflection/parameter_map.hpp @@ -0,0 +1,128 @@ +/* + * Boost.Reflection / paramater map (store parameter information for calls) + * + * (C) Copyright Mariano G. Consoni 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#ifndef BOOST_REFLECTION_PARAMETER_MAP_HPP +#define BOOST_REFLECTION_PARAMETER_MAP_HPP +#include +#include +#include +#include +#include +#include + +namespace boost { namespace reflections { +class parameter_unavailable_exception : public std::exception { +public: + virtual const char * what() { + return "Type not found in parameter_map"; + } +}; + +template +class basic_parameter_map + : protected std::multimap*> { +public: + ~basic_parameter_map() { + for (typename map_type::iterator it = begin(); it != end(); ++it) { + delete it->second; + } + } + typedef std::multimap*> map_type; + using map_type::equal_range; + using map_type::begin; + using map_type::end; + using map_type::insert; + + /** \brief Return all parameters matching the TypeInfo and Info. + * + * Given a type (D) and Info (ie, string describing the parameter), + * return a vector containing all generic_parameters that match, + * or can be converted to the given type. + * + * \return Matching parameters. + * \pre None. + * \post None. + * \param Info The Info (ie, name) describing the parameter needed. + * \tparam D The type of parameter to return. + */ + template + std::vector*> get(const Info& info) { + std::vector*> parameters; + std::pair its + = equal_range(info); + for (typename map_type::iterator current = its.first; + current != its.second; ++current) { + generic_parameter& p = *current->second; + if (p.template can_cast()) { + parameters.push_back(current->second); + } + } + return parameters; + } + + /** \brief Return true if the given parameter exists. + * + * Given a type (D) and Info (ie, string describing the parameter), + * return true if the element exists in the parameter_map. + * + * \return True if the parameter exists. + * \pre None. + * \post None. + * \param Info The Info (ie, name) describing the parameter needed. + * \tparam D The type of parameter to search for. + */ + template + bool has(const Info& info) const { + std::pair its + = equal_range(info); + for (typename map_type::const_iterator current = its.first; + current != its.second; ++current) { + generic_parameter& p = *current->second; + if (p.template can_cast()) { + return true; + } + } + return false; + } + + /** \brief Return the first matching parameter. + * + * Given a type (D) and Info (ie, string describing the parameter), + * return first parameter matching, or that can be converted to that + * type. + * + * \return The first matching parameter. + * \pre None. + * \post None. + * \param Info The Info (ie, name) describing the parameter needed. + * \tparam D The type of parameter to search for. + */ + template + generic_parameter* get_first(const Info& info) { + std::pair its + = equal_range(info); + for (typename map_type::iterator current = its.first; + current != its.second; ++current) { + generic_parameter& p = *current->second; + if (p.template can_cast()) { + return &p; + } + } + return 0; + } +}; +typedef basic_parameter_map<> parameter_map; +}} + +#endif // BOOST_REFLECTION_PARAMETER_MAP_HPP diff --git a/boost/reflection/reflection.hpp b/boost/reflection/reflection.hpp new file mode 100644 index 00000000..b0ccb436 --- /dev/null +++ b/boost/reflection/reflection.hpp @@ -0,0 +1,210 @@ +/* + * Boost.Reflection / main header + * + * (C) Copyright Mariano G. Consoni and Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_REFLECTION_REFLECTION_HPP +#define BOOST_REFLECTION_REFLECTION_HPP + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace reflections { + +// Provide a simplified version of the class for Doxygen, as +// well as for human consumption. +#ifdef BOOST_REFLECTION_DOXYGEN_INVOKED + +template +class basic_reflection { +public: +typedef void ParameterInfo; + /** \brief Internal class for reflecting constructors, functions etc. + * + * The reflector class is returned by the reflect method, and can + * then be used to reflect constructors, functions or data. + * It is normally used chained: + * \code + * reflection r; + * r.reflect() + * .constructor() + * .constructor(); + * \endcode + */ + template + class reflector + { + public: + /** Initialize with a pointer to the reflection + * this reflector will be reflecting into. + * This is called by the reflection::reflect function. + * \param current_reflection The reflection to set to type T. + * \pre current_reflection has not already been reflected into. + * \post None. + */ + reflector(basic_reflection* + current_reflection) + : reflection_(current_reflection) { + } + + // Typedefs for the specific instantiations used by this class. + typedef basic_function_info function_info; + typedef basic_constructor_info constructor_info; + + /** \brief Reflect a constructor with params (Params...) + * Reflects the constructor of T that takes the arguments listed + * in Params... + * \tparam Params... A variable length list of parameters the + * constructor takes. + * \returns *this. + * \pre None. + * \post None. + */ + template + reflector& constructor() {} + + /** \brief Reflect a data member of the class. + * \param data_ptr The fully-qualified member address of the data member. + * \param info A description or other Info of this data member. + * \tparam Data The type of the data being reflected. + * Reflect a data member of the class, which will be referenceable + * by its type and the value of the Info parameter. + * \code + * reflection r; + * r.reflect().constructor() + * .data(&MyClass:someData, "someDataDescription"); + * \endcode + * \pre None. + * \post None. + */ + template + reflector& data(Data T::*data_ptr, Info info) {} + + /** \brief Reflect a member function of the class. + * \param func The fully-qualified member address of the function. + * \param info A description or other Info of this function. + * \tparam Data The function's return type. + * \tparam Params... The parameters of the function. + * Reflect a member function of the class, which will be referenceable + * by its type and the value of the Info parameter. The template + * parameters only need to be included if the function is overloaded. + * \code + * reflection r; + * r.reflect().constructor() + * .function(&MyClass:someFunction, "someFuncDescription"); + * \endcode + * \pre None. + * \post None. + */ + template + reflector& function(void (T::*func)(), Info info) {} + }; + + /** \brief Attempt to retrieve a constructor. + * \tparam Params... The parameters of the requested function. + * \returns A constructor reference (that must be checked for validity). + * + * Attempt to retrieve any constructor whose Params match the list in + * Params... + * For example: + * \code + * r.reflect() + * .constructor() ; + * instance_constructor<> ic = r.get_constructor(); + * if (i.valid()) { + * instance i = ic(); + * } + * \endcode + * \pre None. + * \post None. + */ + template + instance_constructor get_constructor() const {} + + /** \brief Attempt to retrieve a data member. + * \tparam Data The type of the requested data. + * \returns A data reference (that must be checked for validity). + * + * Attempt to retrieve any data of type Data and the same Info. + * For example: + * \code + * r.reflect() + * .constructor() + * .data(&MyClass:someInt, "My Int"); + * instance_constructor<> ic = r.get_constructor(); + * instance i = ic(); + * data d = r.get_data("My Int"); + * int& myIntRef = d(i); + * \endcode + * \pre None. + * \post None. + */ + template + data get_data(Info info) const {} + + /** \brief Set the type of this reflection. + * \tparam T The type to set the reflection to. + * Set the type of this reflection, and return a reflector + * which can be used to reflect constructors, data and functions + * of the class T. + * \returns A reflector instance. + * \pre reflect() has not been called. + * \post None. + */ + template + reflector reflect() {} +}; + +#else +using extensions::type_info_handler; +namespace impl { +template +void destruct(void * val) { + delete static_cast(val); +} + +#define BOOST_PP_ITERATION_LIMITS (0, \ + BOOST_PP_INC(BOOST_REFLECTION_MAX_FUNCTOR_PARAMS) - 1) +#define BOOST_PP_FILENAME_1 \ + +#include BOOST_PP_ITERATE() + +// This is used to store, indexed by type information, +// any normal function pointers. +typedef void (*FunctionPtr)(); + +} // namespace impl + +// Since there are two specializations, with and without parameter +// information - but which are otherwise mostly the same - the +// implementation file is included twice. +#define BOOST_REFLECTION_WITH_PARAMETER_INFO +#include +#include +#include +#undef BOOST_REFLECTION_WITH_PARAMETER_INFO + +#include +#include +#include +#endif // BOOST_REFLECTION_DOXYGEN_INVOKED + +typedef basic_reflection<> reflection; +}} +#endif // BOOST_REFLECTION_REFLECTION_HPP diff --git a/boost/task.hpp b/boost/task.hpp new file mode 100644 index 00000000..4e95fb76 --- /dev/null +++ b/boost/task.hpp @@ -0,0 +1,44 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_H +#define BOOST_TASKS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // BOOST_TASKS_H + diff --git a/boost/task/as_sub_task.hpp b/boost/task/as_sub_task.hpp new file mode 100755 index 00000000..8cccd592 --- /dev/null +++ b/boost/task/as_sub_task.hpp @@ -0,0 +1,51 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_AS_SUB_TASK_H +#define BOOST_TASKS_AS_SUB_TASK_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasks { + +struct as_sub_task +{ + template< typename R > + handle< R > operator()( BOOST_RV_REF( task< R >) t) + { + detail::worker * w( detail::worker::tss_get() ); + if ( w) + { + spin::promise< R > prom; + spin::shared_future< R > f( prom.get_future() ); + context ctx; + handle< R > h( f, ctx); + w->put( callable( t, boost::move( prom), ctx) ); + return h; + } + else + return new_thread()( t); + } +}; + +}} + +#include + +#endif // BOOST_TASKS_AS_SUB_TASK_H diff --git a/boost/task/async.hpp b/boost/task/async.hpp new file mode 100644 index 00000000..1a4e1782 --- /dev/null +++ b/boost/task/async.hpp @@ -0,0 +1,128 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_ASYNC_H +#define BOOST_TASKS_ASYNC_H + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasks { + +template< typename R > +handle< R > async( task< R > t) +{ return async( boost::move( t) ); } + +template< typename R > +handle< R > async( BOOST_RV_REF( task< R >) t) +{ return as_sub_task()( t); } + +template< typename R > +handle< R > async( task< R > t, as_sub_task ast) +{ return async( boost::move( t), ast); } + +template< typename R > +handle< R > async( BOOST_RV_REF( task< R >) t, as_sub_task ast) +{ return ast( t); } + +template< typename R > +handle< R > async( task< R > t, own_thread ot) +{ return async( boost::move( t) ); } + +template< typename R > +handle< R > async( BOOST_RV_REF( task< R >) t, own_thread ot) +{ return ot( t); } + +template< typename R > +handle< R > async( task< R > t, new_thread nt) +{ return async( boost::move( t), nt); } + +template< typename R > +handle< R > async( BOOST_RV_REF( task< R >) t, new_thread nt) +{ return nt( t); } + +template< typename R, typename Queue, typename UMS > +handle< R > async( task< R > t, static_pool< Queue, UMS > & pool) +{ return async( boost::move(t), pool); } + +template< typename R, typename Queue, typename UMS > +handle< R > async( BOOST_RV_REF( task< R >) t, static_pool< Queue, UMS > & pool) +{ return pool.submit( t); } + +template< typename R, typename Attr, typename Queue, typename UMS > +handle< R > async( task< R > t, Attr attr, static_pool< Queue, UMS > & pool) +{ return async( boost::move(t), attr, pool); } + +template< typename R, typename Attr, typename Queue, typename UMS > +handle< R > async( BOOST_RV_REF( task< R >) t, Attr attr, static_pool< Queue, UMS > & pool) +{ return pool.submit( t, attr); } + +template< typename R, typename Strategy > +handle< R > async( + task< R > t, + tasklets::scheduler< Strategy > & sched, + std::size_t stacksize = tasklet::default_stacksize) +{ return async( boost::move( t), sched, stacksize); } + +template< typename R, typename Strategy > +handle< R > async( + BOOST_RV_REF( task< R >) t, + tasklets::scheduler< Strategy > & sched, + std::size_t stacksize = tasklet::default_stacksize) +{ + if ( this_task::runs_in_pool() ) + { + spin::promise< R > prom; + spin::shared_future< R > f( prom.get_future() ); + context ctx; + handle< R > h( f, ctx); + tasklet fib( callable( t, boost::move( prom), ctx), stacksize); + sched.submit_tasklet( boost::move( fib) ); + return h; + } + else + { + promise< R > prom; + shared_future< R > f( prom.get_future() ); + context ctx; + handle< R > h( f, ctx); + tasklet fib( + callable( + t, +// TODO: workaround because thread_move_t will be abigous for move +#ifdef BOOST_HAS_RVALUE_REFS + boost::move( prom), +#else + boost::detail::thread_move_t< promise< R > >( prom), +#endif + ctx), stacksize); + sched.submit_tasklet( boost::move( fib) ); + return h; + } +} + +}} + +#include + +#endif // BOOST_TASKS_ASYNC_H diff --git a/boost/task/bounded_fifo.hpp b/boost/task/bounded_fifo.hpp new file mode 100644 index 00000000..d09e31ed --- /dev/null +++ b/boost/task/bounded_fifo.hpp @@ -0,0 +1,205 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_BOUNDED_FIFO_H +#define BOOST_TASKS_BOUNDED_FIFO_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasks { + +class bounded_fifo +{ +public: + typedef detail::has_no_attribute attribute_tag_type; + typedef callable value_type; + +private: + struct node + { + typedef shared_ptr< node > sptr_t; + + value_type va; + sptr_t next; + }; + + enum state + { + ACTIVE = 0, + DEACTIVE + }; + + atomic< state > state_; + atomic< std::size_t > count_; + node::sptr_t head_; + mutable mutex head_mtx_; + node::sptr_t tail_; + mutable mutex tail_mtx_; + condition not_full_cond_; + std::size_t hwm_; + std::size_t lwm_; + fast_semaphore & fsem_; + + bool active_() const + { return ACTIVE == state_.load(); } + + void deactivate_() + { state_.store( DEACTIVE); } + + std::size_t size_() const + { return count_.load(); } + + bool empty_() const + { return head_ == get_tail_(); } + + bool full_() const + { return size_() >= hwm_; } + + node::sptr_t get_tail_() const + { + lock_guard< mutex > lk( tail_mtx_); + node::sptr_t tmp = tail_; + return tmp; + } + + node::sptr_t pop_head_() + { + node::sptr_t old_head = head_; + head_ = old_head->next; + count_.fetch_sub( 1); + return old_head; + } + +public: + bounded_fifo( + fast_semaphore & fsem, + high_watermark const& hwm, + low_watermark const& lwm) : + state_( ACTIVE), + count_( 0), + head_( new node), + head_mtx_(), + tail_( head_), + tail_mtx_(), + not_full_cond_(), + hwm_( hwm), + lwm_( lwm), + fsem_( fsem) + {} + + std::size_t upper_bound() const + { return hwm_; } + + std::size_t lower_bound() const + { return lwm_; } + + bool active() const + { return active_(); } + + void deactivate() + { + unique_lock< mutex > lk( head_mtx_); + deactivate_(); + not_full_cond_.notify_all(); + } + + bool empty() const + { + unique_lock< mutex > lk( head_mtx_); + return empty_(); + } + + void put( value_type const& va) + { + node::sptr_t new_node( new node); + { + unique_lock< mutex > lk( tail_mtx_); + + if ( full_() ) + { + while ( active_() && full_() ) + not_full_cond_.wait( lk); + } + if ( ! active_() ) + throw task_rejected("queue is not active"); + + tail_->va = va; + tail_->next = new_node; + tail_ = new_node; + count_.fetch_add( 1); + } + fsem_.post(); + } + + template< typename TimeDuration > + void put( + value_type const& va, + TimeDuration const& rel_time) + { + node::sptr_t new_node( new node); + { + unique_lock< mutex > lk( tail_mtx_); + + if ( full_() ) + { + while ( active_() && full_() ) + if ( ! not_full_cond_.wait( lk, rel_time) ) + throw task_rejected("timed out"); + } + if ( ! active_() ) + throw task_rejected("queue is not active"); + + tail_->va = va; + tail_->next = new_node; + tail_ = new_node; + count_.fetch_add( 1); + } + fsem_.post(); + } + + bool try_take( value_type & va) + { + unique_lock< mutex > lk( head_mtx_); + if ( empty_() ) + return false; + va.swap( head_->va); + pop_head_(); + bool valid = ! va.empty(); + if ( valid && size_() <= lwm_) + { + if ( lwm_ == hwm_) + not_full_cond_.notify_one(); + else + // more than one producer could be waiting + // in order to submit an task + not_full_cond_.notify_all(); + } + return valid; + } +}; + +}} + +#include + +#endif // BOOST_TASKS_BOUNDED_FIFO_H diff --git a/boost/task/bounded_prio_queue.hpp b/boost/task/bounded_prio_queue.hpp new file mode 100644 index 00000000..47978edd --- /dev/null +++ b/boost/task/bounded_prio_queue.hpp @@ -0,0 +1,238 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_BOUNDED_PRIO_QUEUE_H +#define BOOST_TASKS_BOUNDED_PRIO_QUEUE_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasks { + +template< + typename Attr, + typename Comp = std::less< Attr > +> +class bounded_prio_queue +{ +public: + typedef detail::has_attribute attribute_tag_type; + typedef Attr attribute_type; + + struct value_type + { + callable ca; + attribute_type attr; + + value_type( + callable const& ca_, + attribute_type const& attr_) : + ca( ca_), attr( attr_) + { BOOST_ASSERT( ! ca.empty() ); } + + void swap( value_type & other) + { + ca.swap( other.ca); + std::swap( attr, other.attr); + } + }; + +private: + struct compare : public std::binary_function< value_type, value_type, bool > + { + bool operator()( value_type const& va1, value_type const& va2) + { return Comp()( va1.attr, va2.attr); } + }; + + typedef std::priority_queue< + value_type, + std::deque< value_type >, + compare + > queue_type; + + enum state + { + ACTIVE = 0, + DEACTIVE + }; + + atomic< state > state_; + queue_type queue_; + mutable shared_mutex mtx_; + condition not_full_cond_; + std::size_t hwm_; + std::size_t lwm_; + fast_semaphore & fsem_; + + bool active_() const + { return ACTIVE == state_.load(); } + + void deactivate_() + { state_.store( DEACTIVE); } + + bool empty_() const + { return queue_.empty(); } + + bool full_() const + { return size_() >= hwm_; } + + std::size_t size_() const + { return queue_.size(); } + + void put_( + value_type const& va, + unique_lock< shared_mutex > & lk) + { + if ( full_() ) + { + not_full_cond_.wait( + lk, + bind( + & bounded_prio_queue::producers_activate_, + this) ); + } + if ( ! active_() ) + throw task_rejected("queue is not active"); + queue_.push( va); + fsem_.post(); + } + + template< typename TimeDuration > + void put_( + value_type const& va, + TimeDuration const& rel_time, + unique_lock< shared_mutex > & lk) + { + if ( full_() ) + { + if ( ! not_full_cond_.timed_wait( + lk, + rel_time, + bind( + & bounded_prio_queue::producers_activate_, + this) ) ) + throw task_rejected("timed out"); + } + if ( ! active_() ) + throw task_rejected("queue is not active"); + queue_.push( va); + fsem_.post(); + } + + bool try_take_( callable & ca) + { + if ( empty_() ) + return false; + callable tmp( queue_.top().ca); + queue_.pop(); + ca.swap( tmp); + bool valid = ! ca.empty(); + if ( valid && size_() <= lwm_) + { + if ( lwm_ == hwm_) + not_full_cond_.notify_one(); + else + // more than one producer could be waiting + // in order to submit an task + not_full_cond_.notify_all(); + } + return valid; + } + + bool producers_activate_() const + { return ! active_() || ! full_(); } + +public: + bounded_prio_queue( + fast_semaphore & fsem, + high_watermark const& hwm, + low_watermark const& lwm) : + state_( ACTIVE), + queue_(), + mtx_(), + not_full_cond_(), + hwm_( hwm), + lwm_( lwm), + fsem_( fsem) + { + if ( lwm_ > hwm_ ) + throw invalid_watermark(); + } + + bool active() const + { return active_(); } + + void deactivate() + { + unique_lock< shared_mutex > lk( mtx_); + deactivate_(); + not_full_cond_.notify_all(); + } + + bool empty() const + { + shared_lock< shared_mutex > lk( mtx_); + return empty_(); + } + + std::size_t upper_bound() const + { + shared_lock< shared_mutex > lk( mtx_); + return hwm_; + } + + std::size_t lower_bound() const + { + shared_lock< shared_mutex > lk( mtx_); + return lwm_; + } + + void put( value_type const& va) + { + unique_lock< shared_mutex > lk( mtx_); + put_( va, lk); + } + + template< typename TimeDuration > + void put( + value_type const& va, + TimeDuration const& rel_time) + { + unique_lock< shared_mutex > lk( mtx_); + put_( va, rel_time, lk); + } + + bool try_take( callable & ca) + { + unique_lock< shared_mutex > lk( mtx_); + return try_take_( ca); + } +}; + +}} + +#include + +#endif // BOOST_TASKS_BOUNDED_PRIO_QUEUE_H diff --git a/boost/task/bounded_smart_queue.hpp b/boost/task/bounded_smart_queue.hpp new file mode 100644 index 00000000..b982372a --- /dev/null +++ b/boost/task/bounded_smart_queue.hpp @@ -0,0 +1,249 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_BOUNDED_SMART_QUEUE_H +#define BOOST_TASKS_BOUNDED_SMART_QUEUE_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasks { + +template< + typename Attr, + typename Comp, + typename Enq = detail::replace_oldest, + typename Deq = detail::take_oldest +> +class bounded_smart_queue +{ +public: + typedef detail::has_attribute attribute_tag_type; + typedef Attr attribute_type; + + struct value_type + { + callable ca; + attribute_type attr; + + value_type( + callable const& ca_, + attribute_type const& attr_) : + ca( ca_), attr( attr_) + { BOOST_ASSERT( ! ca.empty() ); } + + void swap( value_type & other) + { + ca.swap( other.ca); + std::swap( attr, other.attr); + } + }; + +private: + typedef multi_index::multi_index_container< + value_type, + multi_index::indexed_by< + multi_index::ordered_non_unique< + multi_index::member< + value_type, + Attr, + & value_type::attr + >, + Comp + > + > + > queue_type; + typedef typename queue_type::template nth_index< 0 >::type queue_index; + + enum state + { + ACTIVE = 0, + DEACTIVE + }; + + atomic< state > state_; + queue_type queue_; + queue_index & idx_; + mutable shared_mutex mtx_; + condition not_full_cond_; + Enq enq_op_; + Deq deq_op_; + std::size_t hwm_; + std::size_t lwm_; + fast_semaphore & fsem_; + + bool active_() const + { return ACTIVE == state_.load(); } + + void deactivate_() + { state_.store( DEACTIVE); } + + bool empty_() const + { return queue_.empty(); } + + bool full_() const + { return size_() >= hwm_; } + + std::size_t size_() const + { return queue_.size(); } + + void put_( + value_type const& va, + unique_lock< shared_mutex > & lk) + { + if ( full_() ) + { + not_full_cond_.wait( + lk, + bind( + & bounded_smart_queue::producers_activate_, + this) ); + } + if ( ! active_() ) + throw task_rejected("queue is not active"); + enq_op_( idx_, va); + fsem_.post(); + } + + template< typename TimeDuration > + void put_( + value_type const& va, + TimeDuration const& rel_time, + unique_lock< shared_mutex > & lk) + { + if ( full_() ) + { + if ( ! not_full_cond_.timed_wait( + lk, + rel_time, + bind( + & bounded_smart_queue::producers_activate_, + this) ) ) + throw task_rejected("timed out"); + } + if ( ! active_() ) + throw task_rejected("queue is not active"); + enq_op_( idx_, va); + fsem_.post(); + } + + bool try_take_( callable & ca) + { + if ( empty_() ) + return false; + deq_op_( idx_, ca); + bool valid = ! ca.empty(); + if ( valid && size_() <= lwm_) + { + if ( lwm_ == hwm_) + not_full_cond_.notify_one(); + else + // more than one producer could be waiting + // in order to submit an task + not_full_cond_.notify_all(); + } + return valid; + } + + bool producers_activate_() const + { return ! active_() || ! full_(); } + +public: + bounded_smart_queue( + fast_semaphore & fsem, + high_watermark const& hwm, + low_watermark const& lwm) : + state_( ACTIVE), + queue_(), + idx_( queue_.get< 0 >() ), + mtx_(), + not_full_cond_(), + enq_op_(), + deq_op_(), + hwm_( hwm), + lwm_( lwm), + fsem_( fsem) + { + if ( lwm_ > hwm_ ) + throw invalid_watermark(); + } + + bool active() const + { return active_(); } + + void deactivate() + { + unique_lock< shared_mutex > lk( mtx_); + deactivate_(); + not_full_cond_.notify_all(); + } + + bool empty() const + { + shared_lock< shared_mutex > lk( mtx_); + return empty_(); + } + + std::size_t upper_bound() const + { + shared_lock< shared_mutex > lk( mtx_); + return hwm_; + } + + std::size_t lower_bound() const + { + shared_lock< shared_mutex > lk( mtx_); + return lwm_; + } + + void put( value_type const& va) + { + unique_lock< shared_mutex > lk( mtx_); + put_( va, lk); + } + + template< typename TimeDuration > + void put( + value_type const& va, + TimeDuration const& rel_time) + { + unique_lock< shared_mutex > lk( mtx_); + put_( va, rel_time, lk); + } + + bool try_take( callable & ca) + { + unique_lock< shared_mutex > lk( mtx_); + return try_take_( ca); + } +}; + +}} + +#include + +#endif // BOOST_TASKS_BOUNDED_SMART_QUEUE_H diff --git a/boost/task/callable.hpp b/boost/task/callable.hpp new file mode 100755 index 00000000..379ce4bf --- /dev/null +++ b/boost/task/callable.hpp @@ -0,0 +1,165 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_CALLABLE_H +#define BOOST_TASKS_CALLABLE_H + +#include +#include +#include +#include +#include + +#include +#include + +#include + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251 4275) +# endif + +namespace boost { +namespace tasks { +namespace detail { + +struct BOOST_TASKS_DECL callable_base +{ + atomic< unsigned int > use_count; + + callable_base() : + use_count( 0) + {} + + virtual ~callable_base() {} + + virtual void run() = 0; + + virtual void reset( shared_ptr< thread > const&) = 0; + + inline friend void intrusive_ptr_add_ref( callable_base * p) + { p->use_count.fetch_add( 1, memory_order_relaxed); } + + inline friend void intrusive_ptr_release( callable_base * p) + { + if ( p->use_count.fetch_sub( 1, memory_order_release) == 1) + { + atomic_thread_fence( memory_order_acquire); + delete p; + } + } +}; + +template< typename Task, typename Promise > +class callable_object : public callable_base +{ +private: + Task t_; + context ctx_; + +public: +#ifdef BOOST_HAS_RVALUE_REFS + callable_object( + Task && t, + Promise && prom, + context const& ctx) : + t_( t), ctx_( ctx) + { t_.set_promise( prom); } +#else + callable_object( + BOOST_RV_REF( Task) t, + boost::detail::thread_move_t< Promise > prom, + context const& ctx) : + t_( t), ctx_( ctx) + { t_.set_promise( prom); } +#endif + + callable_object( + BOOST_RV_REF( Task) t, + BOOST_RV_REF( Promise) prom, + context const& ctx) : + t_( t), ctx_( ctx) + { t_.set_promise( prom); } + + void run() + { t_(); } + + void reset( shared_ptr< thread > const& thrd) + { ctx_.reset( thrd); } +}; + +} + +class BOOST_TASKS_DECL callable +{ +private: + intrusive_ptr< detail::callable_base > base_; + +public: + callable(); + +#ifdef BOOST_HAS_RVALUE_REFS + template< typename Task, typename Promise > + callable( + Task && t, + Promise && prom, + context const& ctx) : + base_( new detail::callable_object< Task, Promise >( t, prom, ctx) ) + {} +#else + template< typename Task, typename Promise > + callable( + BOOST_RV_REF( Task) t, + boost::detail::thread_move_t< Promise > prom, + context const& ctx) : + base_( new detail::callable_object< Task, Promise >( t, prom, ctx) ) + {} +#endif + + template< typename Task, typename Promise > + callable( + BOOST_RV_REF( Task) t, + BOOST_RV_REF( Promise) prom, + context const& ctx) : + base_( new detail::callable_object< Task, Promise >( t, prom, ctx) ) + {} + + void operator()(); + + bool empty() const; + + void clear(); + + void reset( shared_ptr< thread > const&); + + void swap( callable &); +}; + +class context_guard : private noncopyable +{ +private: + callable & ca_; + +public: + context_guard( callable & ca, shared_ptr< thread > const& thrd) : + ca_( ca) + { ca_.reset( thrd); } + + ~context_guard() + { ca_.clear(); } +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#include + +#endif // BOOST_TASKS_CALLABLE_H + diff --git a/boost/task/context.hpp b/boost/task/context.hpp new file mode 100644 index 00000000..f85bbd53 --- /dev/null +++ b/boost/task/context.hpp @@ -0,0 +1,94 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_CONTEXT_H +#define BOOST_TASKS_CONTEXT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251 4275) +# endif + +namespace boost { +namespace tasks { +namespace detail { + +class BOOST_TASKS_DECL context_base : private noncopyable +{ +private: + atomic< unsigned int > use_count_; + bool requested_; + mutex mtx_; + shared_ptr< thread > thrd_; + + void reset_( shared_ptr< thread > const& thrd); + + void interrupt_(); + +public: + context_base(); + + void reset( shared_ptr< thread > const& thrd); + + void interrupt(); + + bool interruption_requested(); + + inline friend void intrusive_ptr_add_ref( context_base * p) + { p->use_count_.fetch_add( 1, memory_order_relaxed); } + + inline friend void intrusive_ptr_release( context_base * p) + { + if ( p->use_count_.fetch_sub( 1, memory_order_release) == 1) + { + atomic_thread_fence( memory_order_acquire); + delete p; + } + } +}; + +} + +class BOOST_TASKS_DECL context +{ +private: + intrusive_ptr< detail::context_base > base_; + +public: + context(); + + void reset( shared_ptr< thread > const& thrd); + + void interrupt(); + + bool interruption_requested(); + + void swap( context & other); +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#include + +#endif // BOOST_TASKS_DETAIL_context_H diff --git a/boost/task/detail/bind_processor.hpp b/boost/task/detail/bind_processor.hpp new file mode 100644 index 00000000..01b7d0ef --- /dev/null +++ b/boost/task/detail/bind_processor.hpp @@ -0,0 +1,37 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_DETAIL_BIND_PROCESSOR_H +#define BOOST_TASKS_DETAIL_BIND_PROCESSOR_H + +#include + +# if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# define BOOST_HAS_PROCESSOR_BINDINGS 1 +# include +# elif defined(linux) || defined(__linux) || defined(__linux__) +# define BOOST_HAS_PROCESSOR_BINDINGS 1 +# include +# elif defined(__IBMCPP__) || defined(_AIX) +# define BOOST_HAS_PROCESSOR_BINDINGS 1 +# include +# elif defined(__hpux) +# define BOOST_HAS_PROCESSOR_BINDINGS 1 +# include +# elif defined(sun) || defined(__sun) +# define BOOST_HAS_PROCESSOR_BINDINGS 1 +# include +# elif defined(__FreeBSD__) +#include +# if (__FreeBSD_version >= 701000) +# define BOOST_HAS_PROCESSOR_BINDINGS 1 +# include +# endif +# else +# undef BOOST_HAS_PROCESSOR_BINDINGS +# endif + +#endif // BOOST_TASKS_DETAIL_BIND_PROCESSOR_H diff --git a/boost/task/detail/bind_processor_aix.hpp b/boost/task/detail/bind_processor_aix.hpp new file mode 100644 index 00000000..27d51f32 --- /dev/null +++ b/boost/task/detail/bind_processor_aix.hpp @@ -0,0 +1,52 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_DETAIL_BIND_PROCESSOR_AIX_H +#define BOOST_TASKS_DETAIL_BIND_PROCESSOR_AIX_H + +extern "C" +{ +#include +#include +} + +#include +#include +#include + +#include + +namespace boost { +namespace this_thread { + +inline +void bind_to_processor( unsigned int n) +{ + BOOST_ASSERT( n >= 0); + BOOST_ASSERT( n < boost::thread::hardware_concurrency() ); + + if ( ::bindprocessor( BINDTHREAD, ::thread_self(), static_cast< cpu_t >( n) ) == -1) + throw boost::system::system_error( + boost::system::error_code( + errno, + boost::system::system_category() ) ); +} + +inline +void bind_to_any_processor() +{ + if ( ::bindprocessor( BINDTHREAD, ::thread_self(), PROCESSOR_CLASS_ANY) == -1) + throw boost::system::system_error( + boost::system::error_code( + errno, + boost::system::system_category() ) ); +} + +}} + +#include + +#endif // BOOST_TASKS_DETAIL_BIND_PROCESSOR_AIX_H diff --git a/boost/task/detail/bind_processor_freebsd.hpp b/boost/task/detail/bind_processor_freebsd.hpp new file mode 100755 index 00000000..0f577ab5 --- /dev/null +++ b/boost/task/detail/bind_processor_freebsd.hpp @@ -0,0 +1,63 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_DETAIL_BIND_PROCESSOR_FREEBSD_H +#define BOOST_TASKS_DETAIL_BIND_PROCESSOR_FREEBSD_H + +extern "C" +{ +#include +#include +} + +#include +#include +#include + +#include + +namespace boost { +namespace this_thread { + +inline +void bind_to_processor( unsigned int n) +{ + BOOST_ASSERT( n >= 0); + BOOST_ASSERT( n < boost::thread::hardware_concurrency() ); + + cpuset_t cpuset; + CPU_ZERO( & cpuset); + CPU_SET( n, & cpuset); + + if ( ::cpuset_setaffinity( CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof( cpuset), & cpuset) == -1) + throw boost::system::system_error( + boost::system::error_code( + errno, + boost::system::system_category() ) ); +} + +inline +void bind_to_any_processor() +{ + cpuset_t cpuset; + CPU_ZERO( & cpuset); + + unsigned int max( boost::thread::hardware_concurrency() ); + for ( unsigned int i( 0); i < max; ++i) + CPU_SET( i, & cpuset); + + if ( ::cpuset_setaffinity( CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof( cpuset), & cpuset) == -1) + throw boost::system::system_error( + boost::system::error_code( + errno, + boost::system::system_category() ) ); +} + +}} + +#include + +#endif // BOOST_TASKS_DETAIL_BIND_PROCESSOR_FREEBSD_H diff --git a/boost/task/detail/bind_processor_hpux.hpp b/boost/task/detail/bind_processor_hpux.hpp new file mode 100644 index 00000000..7a816097 --- /dev/null +++ b/boost/task/detail/bind_processor_hpux.hpp @@ -0,0 +1,65 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_DETAIL_BIND_PROCESSOR_HPUX_H +#define BOOST_TASKS_DETAIL_BIND_PROCESSOR_HPUX_H + +extern "C" +{ +#include +} + +#include +#include +#include + +#include + +namespace boost { +namespace this_thread { + +inline +void bind_to_processor( unsigned int n) +{ + BOOST_ASSERT( n >= 0); + BOOST_ASSERT( n < boost::thread::hardware_concurrency() ); + + ::pthread_spu_t spu; + int errno_( + ::pthread_processor_bind_np( + PTHREAD_BIND_FORCED_NP, + & spu, + static_cast< pthread_spu_t >( n), + PTHREAD_SELFTID_NP) ); + if ( errno_ != 0) + throw boost::system::system_error( + boost::system::error_code( + errno_, + boost::system::system_category() ) ); +} + +inline +void bind_to_any_processor() +{ + ::pthread_spu_t spu; + int errno_( + ::pthread_processor_bind_np( + PTHREAD_BIND_FORCED_NP, + & spu, + PTHREAD_SPUFLOAT_NP, + PTHREAD_SELFTID_NP) ); + if ( errno_ != 0) + throw boost::system::system_error( + boost::system::error_code( + errno_, + boost::system::system_category() ) ); +} + +}} + +#include + +#endif // BOOST_TASKS_DETAIL_BIND_PROCESSOR_HPUX_H diff --git a/boost/task/detail/bind_processor_linux.hpp b/boost/task/detail/bind_processor_linux.hpp new file mode 100644 index 00000000..13e09385 --- /dev/null +++ b/boost/task/detail/bind_processor_linux.hpp @@ -0,0 +1,66 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_DETAIL_BIND_PRCESSOR_LINUX_H +#define BOOST_TASKS_DETAIL_BIND_PRCESSOR_LINUX_H + +extern "C" +{ +#include +#include +} + +#include +#include +#include + +#include + +namespace boost { +namespace this_thread { + +inline +void bind_to_processor( unsigned int n) +{ + BOOST_ASSERT( n >= 0); + BOOST_ASSERT( n < CPU_SETSIZE); + BOOST_ASSERT( n < boost::thread::hardware_concurrency() ); + + cpu_set_t cpuset; + CPU_ZERO( & cpuset); + CPU_SET( n, & cpuset); + + int errno_( ::pthread_setaffinity_np( ::pthread_self(), sizeof( cpuset), & cpuset) ); + if ( errno_ != 0) + throw boost::system::system_error( + boost::system::error_code( + errno_, + boost::system::system_category() ) ); +} + +inline +void bind_to_any_processor() +{ + cpu_set_t cpuset; + CPU_ZERO( & cpuset); + + unsigned int max( boost::thread::hardware_concurrency() ); + for ( unsigned int i( 0); i < max; ++i) + CPU_SET( i, & cpuset); + + int errno_( ::pthread_setaffinity_np( ::pthread_self(), sizeof( cpuset), & cpuset) ); + if ( errno_ != 0) + throw boost::system::system_error( + boost::system::error_code( + errno_, + boost::system::system_category() ) ); +} + +}} + +#include + +#endif // BOOST_TASKS_DETAIL_BIND_PRCESSOR_LINUX_H diff --git a/boost/task/detail/bind_processor_solaris.hpp b/boost/task/detail/bind_processor_solaris.hpp new file mode 100644 index 00000000..eaa40e84 --- /dev/null +++ b/boost/task/detail/bind_processor_solaris.hpp @@ -0,0 +1,53 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_DETAIL_BIND_PROCESSOR_SOLARIS_H +#define BOOST_TASKS_DETAIL_BIND_PROCESSOR_SOLARIS_H + +extern "C" +{ +#include +#include +#include +} + +#include +#include +#include + +#include + +namespace boost { +namespace this_thread { + +inline +void bind_to_processor( unsigned int n) +{ + BOOST_ASSERT( n >= 0); + BOOST_ASSERT( n < boost::thread::hardware_concurrency() ); + + if ( ::processor_bind( P_LWPID, P_MYID, static_cast< processorid_t >( n), 0) == -1) + throw boost::system::system_error( + boost::system::error_code( + errno, + boost::system::system_category() ) ); +} + +inline +void bind_to_any_processor() +{ + if ( ::processor_bind( P_LWPID, P_MYID, PBIND_NONE, 0) == -1) + throw boost::system::system_error( + boost::system::error_code( + errno, + boost::system::system_category() ) ); +} + +}} + +#include + +#endif // BOOST_TASKS_DETAIL_BIND_PROCESSOR_SOLARIS_H diff --git a/boost/task/detail/bind_processor_windows.hpp b/boost/task/detail/bind_processor_windows.hpp new file mode 100644 index 00000000..c864c08c --- /dev/null +++ b/boost/task/detail/bind_processor_windows.hpp @@ -0,0 +1,55 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_DETAIL_BIND_PROCESSOR_WINDOWS_H +#define BOOST_TASKS_DETAIL_BIND_PROCESSOR_WINDOWS_H + +extern "C" +{ +#include +} + +#include +#include +#include + +#include + +namespace boost { +namespace this_thread { + +inline +void bind_to_processor( unsigned int n) +{ + BOOST_ASSERT( n >= 0); + BOOST_ASSERT( n < boost::thread::hardware_concurrency() ); + + if ( ::SetThreadAffinityMask( ::GetCurrentThread(), ( DWORD_PTR)1 << n) == 0) + throw boost::system::system_error( + boost::system::error_code( + ::GetLastError(), + boost::system::system_category() ) ); +} + +inline +void bind_to_any_processor() +{ + DWORD_PTR ptr( 1); + for ( unsigned int i( 0); i < boost::thread::hardware_concurrency(); ++i) + ptr = ptr << i; + + if ( ::SetThreadAffinityMask( ::GetCurrentThread(), ptr) == 0) + throw boost::system::system_error( + boost::system::error_code( + ::GetLastError(), + boost::system::system_category() ) ); +} + +}} + +#include + +#endif // BOOST_TASKS_DETAIL_BIND_PROCESSOR_WINDOWS_H diff --git a/boost/task/detail/config.hpp b/boost/task/detail/config.hpp new file mode 100644 index 00000000..f1629b29 --- /dev/null +++ b/boost/task/detail/config.hpp @@ -0,0 +1,80 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// this file is based on config.hpp of boost.thread + +#ifndef BOOST_TASKS_DETAIL_CONFIG_H +#define BOOST_TASKS_DETAIL_CONFIG_H + +#include +#include + +# if BOOST_WORKAROUND(__BORLANDC__, < 0x600) +# pragma warn -8008 // Condition always true/false +# pragma warn -8080 // Identifier declared but never used +# pragma warn -8057 // Parameter never used +# pragma warn -8066 // Unreachable code +# endif + +# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_TASKS_DYN_LINK) +# undef BOOST_TASKS_USE_LIB +# define BOOST_TASKS_USE_DLL +# endif + +# if defined(BOOST_TASKS_BUILD_DLL) //Build dll +# elif defined(BOOST_TASKS_BUILD_LIB) //Build lib +# elif defined(BOOST_TASKS_USE_DLL) //Use dll +# elif defined(BOOST_TASKS_USE_LIB) //Use lib +# else //Use default +# if defined(BOOST_TASKS_PLATFORM_WIN32) +# if defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN) + //For compilers supporting auto-tss cleanup + //with Boost.Threads lib, use Boost.Threads lib +# define BOOST_TASKS_USE_LIB +# else + //For compilers not yet supporting auto-tss cleanup + //with Boost.Threads lib, use Boost.Threads dll +# define BOOST_TASKS_USE_DLL +# endif +# else +# define BOOST_TASKS_USE_LIB +# endif +# endif + +# if defined(BOOST_HAS_DECLSPEC) +# if defined(BOOST_TASKS_BUILD_DLL) //Build dll +# define BOOST_TASKS_DECL __declspec(dllexport) +# elif defined(BOOST_TASKS_USE_DLL) //Use dll +# define BOOST_TASKS_DECL __declspec(dllimport) +# else +# define BOOST_TASKS_DECL +# endif +# else +# define BOOST_TASKS_DECL +# endif + +// Automatically link to the correct build variant where possible. +# if ! defined(BOOST_ALL_NO_LIB) && ! defined(BOOST_TASKS_NO_LIB) && ! defined(BOOST_TASKS_BUILD_DLL) && ! defined(BOOST_TASKS_BUILD_LIB) + +// Tell the autolink to link dynamically, this will get undef'ed by auto_link.hpp +# if defined(BOOST_TASKS_USE_DLL) +# define BOOST_DYN_LINK +# endif + +// Set the name of our library, this will get undef'ed by auto_link.hpp +# if defined(BOOST_TASKS_LIB_NAME) +# define BOOST_LIB_NAME BOOST_TASKS_LIB_NAME +# else +# define BOOST_LIB_NAME boost_task +# endif + +// If we're importing code from a dll, then tell auto_link.hpp about it +// And include the header that does the work +#include +# endif // auto-linking disabled + +#endif // BOOST_TASKS_DETAIL_CONFIG_H + diff --git a/boost/task/detail/future_traits.hpp b/boost/task/detail/future_traits.hpp new file mode 100644 index 00000000..ecd1970b --- /dev/null +++ b/boost/task/detail/future_traits.hpp @@ -0,0 +1,82 @@ +// (C) Copyright 2008-9 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_DETAIL_FUTURE_TRAITSHPP +#define BOOST_TASKS_DETAIL_FUTURE_TRAITSHPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace tasks { +namespace detail { + +template +struct future_traits +{ + typedef boost::scoped_ptr storage_type; +#ifdef BOOST_HAS_RVALUE_REFS + typedef T const& source_reference_type; + struct dummy; + typedef typename boost::mpl::if_,dummy&,T&&>::type rvalue_source_type; + typedef typename boost::mpl::if_,T,T&&>::type move_dest_type; +#else + typedef T& source_reference_type; + typedef typename boost::mpl::if_, BOOST_RV_REF( T),T const&>::type rvalue_source_type; + typedef typename boost::mpl::if_,BOOST_RV_REF( T),T>::type move_dest_type; +#endif + + static void init(storage_type& storage,source_reference_type t) + { storage.reset(new T(t)); } + + static void init(storage_type& storage,rvalue_source_type t) + { storage.reset(new T(static_cast(t))); } + + static void cleanup(storage_type& storage) + { storage.reset(); } +}; + +template +struct future_traits +{ + typedef T* storage_type; + typedef T& source_reference_type; + struct rvalue_source_type {}; + typedef T& move_dest_type; + + static void init(storage_type& storage,T& t) + { storage=&t; } + + static void cleanup(storage_type& storage) + { storage=0; } +}; + +template<> +struct future_traits +{ + typedef bool storage_type; + typedef void move_dest_type; + + static void init(storage_type& storage) + { storage=true; } + + static void cleanup(storage_type& storage) + { storage=false; } +}; + +}}} + +#endif // BOOST_TASKS_DETAIL_FUTURE_TRAITS_H diff --git a/boost/task/detail/meta.hpp b/boost/task/detail/meta.hpp new file mode 100644 index 00000000..b3639d20 --- /dev/null +++ b/boost/task/detail/meta.hpp @@ -0,0 +1,27 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_DETAIL_INFO_H +#define BOOST_TASKS_DETAIL_INFO_H + +#include + +namespace boost { +namespace tasks { +namespace detail { + +struct has_attribute +{}; + +struct has_no_attribute +{}; + +}}} + +#include + +#endif // BOOST_TASKS_DETAIL_INFO_H + diff --git a/boost/task/detail/pool_base.hpp b/boost/task/detail/pool_base.hpp new file mode 100755 index 00000000..d17baeba --- /dev/null +++ b/boost/task/detail/pool_base.hpp @@ -0,0 +1,369 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_DETAIL_POOL_BASE_H +#define BOOST_TASKS_DETAIL_POOL_BASE_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasks { +namespace detail { + +template< + typename Queue, + typename UMS +> +class pool_base +{ +private: + friend class worker; + template< typename T, typename Z > + friend class worker_object; + template< typename T, typename Z > + friend void intrusive_ptr_add_ref( pool_base< T, Z > * p); + template< typename T, typename Z > + friend void intrusive_ptr_release( pool_base< T, Z > * p); + + typedef Queue queue_type; + typedef typename queue_type::value_type value_type; + typedef UMS ums_type; + + enum state + { + ACTIVE = 0, + DEACTIVE + }; + + fast_semaphore fsem_; + atomic< unsigned int > use_count_; + worker_group wg_; + shared_mutex mtx_wg_; + atomic< state > state_; + queue_type queue_; + atomic< bool > shtdwn_; + atomic< bool > shtdwn_now_; + + void worker_entry_() + { + shared_lock< shared_mutex > lk( mtx_wg_); + typename detail::worker_group::iterator i( wg_.find( this_thread::get_id() ) ); + lk.unlock(); + BOOST_ASSERT( i != wg_.end() ); + + worker w( * i); + w.run(); + } + + void create_worker_( + poolsize const& psize, + stacksize const& stack_size) + { + wg_.insert( + worker( + * this, + psize, + stack_size, + boost::bind( + & pool_base::worker_entry_, + this) ) ); + } + +# if defined(BOOST_HAS_PROCESSOR_BINDINGS) + void worker_entry_( std::size_t n) + { + this_thread::bind_to_processor( n); + worker_entry_(); + } + + void create_worker_( + poolsize const& psize, + stacksize const& stack_size, + std::size_t n) + { + wg_.insert( + worker( + * this, + psize, + stack_size, + boost::bind( + & pool_base::worker_entry_, + this, + n) ) ); + } +# endif + + std::size_t size_() const + { return wg_.size(); } + + bool deactivated_() const + { return DEACTIVE == state_.load(); } + + bool deactivate_() + { return ACTIVE == state_.exchange( DEACTIVE); } + +public: + explicit pool_base( + poolsize const& psize, + stacksize const& stack_size) : + fsem_( 0), + use_count_( 0), + wg_(), + mtx_wg_(), + state_( ACTIVE), + queue_( fsem_), + shtdwn_( false), + shtdwn_now_( false) + { + lock_guard< shared_mutex > lk( mtx_wg_); + for ( std::size_t i( 0); i < psize; ++i) + create_worker_( psize, stack_size); + } + + explicit pool_base( + poolsize const& psize, + high_watermark const& hwm, + low_watermark const& lwm, + stacksize const& stack_size) : + fsem_( 0), + use_count_( 0), + wg_(), + mtx_wg_(), + state_( ACTIVE), + queue_( fsem_, hwm, lwm), + shtdwn_( false), + shtdwn_now_( false) + { + lock_guard< shared_mutex > lk( mtx_wg_); + for ( std::size_t i( 0); i < psize; ++i) + create_worker_( psize, stack_size); + } + +# if defined(BOOST_HAS_PROCESSOR_BINDINGS) + explicit pool_base( stacksize const& stack_size) : + fsem_( 0), + use_count_( 0), + wg_(), + mtx_wg_(), + state_( ACTIVE), + queue_( fsem_), + shtdwn_( false), + shtdwn_now_( false) + { + poolsize psize( thread::hardware_concurrency() ); + BOOST_ASSERT( psize > 0); + lock_guard< shared_mutex > lk( mtx_wg_); + for ( std::size_t i( 0); i < psize; ++i) + create_worker_( psize, stack_size, i); + } + + explicit pool_base( + high_watermark const& hwm, + low_watermark const& lwm, + stacksize const& stack_size) : + fsem_( 0), + use_count_( 0), + wg_(), + mtx_wg_(), + state_( ACTIVE), + queue_( fsem_, hwm, lwm), + shtdwn_( false), + shtdwn_now_( false) + { + poolsize psize( thread::hardware_concurrency() ); + BOOST_ASSERT( psize > 0); + lock_guard< shared_mutex > lk( mtx_wg_); + for ( std::size_t i( 0); i < psize; ++i) + create_worker_( psize, stack_size, i); + } +# endif + + ~pool_base() + { shutdown(); } + + void interrupt_all_worker() + { + if ( deactivated_() ) return; + + shared_lock< shared_mutex > lk( mtx_wg_); + wg_.interrupt_all(); + } + + void shutdown() + { + if ( deactivated_() || ! deactivate_() ) return; + + queue_.deactivate(); + fsem_.deactivate(); + shared_lock< shared_mutex > lk( mtx_wg_); + shtdwn_.store( true); + wg_.join_all(); + } + + const void shutdown_now() + { + if ( deactivated_() || ! deactivate_() ) return; + + queue_.deactivate(); + fsem_.deactivate(); + shared_lock< shared_mutex > lk( mtx_wg_); + shtdwn_now_.store( true); + wg_.interrupt_all(); + wg_.join_all(); + } + + std::size_t size() + { + shared_lock< shared_mutex > lk( mtx_wg_); + return size_(); + } + + bool closed() + { return deactivated_(); } + + std::size_t upper_bound() + { return queue_.upper_bound(); } + + void upper_bound( high_watermark const& hwm) + { queue_.upper_bound( hwm); } + + std::size_t lower_bound() + { return queue_.lower_bound(); } + + void lower_bound( low_watermark const lwm) + { queue_.lower_bound( lwm); } + + template< typename R > + handle< R > submit( BOOST_RV_REF( task< R >) t) + { + if ( deactivated_() ) + throw task_rejected("pool is closed"); + + if ( this_task::runs_in_pool() ) + { + spin::promise< R > prom; + spin::shared_future< R > f( prom.get_future() ); + context ctx; + handle< R > h( f, ctx); + queue_.put( callable( t, boost::move( prom), ctx) ); + return h; + } + else + { + promise< R > prom; + shared_future< R > f( prom.get_future() ); + context ctx; + handle< R > h( f, ctx); + queue_.put( + callable( + t, +// TODO: workaround because thread_move_t will be abigous for move +#ifdef BOOST_HAS_RVALUE_REFS + boost::move( prom), +#else + boost::detail::thread_move_t< promise< R > >( prom), +#endif + ctx) ); + return h; + } + } + + template< typename R, typename Attr > + handle< R > submit( BOOST_RV_REF( task< R >) t, Attr const& attr) + { + if ( deactivated_() ) + throw task_rejected("pool is closed"); + + if ( this_task::runs_in_pool() ) + { + spin::promise< R > prom; + spin::shared_future< R > f( prom.get_future() ); + context ctx; + handle< R > h( f, ctx); + queue_.put( + value_type( + callable( t, boost::move( prom), ctx), + attr) ); + return h; + } + else + { + promise< R > prom; + shared_future< R > f( prom.get_future() ); + // TODO: if boost.thread uses boost.move + // use boost::move() + context ctx; + handle< R > h( f, ctx); + queue_.put( + value_type( + callable( + t, +// TODO: workaround because thread_move_t will be abigous for move +#ifdef BOOST_HAS_RVALUE_REFS + boost::move( prom), +#else + boost::detail::thread_move_t< promise< R > >( prom), +#endif + ctx), + attr) ); + return h; + } + } +}; + +template< + typename Queue, + typename UMS +> +void intrusive_ptr_add_ref( pool_base< Queue, UMS > * p) +{ p->use_count_.fetch_add( 1, memory_order_relaxed); } + +template< + typename Queue, + typename UMS +> +void intrusive_ptr_release( pool_base< Queue, UMS > * p) +{ + if ( p->use_count_.fetch_sub( 1, memory_order_release) == 1) + { + atomic_thread_fence( memory_order_acquire); + delete p; + } +} + +}}} + +#include + +#endif // BOOST_TASKS_DETAIL_POOL_BASE_H + diff --git a/boost/task/detail/smart.hpp b/boost/task/detail/smart.hpp new file mode 100755 index 00000000..2aa1947c --- /dev/null +++ b/boost/task/detail/smart.hpp @@ -0,0 +1,67 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_DETAIL_SMART_H +#define BOOST_DETAIL_SMART_H + +#include + +#include + +namespace boost { +namespace tasks { +namespace detail { + +struct replace_oldest +{ + template< + typename PrioIndex, + typename Value + > + void operator()( PrioIndex & idx, Value const& va) + { + typedef typename PrioIndex::iterator iterator; + iterator i( idx.find( va.attr) ); + if ( i == idx.end() ) + idx.insert( va); + else + idx.replace( i, va); + } +}; + +struct take_oldest +{ + class swapper + { + private: + callable & ca_; + + public: + swapper( callable & ca) : + ca_( ca) + {} + + template< typename Value > + void operator()( Value & va) + { ca_.swap( va.ca); } + }; + + template< typename PrioIndex > + void operator()( PrioIndex & idx, callable & ca) + { + typedef typename PrioIndex::iterator iterator; + iterator i( idx.begin() ); + BOOST_ASSERT( i != idx.end() ); + idx.modify( i, swapper( ca) ); + idx.erase( i); + } +}; + +}}} + +#include + +#endif // BOOST_TASKS_DETAIL_SMART_H diff --git a/boost/task/detail/worker.hpp b/boost/task/detail/worker.hpp new file mode 100644 index 00000000..45c6a7f7 --- /dev/null +++ b/boost/task/detail/worker.hpp @@ -0,0 +1,279 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_DETAIL_WORKER_H +#define BOOST_TASKS_DETAIL_WORKER_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251 4275) +# endif + +namespace boost { +namespace tasks { +namespace detail { + +struct worker_base +{ + virtual ~worker_base() {} + + virtual const thread::id get_id() const = 0; + + virtual void join() const = 0; + + virtual void interrupt() const = 0; + + virtual void put( callable const&) = 0; + + virtual bool try_steal( callable &) = 0; + + virtual void run() = 0; + + virtual void yield() = 0; +}; + +template< + typename Pool, + typename Worker +> +class worker_object : public worker_base, + private noncopyable +{ +private: + class random_idx + { + private: + rand48 rng_; + uniform_int<> six_; + variate_generator< rand48 &, uniform_int<> > die_; + + public: + random_idx( std::size_t size) : + rng_(), + six_( 0, size - 1), + die_( rng_, six_) + {} + + std::size_t operator()() + { return die_(); } + }; + + typedef shared_ptr< thread > thread_t; + + Pool & pool_; + thread_t thrd_; + tasklets::scheduler< + typename Pool::ums_type > * sched_;// TODO: make not as pointer if WIN32 Fiber API is replaced by assembler + + wsq wsq_; + bool shtdwn_; + std::size_t stack_size_; + random_idx rnd_idx_; + + void execute_( callable & ca) + { + BOOST_ASSERT( ! ca.empty() ); + { + context_guard lk( ca, thrd_); + ca(); + } + ca.clear(); + BOOST_ASSERT( ca.empty() ); + } + + bool try_take_global_callable_( callable & ca) + { return pool_.queue_.try_take( ca); } + + bool try_take_local_callable_( callable & ca) + { return wsq_.try_take( ca); } + + bool try_steal_other_callable_( callable & ca) + { + std::size_t idx( rnd_idx_() ); + for ( std::size_t j( 0); j < pool_.wg_.size(); ++j) + { + Worker other( pool_.wg_[idx]); + if ( this_thread::get_id() == other.get_id() ) continue; + if ( ++idx >= pool_.wg_.size() ) idx = 0; + if ( other.try_steal( ca) ) + return true; + } + return false; + } + + void run_() + { + while ( ! shutdown_() ) + { + callable ca; + if ( try_take_local_callable_( ca) || + try_steal_other_callable_( ca) || + try_take_global_callable_( ca) ) + { + execute_( ca); + if ( 0 < sched_->ready() ) return; + } + else + { + if ( 0 < sched_->ready() ) return; + pool_.fsem_.wait(); + } + } + } + + bool shutdown_() + { + if ( shutdown__() && pool_.queue_.empty() && 0 == sched_->ready() ) + return true; + else if ( shutdown_now__() ) + return true; + return false; + } + + bool shutdown__() + { + if ( ! shtdwn_) + shtdwn_ = pool_.shtdwn_; + return shtdwn_; + } + + bool shutdown_now__() + { return pool_.shtdwn_now_; } + +public: + worker_object( + Pool & pool, + poolsize const& psize, + stacksize const& stack_size, + function< void() > const& fn) : + pool_( pool), + thrd_( new thread( fn) ), + sched_(), + wsq_( pool_.fsem_), + shtdwn_( false), + stack_size_( stack_size), + rnd_idx_( psize) + { BOOST_ASSERT( ! fn.empty() ); } + + ~worker_object() + { delete sched_; } + + const thread::id get_id() const + { return thrd_->get_id(); } + + void join() const + { thrd_->join(); } + + void + interrupt() const + { thrd_->interrupt(); } + + void put( callable const& ca) + { + BOOST_ASSERT( ! ca.empty() ); + wsq_.put( ca); + } + + bool try_steal( callable & ca) + { return wsq_.try_steal( ca); } + + void run() + { +// TODO: remove if WIN32 Fiber API is replaced by assembler + sched_ = new tasklets::scheduler< typename Pool::ums_type >(); + + BOOST_ASSERT( get_id() == this_thread::get_id() ); + + sched_->submit_tasklet( + tasklets::make_tasklet( + bind( + & worker_object::run_, + this), + stack_size_) ); + while ( ! shutdown_() ) + sched_->run(); + } + + void yield() + { + sched_->submit_tasklet( + tasklets::make_tasklet( + bind( + & worker_object::run_, + this), + stack_size_) ); + this_tasklet::yield(); + } +}; + +class BOOST_TASKS_DECL worker +{ +private: + static thread_specific_ptr< worker > tss_; + + shared_ptr< worker_base > impl_; + +public: + template< typename Pool > + worker( + Pool & pool, + poolsize const& psize, + stacksize const& stack_size, + function< void() > const& fn) : + impl_( + new worker_object< Pool, worker >( + pool, + psize, + stack_size, + fn) ) + {} + + const thread::id get_id() const; + + void join() const; + + void interrupt() const; + + void put( callable const&); + + bool try_steal( callable &); + + void run(); + + void yield(); + + static worker * tss_get(); +}; + +}}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#include + +#endif // BOOST_TASKS_DETAIL_WORKER_H + diff --git a/boost/task/detail/worker_group.hpp b/boost/task/detail/worker_group.hpp new file mode 100644 index 00000000..db917544 --- /dev/null +++ b/boost/task/detail/worker_group.hpp @@ -0,0 +1,101 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_DETAIL_WORKER_GROUP_H +#define BOOST_TASKS_DETAIL_WORKER_GROUP_H + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251 4275) +# endif + +namespace boost { +namespace tasks { +namespace detail { + +class BOOST_TASKS_DECL worker_group +{ +private: + struct id_idx_tag {}; + struct rnd_idx_tag {}; + + typedef multi_index::multi_index_container< + worker, + multi_index::indexed_by< + multi_index::ordered_unique< + multi_index::tag< id_idx_tag >, + multi_index::const_mem_fun< + worker, + const thread::id, + & worker::get_id + > + >, + multi_index::random_access< multi_index::tag< rnd_idx_tag > > + > + > container; + + typedef container::index< id_idx_tag >::type id_idx; + typedef container::index< rnd_idx_tag >::type rnd_idx; + + container cont_; + id_idx & id_idx_; + rnd_idx & rnd_idx_; + + +public: + typedef id_idx::iterator iterator; + typedef id_idx::const_iterator const_iterator; + + worker_group(); + + ~worker_group(); + + std::size_t size() const; + + bool empty() const; + + const worker operator[]( std::size_t pos) const; + + const iterator begin(); + const const_iterator begin() const; + + const iterator end(); + const const_iterator end() const; + + const const_iterator find( thread::id const& id) const; + + void insert( worker const& w); + + iterator erase( iterator const& i); + + void join_all(); + + void interrupt_all(); +}; + +}}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#include + +#endif // BOOST_TASKS_DETAIL_WORKER_GROUP_H + diff --git a/boost/task/detail/wsq.hpp b/boost/task/detail/wsq.hpp new file mode 100644 index 00000000..c5876195 --- /dev/null +++ b/boost/task/detail/wsq.hpp @@ -0,0 +1,65 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_DETAIL_WSQ_H +#define BOOST_TASKS_DETAIL_WSQ_H + +#include +#include +#include +#include + +#include +#include +#include + +#include + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251 4275) +# endif + +namespace boost { +namespace tasks { +namespace detail { + +class BOOST_TASKS_DECL wsq : private noncopyable +{ +private: + const int initial_size_; + shared_array< callable > array_; + int capacity_; + int mask_; + atomic< unsigned int > head_idx_; + atomic< unsigned int > tail_idx_; + recursive_mutex mtx_; + fast_semaphore & fsem_; + +public: + wsq( fast_semaphore & fsem); + + bool empty() const; + + std::size_t size() const; + + void put( callable const&); + + bool try_take( callable &); + + bool try_steal( callable &); +}; + +}}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#include + +#endif // BOOST_TASKS_DETAIL_WSQ_H + diff --git a/boost/task/exceptions.hpp b/boost/task/exceptions.hpp new file mode 100644 index 00000000..5daeab86 --- /dev/null +++ b/boost/task/exceptions.hpp @@ -0,0 +1,105 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_EXCEPTIONS_H +#define BOOST_TASKS_EXCEPTIONS_H + +#include +#include + +#include + +namespace boost { +namespace tasks { + +class invalid_poolsize : public std::invalid_argument +{ +public: + invalid_poolsize() : + std::invalid_argument("core poolsize must be greater than zero") + {} +}; + +class invalid_stacksize : public std::invalid_argument +{ +public: + invalid_stacksize() : + std::invalid_argument("stacksize must be greater than zero") + {} +}; + +class invalid_watermark : public std::invalid_argument +{ +public: + invalid_watermark() : + std::invalid_argument("invalid watermark") + {} +}; + +class task_uninitialized : public std::logic_error +{ +public: + task_uninitialized() : + std::logic_error("task uninitialized") + {} +}; + +class task_already_executed : public std::logic_error +{ +public: + task_already_executed() : + std::logic_error("task already executed") + {} +}; + +class task_moved : public std::logic_error +{ +public: + task_moved() : + std::logic_error("task moved") + {} +}; + +class broken_task : public std::logic_error +{ +public: + broken_task() : + std::logic_error("broken task") + {} +}; + +struct task_interrupted +{}; + +class task_rejected : public std::runtime_error +{ +public: + task_rejected( std::string const& msg) : + std::runtime_error( msg) + {} +}; + +class pool_moved : public std::logic_error +{ +public: + pool_moved() : + std::logic_error("pool moved") + {} +}; + +class lock_error : public std::logic_error +{ +public: + lock_error() : + std::logic_error("lock invalid") + {} +}; + +}} + +#include + +#endif // BOOST_TASKS_EXCEPTIONS_H diff --git a/boost/task/fast_semaphore.hpp b/boost/task/fast_semaphore.hpp new file mode 100755 index 00000000..da91a272 --- /dev/null +++ b/boost/task/fast_semaphore.hpp @@ -0,0 +1,48 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASK_FAST_SEMAPHORE_H +#define BOOST_TASK_FAST_SEMAPHORE_H + +#include + +#include +#include + +#include + +#include + +namespace boost { +namespace tasks { + +class BOOST_TASKS_DECL fast_semaphore : private boost::noncopyable +{ +private: + unsigned int spin_count_; + atomic< int > sem_count_; + atomic< bool > sem_active_; + semaphore sem_; + +public: + fast_semaphore( int, unsigned int = 0); + + ~fast_semaphore(); + + void post( int = 1); + + void wait(); + + bool try_wait(); + + void deactivate(); +}; + +}} + +#include + +#endif // BOOST_TASK_FAST_SEMAPHORE_H diff --git a/boost/task/handle.hpp b/boost/task/handle.hpp new file mode 100644 index 00000000..d25854db --- /dev/null +++ b/boost/task/handle.hpp @@ -0,0 +1,285 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// parts are based on boost.future by Anthony Williams + +#ifndef BOOST_TASKS_HANDLE_H +#define BOOST_TASKS_HANDLE_H + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace boost { +namespace tasks { +namespace detail { + +template< typename R > +struct handle_base +{ + template< typename X > + friend void intrusive_ptr_add_ref( handle_base< X > *); + template< typename X > + friend void intrusive_ptr_release( handle_base< X > *); + + typedef intrusive_ptr< handle_base > ptr; + + atomic< unsigned int > use_count; + + virtual ~handle_base() {} + + virtual void interrupt() = 0; + + virtual void interrupt_and_wait() = 0; + + virtual bool interrupt_and_wait_until( system_time const& abs_time) = 0; + + virtual bool interruption_requested() = 0; + + virtual R get() = 0; + + virtual bool is_ready() const = 0; + + virtual bool has_value() const = 0; + + virtual bool has_exception() const = 0; + + virtual void wait() const = 0; + + virtual bool wait_until( system_time const& abs_time) const = 0; +}; + +template< typename R > +void intrusive_ptr_add_ref( handle_base< R > * p) +{ p->use_count.fetch_add( 1, memory_order_relaxed); } + +template< typename R > +void intrusive_ptr_release( handle_base< R > * p) +{ + if ( p->use_count.fetch_sub( 1, memory_order_release) == 1) + { + atomic_thread_fence( memory_order_acquire); + delete p; + } +} + +template< typename R, typename F > +class handle_object : public handle_base< R > +{ +private: + F fut_; + context ctx_; + +public: + handle_object() : + fut_(), + ctx_() + {} + + handle_object( + F const& fut, + context const& ctx) : + fut_( fut), + ctx_( ctx) + {} + + void interrupt() + { ctx_.interrupt(); } + + void interrupt_and_wait() + { + interrupt(); + wait(); + } + + bool interrupt_and_wait_until( system_time const& abs_time) + { + interrupt(); + return wait_until( abs_time); + } + + bool interruption_requested() + { return ctx_.interruption_requested(); } + + R get() + { + try + { return fut_.get(); } + catch ( future_uninitialized const&) + { throw task_uninitialized(); } + catch ( broken_promise const&) + { throw broken_task(); } + catch ( promise_already_satisfied const&) + { throw task_already_executed(); } + } + + bool is_ready() const + { return fut_.is_ready(); } + + bool has_value() const + { return fut_.has_value(); } + + bool has_exception() const + { return fut_.has_exception(); } + + void wait() const + { + try + { fut_.wait(); } + catch ( future_uninitialized const&) + { throw task_uninitialized(); } + catch ( broken_promise const&) + { throw broken_task(); } + catch ( thread_interrupted const&) + { throw task_interrupted(); } + } + + bool wait_until( system_time const& abs_time) const + { + try + { return fut_.timed_wait_until( abs_time); } + catch ( future_uninitialized const&) + { throw task_uninitialized(); } + catch ( broken_promise const&) + { throw broken_task(); } + catch ( thread_interrupted const&) + { throw task_interrupted(); } + } +}; + +} + +template< typename T > +struct is_handle_type; + +template< typename R > +class handle +{ +private: + intrusive_ptr< detail::handle_base< R > > base_; + +public: + handle() : + base_( new detail::handle_object< R, spin::shared_future< R > >() ) + {} + + template< typename F > + handle( + F const& fut, + context const& ctx) : + base_( new detail::handle_object< R, F >( fut, ctx) ) + {} + + void interrupt() + { base_->interrupt(); } + + void interrupt_and_wait() + { base_->interrupt_and_wait(); } + + bool interrupt_and_wait_until( system_time const& abs_time) + { return base_->interrupt_and_wait_until( abs_time); } + + template< typename TimeDuration > + bool interrupt_and_wait_for( TimeDuration const& rel_time) + { return interrupt_and_wait_until( get_system_time() + rel_time); } + + bool interruption_requested() + { return base_->interruption_requested(); } + + R get() + { return base_->get(); } + + bool is_ready() const + { return base_->is_ready(); } + + bool has_value() const + { return base_->has_value(); } + + bool has_exception() const + { return base_->has_exception(); } + + void wait() const + { base_->wait(); } + + bool wait_until( system_time const& abs_time) const + { return base_->wait_until( abs_time); } + + template< typename TimeDuration > + bool wait_for( TimeDuration const& rel_time) const + { return wait_until( get_system_time() + rel_time); } + + void swap( handle< R > & other) + { base_.swap( other.base_); } +}; + +template< typename T > +struct is_handle_type +{ BOOST_STATIC_CONSTANT( bool, value = false); }; + +template< typename T > +struct is_handle_type< handle< T > > +{ + BOOST_STATIC_CONSTANT( bool, value = true); +}; + +template< typename Iterator > +typename disable_if< is_handle_type< Iterator >, void >::type waitfor_all( + Iterator begin, Iterator end) +{ + for ( Iterator i = begin; i != end; ++i) + i->wait(); +} + +template< typename R1, typename R2 > +void waitfor_all( handle< R1 > & h1, handle< R2 > & h2) +{ + h1.wait(); + h2.wait(); +} + +template< typename R1, typename R2, typename R3 > +void waitfor_all( handle< R1 > & h1, handle< R2 > & h2, handle< R3 > & h3) +{ + h1.wait(); + h2.wait(); + h3.wait(); +} + +template< typename R1, typename R2, typename R3, typename R4 > +void waitfor_all( + handle< R1 > & h1, handle< R2 > & h2, handle< R3 > & h3, handle< R4 > & h4) +{ + h1.wait(); + h2.wait(); + h3.wait(); + h4.wait(); +} + +template< typename R1, typename R2, typename R3, typename R4, typename R5 > +void waitfor_all( + handle< R1 > & h1, handle< R2 > & h2, handle< R3 > & h3, handle< R4 > & h4, + handle< R5 > & h5) +{ + h1.wait(); + h2.wait(); + h3.wait(); + h4.wait(); + h5.wait(); +} + +}} + +#include + +#endif // BOOST_TASKS_HANDLE_H diff --git a/boost/task/meta.hpp b/boost/task/meta.hpp new file mode 100644 index 00000000..fe60eaea --- /dev/null +++ b/boost/task/meta.hpp @@ -0,0 +1,40 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_META_H +#define BOOST_TASKS_META_H + +#include +#include + +#include + +#include + +namespace boost { +namespace tasks { + +template< typename Pool > +struct has_attribute : public mpl::bool_< + is_same< + detail::has_attribute, + typename Pool::queue_type::attribute_tag_type + >::value +> +{}; + +template< typename Pool > +struct attribute_type +{ + typedef typename Pool::queue_type::attribute_type type; +}; + +}} + +#include + +#endif // BOOST_TASKS_META_H + diff --git a/boost/task/new_thread.hpp b/boost/task/new_thread.hpp new file mode 100755 index 00000000..0b591cee --- /dev/null +++ b/boost/task/new_thread.hpp @@ -0,0 +1,94 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_NEW_THREAD_H +#define BOOST_TASKS_NEW_THREAD_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasks { +namespace detail { + +struct joiner +{ + void operator()( thread * thrd) + { + try + { + BOOST_ASSERT( thrd); + BOOST_ASSERT( thrd->joinable() ); + thrd->join(); + } + catch (...) + {} + delete thrd; + } +}; + +} + +struct new_thread +{ + template< typename R > + handle< R > operator()( BOOST_RV_REF( task< R >) t) + { + if ( this_task::runs_in_pool() ) + { + spin::promise< R > prom; + spin::shared_future< R > f( prom.get_future() ); + context ctx1, ctx2; + handle< R > h( f, ctx1); + callable ca( t, boost::move( prom), ctx2); + shared_ptr< thread > thrd( + new thread( ca), + detail::joiner() ); + ctx1.reset( thrd); + return h; + } + else + { + promise< R > prom; + shared_future< R > f( prom.get_future() ); + context ctx1, ctx2; + handle< R > h( f, ctx1); + callable ca( + t, +// TODO: workaround because thread_move_t will be abigous for move +#ifdef BOOST_HAS_RVALUE_REFS + boost::move( prom), +#else + boost::detail::thread_move_t< promise< R > >( prom), +#endif + ctx2); + shared_ptr< thread > thrd( + new thread( ca), + detail::joiner() ); + ctx1.reset( thrd); + return h; + } + } +}; + +}} + +#include + +#endif // BOOST_TASKS_NEW_THREAD_H diff --git a/boost/task/own_thread.hpp b/boost/task/own_thread.hpp new file mode 100755 index 00000000..86c3c92f --- /dev/null +++ b/boost/task/own_thread.hpp @@ -0,0 +1,66 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_OWN_THREAD_H +#define BOOST_TASKS_OWN_THREAD_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasks { + +struct own_thread +{ + template< typename R > + handle< R > operator()( BOOST_RV_REF( task< R >) t) + { + if ( this_task::runs_in_pool() ) + { + spin::promise< R > prom; + spin::shared_future< R > f( prom.get_future() ); + context ctx; + handle< R > h( f, ctx); + callable ca( t, boost::move( prom), ctx); + ca(); + return h; + } + else + { + promise< R > prom; + shared_future< R > f( prom.get_future() ); + context ctx; + handle< R > h( f, ctx); + callable ca( + t, +// TODO: workaround because thread_move_t will be abigous for move +#ifdef BOOST_HAS_RVALUE_REFS + boost::move( prom), +#else + boost::detail::thread_move_t< promise< R > >( prom), +#endif + ctx); + ca(); + return h; + } + } +}; + +}} + +#include + +#endif // BOOST_TASKS_OWN_THREAD_H diff --git a/boost/task/poolsize.hpp b/boost/task/poolsize.hpp new file mode 100644 index 00000000..c0af86d3 --- /dev/null +++ b/boost/task/poolsize.hpp @@ -0,0 +1,43 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_POOLSIZE_H +#define BOOST_TASKS_POOLSIZE_H + +#include + +#include + +#include + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251 4275) +# endif + +namespace boost { +namespace tasks { + +class BOOST_TASKS_DECL poolsize +{ +private: + std::size_t value_; + +public: + explicit poolsize( std::size_t value); + + operator std::size_t () const; +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#include + +#endif // BOOST_TASKS_POOLSIZE_H diff --git a/boost/task/semaphore.hpp b/boost/task/semaphore.hpp new file mode 100755 index 00000000..f4ba5449 --- /dev/null +++ b/boost/task/semaphore.hpp @@ -0,0 +1,64 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASK_SEMAPHORE_H +#define BOOST_TASK_SEMAPHORE_H + +#include + +extern "C" +{ +# if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__) +#include +# else +#include +# endif +} + +#include + +#include + +#include + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251 4275) +# endif + +namespace boost { +namespace tasks { + +class BOOST_TASKS_DECL semaphore : private boost::noncopyable +{ +private: +# if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__) + HANDLE handle_; +# else + int handle_; +# endif +public: + semaphore( int); + + ~semaphore(); + + void post( int = 1); + + void wait(); + + bool try_wait(); +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + + +#include + +#endif // BOOST_TASK_SEMAPHORE_H diff --git a/boost/task/spin/auto_reset_event.hpp b/boost/task/spin/auto_reset_event.hpp new file mode 100644 index 00000000..50c79db3 --- /dev/null +++ b/boost/task/spin/auto_reset_event.hpp @@ -0,0 +1,49 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_SPIN_AUTO_RESET_EVENT_H +#define BOOST_TASKS_SPIN_AUTO_RESET_EVENT_H + +#include +#include +#include + +#include + +namespace boost { +namespace tasks { +namespace spin { + +class BOOST_TASKS_DECL auto_reset_event : private noncopyable +{ +private: + enum state + { + SET = 0, + RESET + }; + + atomic< state > state_; + +public: + explicit auto_reset_event( bool = false); + + void set(); + + void wait(); + + bool try_wait(); + + bool timed_wait( system_time const&); + + template< typename TimeDuration > + bool timed_wait( TimeDuration const& rel_time) + { return timed_wait( get_system_time() + rel_time); } +}; + +}}} + +#endif // BOOST_TASKS_SPIN_AUTO_RESET_EVENT_H diff --git a/boost/task/spin/barrier.hpp b/boost/task/spin/barrier.hpp new file mode 100644 index 00000000..6acae2a6 --- /dev/null +++ b/boost/task/spin/barrier.hpp @@ -0,0 +1,39 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_SPIN_BARRIER_H +#define BOOST_TASKS_SPIN_BARRIER_H + +#include + +#include + +#include +#include +#include + +namespace boost { +namespace tasks { +namespace spin { + +class BOOST_TASKS_DECL barrier : private noncopyable +{ +private: + std::size_t initial_; + std::size_t current_; + bool cycle_; + mutex mtx_; + condition cond_; + +public: + barrier( std::size_t); + + bool wait(); +}; + +}}} + +#endif // BOOST_TASKS_SPIN_BARRIER_H diff --git a/boost/task/spin/bounded_channel.hpp b/boost/task/spin/bounded_channel.hpp new file mode 100644 index 00000000..84a5ca5c --- /dev/null +++ b/boost/task/spin/bounded_channel.hpp @@ -0,0 +1,398 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_SPIN_BOUNDED_CHANNEL_H +#define BOOST_TASKS_SPIN_BOUNDED_CHANNEL_H + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace boost { +namespace tasks { +namespace spin { +namespace detail { + +template< typename T > +struct bounded_channel_base_node +{ + typedef intrusive_ptr< bounded_channel_base_node > ptr; + + atomic< std::size_t > use_count; + T va; + ptr next; + + bounded_channel_base_node() : + use_count( 0), + va(), + next() + {} +}; + +template< typename T > +void intrusive_ptr_add_ref( bounded_channel_base_node< T > * p) +{ p->use_count.fetch_add( 1, memory_order_relaxed); } + +template< typename T > +void intrusive_ptr_release( bounded_channel_base_node< T > * p) +{ + if ( p->use_count.fetch_sub( 1, memory_order_release) == 1) + { + atomic_thread_fence( memory_order_acquire); + delete p; + } +} + +template< typename T > +class bounded_channel_base : private noncopyable +{ +public: + typedef optional< T > value_type; + +private: + typedef bounded_channel_base_node< value_type > node_type; + + template< typename X > + friend void intrusive_ptr_add_ref( bounded_channel_base< X > * p); + template< typename X > + friend void intrusive_ptr_release( bounded_channel_base< X > * p); + + enum state + { + ACTIVE = 0, + DEACTIVE + }; + + atomic< state > state_; + atomic< std::size_t > count_; + typename node_type::ptr head_; + mutable mutex head_mtx_; + typename node_type::ptr tail_; + mutable mutex tail_mtx_; + condition not_empty_cond_; + condition not_full_cond_; + unsigned int hwm_; + unsigned int lwm_; + atomic< std::size_t > use_count_; + + bool active_() const + { return ACTIVE == state_.load(); } + + void deactivate_() + { state_.store( DEACTIVE); } + + std::size_t size_() const + { return count_.load(); } + + bool empty_() const + { return head_ == get_tail_(); } + + bool full_() const + { return size_() >= hwm_; } + + typename node_type::ptr get_tail_() const + { + mutex::scoped_lock lk( tail_mtx_); + typename node_type::ptr tmp = tail_; + return tmp; + } + + typename node_type::ptr pop_head_() + { + typename node_type::ptr old_head = head_; + head_ = old_head->next; + count_.fetch_sub( 1); + return old_head; + } + +public: + bounded_channel_base( + std::size_t hwm, + std::size_t lwm) : + state_( ACTIVE), + count_( 0), + head_( new node_type() ), + head_mtx_(), + tail_( head_), + tail_mtx_(), + not_empty_cond_(), + not_full_cond_(), + hwm_( hwm), + lwm_( lwm), + use_count_( 0) + { + if ( hwm_ < lwm_) + throw invalid_watermark(); + } + + bounded_channel_base( std::size_t wm) : + state_( ACTIVE), + count_( 0), + head_( new node_type() ), + head_mtx_(), + tail_( head_), + tail_mtx_(), + not_empty_cond_(), + not_full_cond_(), + hwm_( wm), + lwm_( wm), + use_count_( 0) + {} + + std::size_t upper_bound() const + { return hwm_; } + + std::size_t lower_bound() const + { return lwm_; } + + bool active() const + { return active_(); } + + void deactivate() + { + mutex::scoped_lock head_lk( head_mtx_); + mutex::scoped_lock tail_lk( tail_mtx_); + not_empty_cond_.notify_all(); + not_full_cond_.notify_all(); + deactivate_(); + } + + bool empty() const + { + mutex::scoped_lock lk( head_mtx_); + return empty_(); + } + + void put( T const& t) + { + typename node_type::ptr new_node( new node_type() ); + { + mutex::scoped_lock lk( tail_mtx_); + + if ( full_() ) + { + while ( active_() && full_() ) + not_full_cond_.wait( lk); + } + + if ( ! active_() ) + throw std::runtime_error("queue is not active"); + + tail_->va = t; + tail_->next = new_node; + tail_ = new_node; + count_.fetch_add( 1); + } + not_empty_cond_.notify_one(); + } + + bool put( T const& t, system_time const& abs_time) + { + typename node_type::ptr new_node( new node_type() ); + { + mutex::scoped_lock lk( tail_mtx_); + + if ( full_() ) + { + while ( active_() && full_() ) + if ( ! not_full_cond_.timed_wait( lk, abs_time) ); + return false; + } + + if ( ! active_() ) + throw std::runtime_error("queue is not active"); + + tail_->va = t; + tail_->next = new_node; + tail_ = new_node; + count_.fetch_add( 1); + } + not_empty_cond_.notify_one(); + return true; + } + + bool take( value_type & va) + { + mutex::scoped_lock lk( head_mtx_); + bool empty = empty_(); + if ( ! active_() && empty) + return false; + if ( empty) + { + try + { + while ( active_() && empty_() ) + not_empty_cond_.wait( lk); + } + catch ( task_interrupted const&) + { return false; } + } + if ( ! active_() && empty_() ) + return false; + swap( va, head_->va); + pop_head_(); + if ( size_() <= lwm_) + { + if ( lwm_ == hwm_) + not_full_cond_.notify_one(); + else + // more than one producer could be waiting + // for submiting an action object + not_full_cond_.notify_all(); + } + return va; + } + + bool take( value_type & va, system_time const& abs_time) + { + mutex::scoped_lock lk( head_mtx_); + bool empty = empty_(); + if ( ! active_() && empty) + return false; + if ( empty) + { + try + { + while ( active_() && empty_() ) + if ( ! not_empty_cond_.timed_wait( lk, abs_time) ) + return false; + } + catch ( task_interrupted const&) + { return false; } + } + if ( ! active_() && empty_() ) + return false; + swap( va, head_->va); + pop_head_(); + if ( size_() <= lwm_) + { + if ( lwm_ == hwm_) + not_full_cond_.notify_one(); + else + // more than one producer could be waiting + // for submiting an action object + not_full_cond_.notify_all(); + } + return va; + } + + bool try_take( value_type & va) + { + mutex::scoped_lock lk( head_mtx_); + if ( empty_() ) + return false; + swap( va, head_->va); + pop_head_(); + bool valid = va; + if ( valid && size_() <= lwm_) + { + if ( lwm_ == hwm_) + not_full_cond_.notify_one(); + else + // more than one producer could be waiting + // in order to submit an task + not_full_cond_.notify_all(); + } + return valid; + } +}; + +template< typename T > +void intrusive_ptr_add_ref( bounded_channel_base< T > * p) +{ p->use_count_.fetch_add( 1, memory_order_relaxed); } + +template< typename T > +void intrusive_ptr_release( bounded_channel_base< T > * p) +{ + if ( p->use_count_.fetch_sub( 1, memory_order_release) == 1) + { + atomic_thread_fence( memory_order_acquire); + delete p; + } +} + +} + +template< typename T > +class bounded_channel +{ +private: + typedef typename detail::bounded_channel_base< T >::value_type value_type; + + intrusive_ptr< detail::bounded_channel_base< T > > base_; + +public: + bounded_channel( + std::size_t hwm, + std::size_t lwm) : + base_( new detail::bounded_channel_base< T >( hwm, lwm) ) + {} + + bounded_channel( std::size_t wm) : + base_( new detail::bounded_channel_base< T >( wm) ) + {} + + void upper_bound( std::size_t hwm) + { base_->upper_bound( hwm); } + + std::size_t upper_bound() const + { return base_->upper_bound(); } + + void lower_bound( std::size_t lwm) + { base_->lower_bound( lwm); } + + std::size_t lower_bound() const + { return base_->lower_bound(); } + + bool active() const + { return base_->active(); } + + void deactivate() + { base_->deactivate(); } + + bool empty() const + { return base_->empty(); } + + void put( T const& t) + { base_->put( t); } + + bool put( T const& t, system_time const& abs_time) + { return base_->put( t, abs_time); } + + template< typename TimeDuration > + bool put( T const& t, TimeDuration const& rel_time) + { return base_->put( t, get_system_time() + rel_time); } + + bool take( value_type & va) + { return base_->take( va);} + + bool take( value_type & va, system_time const& abs_time) + { return base_->take( va, abs_time);} + + template< typename TimeDuration > + bool take( value_type & va, TimeDuration const& rel_time) + { return base_->take( va, get_system_time() + rel_time);} + + bool try_take( value_type & va) + { return base_->try_take( va); } +}; + +}}} + +#include + +#endif // BOOST_TASKS_SPIN_BOUNDED_CHANNEL_H diff --git a/boost/task/spin/condition.hpp b/boost/task/spin/condition.hpp new file mode 100644 index 00000000..ddbe1108 --- /dev/null +++ b/boost/task/spin/condition.hpp @@ -0,0 +1,293 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// based on boost::interprocess::sync::interprocess_condition + +#ifndef BOOST_TASKS_SPIN_CONDITION_H +#define BOOST_TASKS_SPIN_CONDITION_H + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace boost { +namespace tasks { +namespace spin { + +class BOOST_TASKS_DECL condition : private noncopyable +{ +private: + enum command + { + SLEEPING = 0, + NOTIFY_ONE, + NOTIFY_ALL + }; + + atomic< command > cmd_; + atomic< std::size_t > waiters_; + mutex enter_mtx_; + mutex check_mtx_; + + void notify_( command); + +public: + condition(); + + void notify_one(); + + void notify_all(); + + void wait( unique_lock< mutex > & lk) + { + if ( ! lk) + throw lock_error(); + wait( * lk.mutex() ); + } + + template< typename Pred > + void wait( unique_lock< mutex > & lk, Pred pred) + { + if ( ! lk) + throw lock_error(); + + while ( ! pred() ) + wait( * lk.mutex() ); + } + + bool timed_wait( unique_lock< mutex > & lk, system_time const& abs_time) + { + if ( abs_time.is_infinity() ) + { + wait( lk); + return true; + } + + if ( ! lk) + throw lock_error(); + return timed_wait( * lk.mutex(), abs_time); + } + + template< typename Pred > + bool timed_wait( unique_lock< mutex > & lk, system_time const& abs_time, Pred pred) + { + if ( abs_time.is_infinity() ) + { + wait( lk, pred); + return true; + } + + if ( ! lk) + throw lock_error(); + + while ( ! pred() ) + if ( ! timed_wait( * lk.mutex(), abs_time) ) + return pred(); + return true; + } + + template< typename TimeDuration > + bool timed_wait( unique_lock< mutex > & lk, TimeDuration const& rel_time) + { return timed_wait( lk, get_system_time() + rel_time); } + + template< + typename TimeDuration, + typename Pred + > + bool timed_wait( unique_lock< mutex > & lk, TimeDuration const& rel_time, Pred pred) + { return timed_wait( lk, get_system_time() + rel_time, pred); } + + template< typename LockType > + void wait( LockType & lt) + { + { + mutex::scoped_lock lk( enter_mtx_); + BOOST_ASSERT( lk); + waiters_.fetch_add( 1); + lt.unlock(); + } + + bool unlock_enter_mtx = false; + for (;;) + { + while ( SLEEPING == cmd_.load() ) + { + this_thread::interruption_point(); + if ( this_task::runs_in_pool() ) + this_task::yield(); + else + this_thread::yield(); + this_thread::interruption_point(); + } + + mutex::scoped_lock lk( check_mtx_); + BOOST_ASSERT( lk); + + command expected = NOTIFY_ONE; + cmd_.compare_exchange_strong( expected, SLEEPING); + if ( SLEEPING == expected) + continue; + else if ( NOTIFY_ONE == expected) + { + unlock_enter_mtx = true; + waiters_.fetch_sub( 1); + break; + } + else + { + unlock_enter_mtx = 1 == waiters_.fetch_sub( 1); + if ( unlock_enter_mtx) + { + expected = NOTIFY_ALL; + cmd_.compare_exchange_strong( expected, SLEEPING); + } + break; + } + } + + if ( unlock_enter_mtx) + enter_mtx_.unlock(); + + lt.lock(); + } + + template< + typename LockType, + typename Pred + > + void wait( LockType & lt, Pred pred) + { + while ( ! pred() ) + wait( lt); + } + + template< typename LockType > + bool timed_wait( LockType & lt, system_time const& abs_time) + { + if ( abs_time.is_infinity() ) + { + wait( lt); + return true; + } + + if ( get_system_time() >= abs_time) return false; + + { + mutex::scoped_lock lk( enter_mtx_, abs_time); + BOOST_ASSERT( lk); + waiters_.fetch_add( 1); + lt.unlock(); + } + + bool timed_out = false, unlock_enter_mtx = false; + for (;;) + { + while ( SLEEPING == cmd_.load() ) + { + this_thread::interruption_point(); + if ( this_task::runs_in_pool() ) + this_task::yield(); + else + this_thread::yield(); + this_thread::interruption_point(); + + if ( get_system_time() >= abs_time) + { + timed_out = enter_mtx_.try_lock(); + if ( ! timed_out) + continue; + break; + } + } + + if ( timed_out) + { + waiters_.fetch_sub( 1); + unlock_enter_mtx = true; + break; + } + else + { + mutex::scoped_lock lk( check_mtx_); + BOOST_ASSERT( lk); + + command expected = NOTIFY_ONE; + cmd_.compare_exchange_strong( expected, SLEEPING); + if ( SLEEPING == expected) + continue; + else if ( NOTIFY_ONE == expected) + { + unlock_enter_mtx = true; + waiters_.fetch_sub( 1); + break; + } + else + { + unlock_enter_mtx = 1 == waiters_.fetch_sub( 1); + if ( unlock_enter_mtx) + { + expected = NOTIFY_ALL; + cmd_.compare_exchange_strong( expected, SLEEPING); + } + break; + } + } + } + + if ( unlock_enter_mtx) + enter_mtx_.unlock(); + + lt.lock(); + + return ! timed_out; + } + + template< + typename LockType, + typename Pred + > + bool timed_wait( LockType & lt, system_time const& abs_time, Pred pred) + { + if ( abs_time.is_infinity() ) + { + wait( lt, pred); + return true; + } + + while ( ! pred() ) + if ( ! wait( lt, abs_time) ) + return pred(); + return true; + } + + template< + typename LockType, + typename TimeDuration + > + bool timed_wait( LockType & lt, TimeDuration const& rel_time) + { return timed_wait( lt, get_system_time() + rel_time); } + + template< + typename LockType, + typename TimeDuration, + typename Pred + > + bool timed_wait( LockType & lt, TimeDuration const& rel_time, Pred pred) + { return timed_wait( lt, get_system_time() + rel_time, pred); } +}; + +}}} + +#endif // BOOST_TASKS_SPIN_CONDITION_H diff --git a/boost/task/spin/count_down_event.hpp b/boost/task/spin/count_down_event.hpp new file mode 100644 index 00000000..e13044e6 --- /dev/null +++ b/boost/task/spin/count_down_event.hpp @@ -0,0 +1,50 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_SPIN_COUNT_DOWN_EVENT_H +#define BOOST_TASKS_SPIN_COUNT_DOWN_EVENT_H + +#include + +#include +#include +#include + +#include + +namespace boost { +namespace tasks { +namespace spin { + +class BOOST_TASKS_DECL count_down_event : private noncopyable +{ +private: + std::size_t initial_; + atomic< std::size_t > current_; + +public: + explicit count_down_event( std::size_t); + + std::size_t initial() const; + + std::size_t current() const; + + bool is_set() const; + + void set(); + + void wait(); + + bool timed_wait( system_time const&); + + template< typename TimeDuration > + bool timed_wait( TimeDuration const& rel_time) + { return timed_wait( get_system_time() + rel_time); } +}; + +}}} + +#endif // BOOST_TASKS_SPIN_COUNT_DOWN_EVENT_H diff --git a/boost/task/spin/future.hpp b/boost/task/spin/future.hpp new file mode 100644 index 00000000..a556f88f --- /dev/null +++ b/boost/task/spin/future.hpp @@ -0,0 +1,1125 @@ +// (C) Copyright 2008-9 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_SPIN_FUTURE_HPP +#define BOOST_TASKS_SPIN_FUTURE_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace boost { +namespace tasks { + +namespace future_state { + + enum state { uninitialized, waiting, ready, moved }; + +} + +namespace spin { +namespace detail { + +struct future_object_base +{ + boost::exception_ptr exception; + bool done; + mutex mtx; + spin::condition waiters; + typedef std::list waiter_list; + waiter_list external_waiters; + boost::function callback; + + future_object_base(): + done(false) + {} + + virtual ~future_object_base() + {} + + waiter_list::iterator register_external_waiter(spin::condition& cv) + { + boost::unique_lock lock(mtx); + do_callback(lock); + return external_waiters.insert(external_waiters.end(),&cv); + } + + void remove_external_waiter(waiter_list::iterator it) + { + boost::lock_guard lock(mtx); + external_waiters.erase(it); + } + + void mark_finished_internal() + { + done=true; + waiters.notify_all(); + for(waiter_list::const_iterator it=external_waiters.begin(), + end=external_waiters.end();it!=end;++it) + { + (*it)->notify_all(); + } + } + + struct relocker + { + boost::unique_lock& lock; + + relocker(boost::unique_lock& lock_): + lock(lock_) + { + lock.unlock(); + } + ~relocker() + { + lock.lock(); + } + }; + + void do_callback(boost::unique_lock& lock) + { + if(callback && !done) + { + boost::function local_callback=callback; + relocker relock(lock); + local_callback(); + } + } + + + void wait(bool rethrow=true) + { + boost::unique_lock lock(mtx); + do_callback(lock); + while(!done) + { + waiters.wait(lock); + } + if(rethrow && exception) + { + boost::rethrow_exception(exception); + } + } + + bool timed_wait_until(boost::system_time const& target_time) + { + boost::unique_lock lock(mtx); + do_callback(lock); + while(!done) + { + bool const success=waiters.timed_wait(lock,target_time); + if(!success && !done) + { + return false; + } + } + return true; + } + + void mark_exceptional_finish_internal(boost::exception_ptr const& e) + { + exception=e; + mark_finished_internal(); + } + void mark_exceptional_finish() + { + boost::lock_guard lock(mtx); + mark_exceptional_finish_internal(boost::current_exception()); + } + + bool has_value() + { + boost::lock_guard lock(mtx); + return done && !exception; + } + bool has_exception() + { + boost::lock_guard lock(mtx); + return done && exception; + } + + template + void set_wait_callback(F f,U* u) + { + callback=boost::bind(f,boost::ref(*u)); + } + +private: + future_object_base(future_object_base const&); + future_object_base& operator=(future_object_base const&); +}; + +template +struct future_object: future_object_base +{ + typedef typename tasks::detail::future_traits::storage_type storage_type; + typedef typename tasks::detail::future_traits::source_reference_type source_reference_type; + typedef typename tasks::detail::future_traits::rvalue_source_type rvalue_source_type; + typedef typename tasks::detail::future_traits::move_dest_type move_dest_type; + + storage_type result; + + future_object(): + result(0) + {} + + void mark_finished_with_result_internal(source_reference_type result_) + { + tasks::detail::future_traits::init(result,result_); + mark_finished_internal(); + } + void mark_finished_with_result_internal(rvalue_source_type result_) + { + tasks::detail::future_traits::init(result,static_cast(result_)); + mark_finished_internal(); + } + + void mark_finished_with_result(source_reference_type result_) + { + boost::lock_guard lock(mtx); + mark_finished_with_result_internal(result_); + } + void mark_finished_with_result(rvalue_source_type result_) + { + boost::lock_guard lock(mtx); + mark_finished_with_result_internal(result_); + } + + move_dest_type get() + { + wait(); + return *result; + } + + future_state::state get_state() + { + boost::lock_guard guard(mtx); + if(!done) + { + return future_state::waiting; + } + else + { + return future_state::ready; + } + } + +private: + future_object(future_object const&); + future_object& operator=(future_object const&); +}; + +template<> +struct future_object: future_object_base +{ + future_object() + {} + + void mark_finished_with_result_internal() + { + mark_finished_internal(); + } + + void mark_finished_with_result() + { + boost::lock_guard lock(mtx); + mark_finished_with_result_internal(); + } + + void get() + { + wait(); + } + + future_state::state get_state() + { + boost::lock_guard guard(mtx); + if(!done) + { + return future_state::waiting; + } + else + { + return future_state::ready; + } + } + +private: + future_object(future_object const&); + future_object& operator=(future_object const&); +}; + +class future_waiter +{ + struct registered_waiter + { + boost::shared_ptr future; + detail::future_object_base::waiter_list::iterator wait_iterator; + unsigned index; + + registered_waiter(boost::shared_ptr const& future_, + detail::future_object_base::waiter_list::iterator wait_iterator_, + unsigned index_): + future(future_),wait_iterator(wait_iterator_),index(index_) + {} + + }; + + struct all_futures_lock + { + unsigned count; + boost::scoped_array > locks; + + all_futures_lock(std::vector& futures): + count(futures.size()),locks(new boost::unique_lock[count]) + { + for(unsigned i=0;i(futures[i].future->mtx); + } + } + + void lock() + { + boost::lock(locks.get(),locks.get()+count); + } + + void unlock() + { + for(unsigned i=0;i futures; + unsigned future_count; + +public: + future_waiter(): + future_count(0) + {} + + template + void add(F& f) + { + if(f.future) + { + futures.push_back(registered_waiter(f.future,f.future->register_external_waiter(cv),future_count)); + } + ++future_count; + } + + unsigned wait() + { + all_futures_lock lk(futures); + for(;;) + { + for(unsigned i=0;idone) + { + return futures[i].index; + } + } + cv.wait(lk); + } + } + + ~future_waiter() + { + for(unsigned i=0;iremove_external_waiter(futures[i].wait_iterator); + } + } + +}; + +} + +template +class unique_future; + +template +class shared_future; + +template +struct is_future_type +{ + BOOST_STATIC_CONSTANT(bool, value=false); +}; + +template +struct is_future_type > +{ + BOOST_STATIC_CONSTANT(bool, value=true); +}; + +template +struct is_future_type > +{ + BOOST_STATIC_CONSTANT(bool, value=true); +}; + +template +typename boost::disable_if,void>::type wait_for_all(Iterator begin,Iterator end) +{ + for(Iterator current=begin;current!=end;++current) + { + current->wait(); + } +} + +template +typename boost::enable_if,void>::type wait_for_all(F1& f1,F2& f2) +{ + f1.wait(); + f2.wait(); +} + +template +void wait_for_all(F1& f1,F2& f2,F3& f3) +{ + f1.wait(); + f2.wait(); + f3.wait(); +} + +template +void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4) +{ + f1.wait(); + f2.wait(); + f3.wait(); + f4.wait(); +} + +template +void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5) +{ + f1.wait(); + f2.wait(); + f3.wait(); + f4.wait(); + f5.wait(); +} + +template +typename boost::disable_if,Iterator>::type wait_for_any(Iterator begin,Iterator end) +{ + detail::future_waiter waiter; + for(Iterator current=begin;current!=end;++current) + { + waiter.add(*current); + } + return boost::next(begin,waiter.wait()); +} + +template +typename boost::enable_if,unsigned>::type wait_for_any(F1& f1,F2& f2) +{ + detail::future_waiter waiter; + waiter.add(f1); + waiter.add(f2); + return waiter.wait(); +} + +template +unsigned wait_for_any(F1& f1,F2& f2,F3& f3) +{ + detail::future_waiter waiter; + waiter.add(f1); + waiter.add(f2); + waiter.add(f3); + return waiter.wait(); +} + +template +unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4) +{ + detail::future_waiter waiter; + waiter.add(f1); + waiter.add(f2); + waiter.add(f3); + waiter.add(f4); + return waiter.wait(); +} + +template +unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5) +{ + detail::future_waiter waiter; + waiter.add(f1); + waiter.add(f2); + waiter.add(f3); + waiter.add(f4); + waiter.add(f5); + return waiter.wait(); +} + +template +class promise; + +template +class packaged_task; + +template +class unique_future +{ + typedef boost::shared_ptr > future_ptr; + + BOOST_MOVABLE_BUT_NOT_COPYABLE( unique_future); + + future_ptr future; + + friend class shared_future; + friend class promise; + friend class packaged_task; + friend class detail::future_waiter; + + typedef typename tasks::detail::future_traits::move_dest_type move_dest_type; + + unique_future(future_ptr future_): + future(future_) + {} + +public: + typedef future_state::state state; + + unique_future() + {} + + ~unique_future() + {} + + unique_future( BOOST_RV_REF( unique_future) other): + future(other.future) + { + other.future.reset(); + } + + unique_future& operator=( BOOST_RV_REF( unique_future) other) + { + future=other.future; + other.future.reset(); + return *this; + } + + void swap(unique_future& other) + { + future.swap(other.future); + } + + // retrieving the value + move_dest_type get() + { + if(!future) + { + throw future_uninitialized(); + } + + return future->get(); + } + + // functions to check state, and wait for ready + state get_state() const + { + if(!future) + { + return future_state::uninitialized; + } + return future->get_state(); + } + + + bool is_ready() const + { + return get_state()==future_state::ready; + } + + bool has_exception() const + { + return future && future->has_exception(); + } + + bool has_value() const + { + return future && future->has_value(); + } + + void wait() const + { + if(!future) + { + throw future_uninitialized(); + } + future->wait(false); + } + + template + bool timed_wait(Duration const& rel_time) const + { + return timed_wait_until(boost::get_system_time()+rel_time); + } + + bool timed_wait_until(boost::system_time const& abs_time) const + { + if(!future) + { + throw future_uninitialized(); + } + return future->timed_wait_until(abs_time); + } +}; + +template +class shared_future +{ + typedef boost::shared_ptr > future_ptr; + + BOOST_COPYABLE_AND_MOVABLE( shared_future); + + future_ptr future; + + friend class detail::future_waiter; + friend class promise; + friend class packaged_task; + + shared_future(future_ptr future_): + future(future_) + {} + +public: + shared_future(shared_future const& other): + future(other.future) + {} + + typedef future_state::state state; + + shared_future() + {} + + ~shared_future() + {} + + shared_future& operator=( BOOST_COPY_ASSIGN_REF( shared_future) other) + { + future=other.future; + return *this; + } + + shared_future( BOOST_RV_REF( shared_future) other): + future(other.future) + { + other->future.reset(); + } + shared_future( BOOST_RV_REF( unique_future) other): + future(other.future) + { + other.future.reset(); + } + shared_future& operator=(BOOST_RV_REF( shared_future) other) + { + future.swap(other.future); + other.future.reset(); + return *this; + } + shared_future& operator=( BOOST_RV_REF( unique_future) other) + { + future.swap(other.future); + other.future.reset(); + return *this; + } + + void swap(shared_future& other) + { + future.swap(other.future); + } + + // retrieving the value + R get() + { + if(!future) + { + throw future_uninitialized(); + } + + return future->get(); + } + + // functions to check state, and wait for ready + state get_state() const + { + if(!future) + { + return future_state::uninitialized; + } + return future->get_state(); + } + + + bool is_ready() const + { + return get_state()==future_state::ready; + } + + bool has_exception() const + { + return future && future->has_exception(); + } + + bool has_value() const + { + return future && future->has_value(); + } + + void wait() const + { + if(!future) + { + throw future_uninitialized(); + } + future->wait(false); + } + + template + bool timed_wait(Duration const& rel_time) const + { + return timed_wait_until(boost::get_system_time()+rel_time); + } + + bool timed_wait_until(boost::system_time const& abs_time) const + { + if(!future) + { + throw future_uninitialized(); + } + return future->timed_wait_until(abs_time); + } +}; + +template +class promise +{ + typedef boost::shared_ptr > future_ptr; + + BOOST_MOVABLE_BUT_NOT_COPYABLE( promise); + + future_ptr future; + bool future_obtained; + + void lazy_init() + { + if(!future) + { + future_obtained=false; + future.reset(new detail::future_object); + } + } + +public: +// template explicit promise(Allocator a); + + promise(): + future(),future_obtained(false) + {} + + ~promise() + { + if(future) + { + boost::lock_guard lock(future->mtx); + + if(!future->done) + { + future->mark_exceptional_finish_internal(boost::copy_exception(broken_promise())); + } + } + } + + promise( BOOST_RV_REF( promise) rhs): + future(rhs.future),future_obtained(rhs.future_obtained) + { + rhs.future.reset(); + } + promise & operator=( BOOST_RV_REF( promise) rhs) + { + future=rhs.future; + future_obtained=rhs.future_obtained; + rhs.future.reset(); + return *this; + } + + void swap(promise& other) + { + future.swap(other.future); + std::swap(future_obtained,other.future_obtained); + } + + // Result retrieval + unique_future get_future() + { + lazy_init(); + if(future_obtained) + { + throw future_already_retrieved(); + } + future_obtained=true; + return unique_future(future); + } + + void set_value(typename tasks::detail::future_traits::source_reference_type r) + { + lazy_init(); + boost::lock_guard lock(future->mtx); + if(future->done) + { + throw promise_already_satisfied(); + } + future->mark_finished_with_result_internal(r); + } + +// void set_value(R && r); + void set_value(typename tasks::detail::future_traits::rvalue_source_type r) + { + lazy_init(); + boost::lock_guard lock(future->mtx); + if(future->done) + { + throw promise_already_satisfied(); + } + future->mark_finished_with_result_internal(static_cast::rvalue_source_type>(r)); + } + + void set_exception(boost::exception_ptr p) + { + lazy_init(); + boost::lock_guard lock(future->mtx); + if(future->done) + { + throw promise_already_satisfied(); + } + future->mark_exceptional_finish_internal(p); + } + + template + void set_wait_callback(F f) + { + lazy_init(); + future->set_wait_callback(f,this); + } + +}; + +template <> +class promise +{ + typedef boost::shared_ptr > future_ptr; + + BOOST_MOVABLE_BUT_NOT_COPYABLE( promise); + + future_ptr future; + bool future_obtained; + + void lazy_init() + { + if(!future) + { + future_obtained=false; + future.reset(new detail::future_object); + } + } +public: + promise(): + future(),future_obtained(false) + {} + + ~promise() + { + if(future) + { + boost::lock_guard lock(future->mtx); + + if(!future->done) + { + future->mark_exceptional_finish_internal(boost::copy_exception(broken_promise())); + } + } + } + + promise( BOOST_RV_REF( promise) rhs): + future(rhs.future),future_obtained(rhs.future_obtained) + { + rhs.future.reset(); + } + promise & operator=( BOOST_RV_REF( promise) rhs) + { + future=rhs.future; + future_obtained=rhs.future_obtained; + rhs.future.reset(); + return *this; + } + + void swap(promise& other) + { + future.swap(other.future); + std::swap(future_obtained,other.future_obtained); + } + + // Result retrieval + unique_future get_future() + { + lazy_init(); + + if(future_obtained) + { + throw future_already_retrieved(); + } + future_obtained=true; + return unique_future(future); + } + + void set_value() + { + lazy_init(); + boost::lock_guard lock(future->mtx); + if(future->done) + { + throw promise_already_satisfied(); + } + future->mark_finished_with_result_internal(); + } + + void set_exception(boost::exception_ptr p) + { + lazy_init(); + boost::lock_guard lock(future->mtx); + if(future->done) + { + throw promise_already_satisfied(); + } + future->mark_exceptional_finish_internal(p); + } + + template + void set_wait_callback(F f) + { + lazy_init(); + future->set_wait_callback(f,this); + } +}; + +namespace detail { + +template +struct task_base: + detail::future_object +{ + bool started; + + task_base(): + started(false) + {} + + void run() + { + { + boost::lock_guard lk(this->mtx); + if(started) + { + throw task_already_started(); + } + started=true; + } + do_run(); + } + + void owner_destroyed() + { + boost::lock_guard lk(this->mtx); + if(!started) + { + started=true; + this->mark_exceptional_finish_internal(boost::copy_exception(broken_promise())); + } + } + + virtual void do_run()=0; +}; + + +template +struct task_object: + task_base +{ + F f; + task_object(F const& f_): + f(f_) + {} + task_object( BOOST_RV_REF( F) f_): + f(f_) + {} + + void do_run() + { + try + { + this->mark_finished_with_result(f()); + } + catch(...) + { + this->mark_exceptional_finish(); + } + } +}; + +template +struct task_object: + task_base +{ + F f; + task_object(F const& f_): + f(f_) + {} + task_object( BOOST_RV_REF( F) f_): + f(f_) + {} + + void do_run() + { + try + { + f(); + this->mark_finished_with_result(); + } + catch(...) + { + this->mark_exceptional_finish(); + } + } +}; + +} + + +template +class packaged_task +{ + BOOST_MOVABLE_BUT_NOT_COPYABLE( packaged_task); + + boost::shared_ptr > task; + bool future_obtained; + +public: + packaged_task(): + future_obtained(false) + {} + + // construction and destruction + template + explicit packaged_task(F const& f): + task(new detail::task_object(f)),future_obtained(false) + {} + explicit packaged_task(R(*f)()): + task(new detail::task_object(f)),future_obtained(false) + {} + + template + explicit packaged_task( BOOST_RV_REF( F) f): + task(new detail::task_object(f)),future_obtained(false) + {} + +// template +// explicit packaged_task(F const& f, Allocator a); +// template +// explicit packaged_task(F&& f, Allocator a); + + + ~packaged_task() + { + if(task) + { + task->owner_destroyed(); + } + } + + packaged_task( BOOST_RV_REF( packaged_task) other): + future_obtained(other.future_obtained) + { + task.swap(other.task); + other.future_obtained=false; + } + packaged_task& operator=( BOOST_RV_REF( packaged_task) other) + { + packaged_task temp(other); + swap(temp); + return *this; + } + + void swap(packaged_task& other) + { + task.swap(other.task); + std::swap(future_obtained,other.future_obtained); + } + + // result retrieval + unique_future get_future() + { + if(!task) + { + throw task_moved(); + } + else if(!future_obtained) + { + future_obtained=true; + return unique_future(task); + } + else + { + throw future_already_retrieved(); + } + } + + + // execution + void operator()() + { + if(!task) + { + throw task_moved(); + } + task->run(); + } + + template + void set_wait_callback(F f) + { + task->set_wait_callback(f,this); + } +}; + +}}} + +#endif // BOOST_TASKS_SPIN_FUTURE_H diff --git a/boost/task/spin/manual_reset_event.hpp b/boost/task/spin/manual_reset_event.hpp new file mode 100644 index 00000000..dc0238ab --- /dev/null +++ b/boost/task/spin/manual_reset_event.hpp @@ -0,0 +1,56 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_SPIN_MANUAL_RESET_EVENT_H +#define BOOST_TASKS_SPIN_MANUAL_RESET_EVENT_H + +#include + +#include +#include +#include + +#include +#include + +namespace boost { +namespace tasks { +namespace spin { + +class BOOST_TASKS_DECL manual_reset_event : private noncopyable +{ +private: + enum state + { + SET = 0, + RESET + }; + + atomic< state > state_; + atomic< std::size_t > waiters_; + mutex enter_mtx_; + +public: + explicit manual_reset_event( bool = false); + + void set(); + + void reset(); + + void wait(); + + bool try_wait(); + + bool timed_wait( system_time const&); + + template< typename TimeDuration > + bool timed_wait( TimeDuration const& rel_time) + { return timed_wait( get_system_time() + rel_time); } +}; + +}}} + +#endif // BOOST_TASKS_SPIN_MANUAL_RESET_EVENT_H diff --git a/boost/task/spin/mutex.hpp b/boost/task/spin/mutex.hpp new file mode 100644 index 00000000..57907e04 --- /dev/null +++ b/boost/task/spin/mutex.hpp @@ -0,0 +1,56 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// based on boost::interprocess::sync::interprocess_spin::mutex + +#ifndef BOOST_TASKS_SPIN_MUTEX_H +#define BOOST_TASKS_SPIN_MUTEX_H + +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasks { +namespace spin { + +class BOOST_TASKS_DECL mutex : private noncopyable +{ +private: + enum state + { + LOCKED = 0, + UNLOCKED + }; + + atomic< state > state_; + +public: + typedef unique_lock< mutex > scoped_lock; + + mutex(); + + void lock(); + + bool try_lock(); + + bool timed_lock( system_time const& abs_time); + + template< typename TimeDuration > + bool timed_lock( TimeDuration const& rel_time) + { return timed_lock( get_system_time() + rel_time); } + + void unlock(); +}; + +typedef mutex try_mutex; + +}}} + +#endif // BOOST_TASKS_SPIN_MUTEX_H diff --git a/boost/task/spin/unbounded_channel.hpp b/boost/task/spin/unbounded_channel.hpp new file mode 100644 index 00000000..6bf70795 --- /dev/null +++ b/boost/task/spin/unbounded_channel.hpp @@ -0,0 +1,273 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_SPIN_UNBOUNDED_CHANNEL_H +#define BOOST_TASKS_SPIN_UNBOUNDED_CHANNEL_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace boost { +namespace tasks { +namespace spin { +namespace detail { + +template< typename T > +struct unbounded_channel_base_node +{ + typedef intrusive_ptr< unbounded_channel_base_node > ptr; + + atomic< std::size_t > use_count; + T va; + ptr next; + + unbounded_channel_base_node() : + use_count( 0), + va(), + next() + {} +}; + +template< typename T > +void intrusive_ptr_add_ref( unbounded_channel_base_node< T > * p) +{ p->use_count.fetch_add( 1, memory_order_relaxed); } + +template< typename T > +void intrusive_ptr_release( unbounded_channel_base_node< T > * p) +{ + if ( p->use_count.fetch_sub( 1, memory_order_release) == 1) + { + atomic_thread_fence( memory_order_acquire); + delete p; + } +} + +template< typename T > +class unbounded_channel_base : private noncopyable +{ +public: + typedef optional< T > value_type; + +private: + typedef unbounded_channel_base_node< value_type > node_type; + + template< typename X > + friend void intrusive_ptr_add_ref( unbounded_channel_base< X > * p); + template< typename X > + friend void intrusive_ptr_release( unbounded_channel_base< X > * p); + + enum state + { + ACTIVE = 0, + DEACTIVE + }; + + atomic< state > state_; + typename node_type::ptr head_; + mutable mutex head_mtx_; + typename node_type::ptr tail_; + mutable mutex tail_mtx_; + condition not_empty_cond_; + atomic< std::size_t > use_count_; + + bool active_() const + { return ACTIVE == state_.load(); } + + void deactivate_() + { state_.store( DEACTIVE); } + + bool empty_() const + { return head_ == get_tail_(); } + + typename node_type::ptr get_tail_() const + { + mutex::scoped_lock lk( tail_mtx_); + typename node_type::ptr tmp = tail_; + return tmp; + } + + typename node_type::ptr pop_head_() + { + typename node_type::ptr old_head = head_; + head_ = old_head->next; + return old_head; + } + +public: + unbounded_channel_base() : + state_( ACTIVE), + head_( new node_type() ), + head_mtx_(), + tail_( head_), + tail_mtx_(), + not_empty_cond_(), + use_count_( 0) + {} + + bool active() const + { return active_(); } + + void deactivate() + { + mutex::scoped_lock lk( head_mtx_); + deactivate_(); + not_empty_cond_.notify_all(); + } + + bool empty() const + { + mutex::scoped_lock lk( head_mtx_); + return empty_(); + } + + void put( T const& t) + { + typename node_type::ptr new_node( new node_type() ); + { + mutex::scoped_lock lk( tail_mtx_); + + if ( ! active_() ) + throw std::runtime_error("queue is not active"); + + tail_->va = t; + tail_->next = new_node; + tail_ = new_node; + } + not_empty_cond_.notify_one(); + } + + bool take( value_type & va) + { + mutex::scoped_lock lk( head_mtx_); + bool empty = empty_(); + if ( ! active_() && empty) + return false; + if ( empty) + { + try + { + while ( active_() && empty_() ) + not_empty_cond_.wait( lk); + } + catch ( task_interrupted const&) + { return false; } + } + + if ( ! active_() && empty_() ) + return false; + swap( va, head_->va); + pop_head_(); + return va; + } + + bool take( value_type & va, system_time const& abs_time) + { + mutex::scoped_lock lk( head_mtx_); + bool empty = empty_(); + if ( ! active_() && empty) + return false; + if ( empty) + { + try + { + while ( active_() && empty_() ) + if ( ! not_empty_cond_.timed_wait( lk, abs_time) ) + return false; + } + catch ( task_interrupted const&) + { return false; } + } + if ( ! active_() && empty_() ) + return false; + swap( va, head_->va); + pop_head_(); + return va; + } + + bool try_take( value_type & va) + { + mutex::scoped_lock lk( head_mtx_); + if ( empty_() ) + return false; + swap( va, head_->va); + pop_head_(); + return va; + } +}; + +template< typename T > +void intrusive_ptr_add_ref( unbounded_channel_base< T > * p) +{ p->use_count_.fetch_add( 1, memory_order_relaxed); } + +template< typename T > +void intrusive_ptr_release( unbounded_channel_base< T > * p) +{ + if ( p->use_count_.fetch_sub( 1, memory_order_release) == 1) + { + atomic_thread_fence( memory_order_acquire); + delete p; + } +} + +} + +template< typename T > +class unbounded_channel +{ +private: + typedef typename detail::unbounded_channel_base< T >::value_type value_type; + + intrusive_ptr< detail::unbounded_channel_base< T > > base_; + +public: + unbounded_channel() : + base_( new detail::unbounded_channel_base< T >() ) + {} + + bool active() const + { return base_->active(); } + + void deactivate() + { base_->deactivate(); } + + bool empty() + { return base_->empty(); } + + void put( T const& t) + { base_->put( t); } + + bool take( value_type & va) + { return base_->take( va); } + + bool take( value_type & va, system_time const& abs_time) + { return base_->take( va, abs_time); } + + template< typename TimeDuration > + bool take( value_type & va, TimeDuration const& rel_time) + { return base_->take( va, get_system_time() + rel_time); } + + bool try_take( value_type & va) + { return base_->try_take( va); } +}; + +}}} + +#include + +#endif // BOOST_TASKS_SPIN_UNBOUNDED_CHANNEL_H diff --git a/boost/task/stacksize.hpp b/boost/task/stacksize.hpp new file mode 100755 index 00000000..f07c467d --- /dev/null +++ b/boost/task/stacksize.hpp @@ -0,0 +1,43 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_STACKSIZE_H +#define BOOST_TASKS_STACKSIZE_H + +#include + +#include + +#include + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251 4275) +# endif + +namespace boost { +namespace tasks { + +class BOOST_TASKS_DECL stacksize +{ +private: + std::size_t value_; + +public: + explicit stacksize( std::size_t value); + + operator std::size_t () const; +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#include + +#endif // BOOST_TASKS_STACKSIZE_H diff --git a/boost/task/static_pool.hpp b/boost/task/static_pool.hpp new file mode 100644 index 00000000..8ab67cfe --- /dev/null +++ b/boost/task/static_pool.hpp @@ -0,0 +1,208 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_STATIC_POOL_H +#define BOOST_TASKS_STATIC_POOL_H + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasks { + +template< + typename Queue, + typename UMS = tasklets::round_robin +> +class static_pool +{ +public: + typedef Queue queue_type; + typedef UMS ums_type; + +private: + typedef detail::pool_base< queue_type, ums_type > base_type; + + template< typename T, typename X > + friend class detail::worker_object; + + BOOST_MOVABLE_BUT_NOT_COPYABLE( static_pool); + +# if defined(BOOST_HAS_PROCESSOR_BINDINGS) + struct tag_bind_to_processors {}; +# endif + + intrusive_ptr< base_type > pool_; + +public: + static_pool() : + pool_() + {} + + explicit static_pool( + poolsize const& psize, + stacksize const& stack_size = stacksize( tasklet::default_stacksize) ) : + pool_( new base_type( psize, stack_size) ) + {} + + explicit static_pool( + poolsize const& psize, + high_watermark const& hwm, + low_watermark const& lwm, + stacksize const& stack_size = stacksize( tasklet::default_stacksize) ) : + pool_( new base_type( psize, hwm, lwm, stack_size) ) + {} + +# if defined(BOOST_HAS_PROCESSOR_BINDINGS) + explicit static_pool( + tag_bind_to_processors, + stacksize const& stack_size = stacksize( tasklet::default_stacksize) ) : + pool_( new base_type( stack_size) ) + {} + + explicit static_pool( + tag_bind_to_processors, + high_watermark const& hwm, + low_watermark const& lwm, + stacksize const& stack_size = stacksize( tasklet::default_stacksize) ) : + pool_( new base_type( hwm, lwm, stack_size) ) + {} + + static tag_bind_to_processors bind_to_processors() + { return tag_bind_to_processors(); } +# endif + + static_pool( BOOST_RV_REF( static_pool) other) : + pool_() + { pool_.swap( other.pool_); } + + static_pool & operator=( BOOST_RV_REF( static_pool) other) + { + static_pool tmp( other); + swap( tmp); + return * this; + } + + void interrupt_all_worker() + { + if ( ! pool_) + throw pool_moved(); + pool_->interrupt_all_worker(); + } + + void shutdown() + { + if ( ! pool_) + throw pool_moved(); + pool_->shutdown(); + } + + const void shutdown_now() + { + if ( ! pool_) + throw pool_moved(); + pool_->shutdown_now(); + } + + std::size_t size() + { + if ( ! pool_) + throw pool_moved(); + return pool_->size(); + } + + bool closed() + { + if ( ! pool_) + throw pool_moved(); + return pool_->closed(); + } + + std::size_t upper_bound() + { + if ( ! pool_) + throw pool_moved(); + return pool_->upper_bound(); + } + + void upper_bound( high_watermark const& hwm) + { + if ( ! pool_) + throw pool_moved(); + pool_->upper_bound( hwm); + } + + std::size_t lower_bound() + { + if ( ! pool_) + throw pool_moved(); + return pool_->lower_bound(); + } + + void lower_bound( low_watermark const lwm) + { + if ( ! pool_) + throw pool_moved(); + pool_->lower_bound( lwm); + } + + template< typename R > + handle< R > submit( task< R > t) + { + if ( ! pool_) + throw pool_moved(); + return pool_->submit( boost::move( t) ); + } + + template< typename R, typename Attr > + handle< R > submit( task< R > t, Attr const& attr) + { + if ( ! pool_) + throw pool_moved(); + return pool_->submit( boost::move( t), attr); + } + + typedef typename shared_ptr< base_type >::unspecified_bool_type unspecified_bool_type; + + operator unspecified_bool_type() const // throw() + { return pool_; } + + bool operator!() const // throw() + { return ! pool_; } + + void swap( static_pool & other) // throw() + { pool_.swap( other.pool_); } +}; + +} + +template< typename Queue, typename UMS > +void swap( tasks::static_pool< Queue, UMS > & l, tasks::static_pool< Queue, UMS > & r) +{ return l.swap( r); } + +} + +#include + +#endif // BOOST_TASKS_STATIC_POOL_H + diff --git a/boost/task/task.hpp b/boost/task/task.hpp new file mode 100644 index 00000000..ec5ed05c --- /dev/null +++ b/boost/task/task.hpp @@ -0,0 +1,497 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_TASK_H +#define BOOST_TASKS_TASK_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace boost { +namespace tasks { +namespace detail { + +template< typename R > +struct promise_adaptor +{ + template< typename X > + friend void intrusive_ptr_add_ref( promise_adaptor< X > *); + template< typename X > + friend void intrusive_ptr_release( promise_adaptor< X > *); + + typedef intrusive_ptr< promise_adaptor > ptr; + + atomic< unsigned int > use_count; + + virtual ~promise_adaptor() {} + + virtual void set_value( + typename tasks::detail::future_traits< R >::source_reference_type) = 0; + + virtual void set_value( + typename tasks::detail::future_traits< R >::rvalue_source_type) = 0; + + virtual void set_exception( exception_ptr) = 0; +}; + +template<> +struct promise_adaptor< void > +{ + template< typename X > + friend void intrusive_ptr_add_ref( promise_adaptor< X > *); + template< typename X > + friend void intrusive_ptr_release( promise_adaptor< X > *); + + typedef intrusive_ptr< promise_adaptor > ptr; + + atomic< unsigned int > use_count; + + virtual ~promise_adaptor() {} + + virtual void set_value() = 0; + + virtual void set_exception( exception_ptr) = 0; +}; + +template< typename R > +void intrusive_ptr_add_ref( promise_adaptor< R > * p) +{ p->use_count.fetch_add( 1, memory_order_relaxed); } + +template< typename R > +void intrusive_ptr_release( promise_adaptor< R > * p) +{ + if ( p->use_count.fetch_sub( 1, memory_order_release) == 1) + { + atomic_thread_fence( memory_order_acquire); + delete p; + } +} + +template< typename R, template< typename > class Promise > +class promise_wrapper; + +template< typename R > +class promise_wrapper< R, promise > : public promise_adaptor< R > +{ +private: + promise< R > prom_; + +public: +#ifdef BOOST_HAS_RVALUE_REFS + promise_wrapper( promise< R > && prom) : + prom_( prom) + {} +#else + promise_wrapper( boost::detail::thread_move_t< promise< R > > prom) : + prom_( prom) + {} +#endif + + void set_value( + typename tasks::detail::future_traits< R >::source_reference_type r) + { prom_.set_value( r); }; + + void set_value( + typename tasks::detail::future_traits< R >::rvalue_source_type r) + { prom_.set_value( r); }; + + void set_exception( exception_ptr p) + { prom_.set_exception( p); } +}; + +template<> +class promise_wrapper< void, promise > : public promise_adaptor< void > +{ +private: + promise< void > prom_; + +public: +#ifdef BOOST_HAS_RVALUE_REFS + promise_wrapper( promise< void > && prom) : + prom_( prom) + {} +#else + promise_wrapper( boost::detail::thread_move_t< promise< void > > prom) : + prom_( prom) + {} +#endif + + void set_value() + { prom_.set_value(); }; + + void set_exception( exception_ptr p) + { prom_.set_exception( p); } +}; + +template< typename R > +class promise_wrapper< R, spin::promise > : public promise_adaptor< R > +{ +private: + spin::promise< R > prom_; + +public: + promise_wrapper() : + prom_() + {} + + promise_wrapper( BOOST_RV_REF( spin::promise< R >) prom) : + prom_( prom) + {} + + void set_value( + typename tasks::detail::future_traits< R >::source_reference_type r) + { prom_.set_value( r); }; + + void set_value( + typename tasks::detail::future_traits< R >::rvalue_source_type r) + { prom_.set_value( r); }; + + void set_exception( exception_ptr p) + { prom_.set_exception( p); } +}; + +template<> +class promise_wrapper< void, spin::promise > : public promise_adaptor< void > +{ +private: + spin::promise< void > prom_; + +public: + promise_wrapper() : + prom_() + {} + + promise_wrapper( BOOST_RV_REF( spin::promise< void >) prom) : + prom_( prom) + {} + + void set_value() + { prom_.set_value(); }; + + void set_exception( exception_ptr p) + { prom_.set_exception( p); } +}; + +template< typename R > +class task_base +{ +private: + template< typename X > + friend void intrusive_ptr_add_ref( task_base< X > *); + template< typename X > + friend void intrusive_ptr_release( task_base< X > *); + + atomic< unsigned int > use_count_; + +protected: + bool done_; + typename promise_adaptor< R >::ptr prom_; + + virtual void do_run() = 0; + +public: + task_base() : + done_( false), + prom_( new promise_wrapper< R, spin::promise >() ) + {} + + virtual ~task_base() {} + + void run() + { + if ( this->done_) throw task_already_executed(); + do_run(); + done_ = true; + } + + void set_promise( typename promise_adaptor< R >::ptr prom) + { prom_ = prom; } +}; + +template< typename R > +void intrusive_ptr_add_ref( task_base< R > * p) +{ p->use_count_.fetch_add( 1, memory_order_relaxed); } + +template< typename R > +void intrusive_ptr_release( task_base< R > * p) +{ + if ( p->use_count_.fetch_sub( 1, memory_order_release) == 1) + { + atomic_thread_fence( memory_order_acquire); + delete p; + } +} + +template< typename R, typename Fn > +class task_wrapper : public task_base< R > +{ +private: + Fn fn_; + + void do_run() + { + try + { this->prom_->set_value( fn_() ); } + catch ( promise_already_satisfied const&) + { throw task_already_executed(); } + catch ( thread_interrupted const&) + { this->prom_->set_exception( copy_exception( task_interrupted() ) ); } + catch ( task_interrupted const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( boost::exception const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::ios_base::failure const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::domain_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::invalid_argument const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::length_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::out_of_range const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::logic_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::overflow_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::range_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::underflow_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::runtime_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::bad_alloc const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::bad_cast const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::bad_typeid const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::bad_exception const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch (...) + { this->prom_->set_exception( current_exception() ); } + } + +public: + task_wrapper( Fn const& fn) : + task_base< R >(), fn_( fn) + {} +}; + +template< typename Fn > +class task_wrapper< void, Fn > : public task_base< void > +{ +private: + Fn fn_; + + void do_run() + { + try + { + fn_(); + this->prom_->set_value(); + } + catch ( promise_already_satisfied const&) + { throw task_already_executed(); } + catch ( thread_interrupted const&) + { this->prom_->set_exception( copy_exception( task_interrupted() ) ); } + catch ( task_interrupted const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( boost::exception const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::ios_base::failure const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::domain_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::invalid_argument const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::length_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::out_of_range const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::logic_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::overflow_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::range_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::underflow_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::runtime_error const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::bad_alloc const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::bad_cast const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::bad_typeid const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch ( std::bad_exception const& e) + { this->prom_->set_exception( copy_exception( e) ); } + catch (...) + { this->prom_->set_exception( current_exception() ); } + } + +public: + task_wrapper( Fn const& fn) : + task_base< void >(), fn_( fn) + {} +}; + +} + +template< typename R > +class task +{ +private: + template< typename T, typename P > + friend class detail::callable_object; + + BOOST_MOVABLE_BUT_NOT_COPYABLE( task); + + intrusive_ptr< detail::task_base< R > > task_; + +// TODO: if boost.thread uses boost.move +// re-work set_promise +#ifdef BOOST_HAS_RVALUE_REFS + void set_promise( promise< R > && prom) + { + if ( ! task_) throw task_moved(); + task_->set_promise( + new detail::promise_wrapper< R, promise >( prom) ); + } +#else + void set_promise( boost::detail::thread_move_t< promise< R > > prom) + { + if ( ! task_) throw task_moved(); + task_->set_promise( + new detail::promise_wrapper< R, promise >( prom) ); + } +#endif + + void set_promise( BOOST_RV_REF( spin::promise< R >) prom) + { + if ( ! task_) throw task_moved(); + task_->set_promise( + new detail::promise_wrapper< R, spin::promise >( prom) ); + } + +public: + task() : + task_() + {} + + explicit task( R( * fn)()) : + task_( new detail::task_wrapper< R, R( *)() >( fn) ) + {} + + template< typename Fn > + explicit task( BOOST_RV_REF( Fn) fn) : + task_( new detail::task_wrapper< R, Fn >( fn) ) + {} + + task( BOOST_RV_REF( task) other) : + task_() + { task_.swap( other.task_); } + + task & operator=( BOOST_RV_REF( task) other) + { + task tmp( other); + swap( tmp); + return * this; + } + +# ifndef BOOST_TASKS_MAX_ARITY +# define BOOST_TASKS_MAX_ARITY 10 +# endif + +# define BOOST_TASKS_ARG(z, n, unused) \ + BOOST_PP_CAT(A, n) BOOST_PP_CAT(a, n) +# define BOOST_ENUM_TASK_ARGS(n) BOOST_PP_ENUM(n, BOOST_TASKS_ARG, ~) + +# define BOOST_TASKS_CTOR(z, n, unused) \ +template< \ + typename Fn, \ + BOOST_PP_ENUM_PARAMS(n, typename A) \ +> \ +explicit task( Fn fn, BOOST_ENUM_TASK_ARGS(n)) \ + : task_( \ + new detail::task_wrapper< R, function< R() > >( \ + bind( fn, BOOST_PP_ENUM_PARAMS(n, a)) ) ) \ + {} + +BOOST_PP_REPEAT_FROM_TO( 1, BOOST_TASKS_MAX_ARITY, BOOST_TASKS_CTOR, ~) + +# undef BOOST_TASKS_CTOR + + void operator()() + { + if ( ! task_) throw task_moved(); + task_->run(); + } + + typedef typename shared_ptr< detail::task_base< R > >::unspecified_bool_type + unspecified_bool_type; + + operator unspecified_bool_type() const // throw() + { return task_; } + + bool operator!() const // throw() + { return ! task_; } + + void swap( task & other) // throw() + { task_.swap( other.task_); } +}; + +template< typename Fn > +task< typename result_of< Fn() >::type > make_task( Fn fn) +{ return task< typename boost::result_of< Fn() >::type >( fn); } + +# define BOOST_TASKS_MAKE_TASK_FUNCTION(z, n, unused) \ +template< \ + typename Fn, \ + BOOST_PP_ENUM_PARAMS(n, typename A) \ +> \ +task< typename result_of< Fn( BOOST_PP_ENUM_PARAMS(n, A)) >::type > \ +make_task( Fn fn, BOOST_ENUM_TASK_ARGS(n)) \ +{ \ + return task< \ + typename result_of< Fn( BOOST_PP_ENUM_PARAMS(n, A)) >::type >( \ + fn, BOOST_PP_ENUM_PARAMS(n, a)); \ +} + +BOOST_PP_REPEAT_FROM_TO(1,BOOST_TASKS_MAX_ARITY,BOOST_TASKS_MAKE_TASK_FUNCTION, ~) + +# undef BOOST_TASKS_MAKE_TASK_FUNCTION +# undef BOOST_ENUM_TASK_ARGS +# undef BOOST_TASKS_ARG +# undef BOOST_TASKS_MAX_ARITY + +} + +using tasks::task; + +template< typename R > +void swap( task< R > & l, task< R > & r) +{ return l.swap( r); } + +} + +#include + +#endif // BOOST_TASKS_TASK_H diff --git a/boost/task/unbounded_fifo.hpp b/boost/task/unbounded_fifo.hpp new file mode 100644 index 00000000..216fdb8f --- /dev/null +++ b/boost/task/unbounded_fifo.hpp @@ -0,0 +1,135 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_UNBOUNDED_FIFO_H +#define BOOST_TASKS_UNBOUNDED_FIFO_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasks { + +class unbounded_fifo +{ +public: + typedef detail::has_no_attribute attribute_tag_type; + typedef callable value_type; + +private: + struct node + { + typedef shared_ptr< node > sptr_t; + + value_type va; + sptr_t next; + }; + + enum state + { + ACTIVE = 0, + DEACTIVE + }; + + atomic< state > state_; + node::sptr_t head_; + mutable mutex head_mtx_; + node::sptr_t tail_; + mutable mutex tail_mtx_; + fast_semaphore & fsem_; + + bool active_() const + { return ACTIVE == state_.load(); } + + void deactivate_() + { state_.store( DEACTIVE); } + + bool empty_() const + { return head_ == get_tail_(); } + + node::sptr_t get_tail_() const + { + lock_guard< mutex > lk( tail_mtx_); + node::sptr_t tmp = tail_; + return tmp; + } + + node::sptr_t pop_head_() + { + node::sptr_t old_head = head_; + head_ = old_head->next; + return old_head; + } + +public: + unbounded_fifo( fast_semaphore & fsem) : + state_( ACTIVE), + head_( new node), + head_mtx_(), + tail_( head_), + tail_mtx_(), + fsem_( fsem) + {} + + bool active() const + { return active_(); } + + void deactivate() + { + unique_lock< mutex > lk( head_mtx_); + deactivate_(); + } + + bool empty() const + { + unique_lock< mutex > lk( head_mtx_); + return empty_(); + } + + void put( value_type const& va) + { + node::sptr_t new_node( new node); + { + unique_lock< mutex > lk( tail_mtx_); + if ( ! active_() ) + throw task_rejected("queue is not active"); + tail_->va = va; + tail_->next = new_node; + tail_ = new_node; + } + fsem_.post(); + } + + bool try_take( value_type & va) + { + unique_lock< mutex > lk( head_mtx_); + if ( empty_() ) + return false; + va.swap( head_->va); + pop_head_(); + return ! va.empty(); + } +}; + +}} + +#include + +#endif // BOOST_TASKS_UNBOUNDED_FIFO_H diff --git a/boost/task/unbounded_prio_queue.hpp b/boost/task/unbounded_prio_queue.hpp new file mode 100644 index 00000000..13251b27 --- /dev/null +++ b/boost/task/unbounded_prio_queue.hpp @@ -0,0 +1,152 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_UNBOUNDED_PRIO_QUEUE_H +#define BOOST_TASKS_UNBOUNDED_PRIO_QUEUE_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasks { + +template< + typename Attr, + typename Comp = std::less< Attr > +> +class unbounded_prio_queue +{ +public: + typedef detail::has_attribute attribute_tag_type; + typedef Attr attribute_type; + + struct value_type + { + callable ca; + attribute_type attr; + + value_type( + callable const& ca_, + attribute_type const& attr_) : + ca( ca_), attr( attr_) + { BOOST_ASSERT( ! ca.empty() ); } + + void swap( value_type & other) + { + ca.swap( other.ca); + std::swap( attr, other.attr); + } + }; + +private: + struct compare : public std::binary_function< value_type, value_type, bool > + { + bool operator()( value_type const& va1, value_type const& va2) + { return Comp()( va1.attr, va2.attr); } + }; + + typedef std::priority_queue< + value_type, + std::deque< value_type >, + compare + > queue_type; + + enum state + { + ACTIVE = 0, + DEACTIVE + }; + + atomic< state > state_; + queue_type queue_; + mutable shared_mutex mtx_; + fast_semaphore & fsem_; + + bool active_() const + { return ACTIVE == state_.load(); } + + void deactivate_() + { state_.store( DEACTIVE); } + + bool empty_() const + { return queue_.empty(); } + + void put_( value_type const& va) + { + if ( ! active_() ) + throw task_rejected("queue is not active"); + queue_.push( va); + fsem_.post(); + } + + bool try_take_( callable & ca) + { + if ( empty_() ) + return false; + callable tmp( queue_.top().ca); + queue_.pop(); + ca.swap( tmp); + return ! ca.empty(); + } + +public: + unbounded_prio_queue( fast_semaphore & fsem) : + state_( ACTIVE), + queue_(), + mtx_(), + fsem_( fsem) + {} + + bool active() const + { return active_(); } + + void deactivate() + { + unique_lock< shared_mutex > lk( mtx_); + deactivate_(); + } + + bool empty() const + { + shared_lock< shared_mutex > lk( mtx_); + return empty_(); + } + + void put( value_type const& va) + { + unique_lock< shared_mutex > lk( mtx_); + put_( va); + } + + bool try_take( callable & ca) + { + unique_lock< shared_mutex > lk( mtx_); + return try_take_( ca); + } +}; + +}} + +#include + +#endif // BOOST_TASKS_UNBOUNDED_PRIO_QUEUE_H diff --git a/boost/task/unbounded_smart_queue.hpp b/boost/task/unbounded_smart_queue.hpp new file mode 100644 index 00000000..24244d0a --- /dev/null +++ b/boost/task/unbounded_smart_queue.hpp @@ -0,0 +1,163 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_UNBOUNDED_SMART_QUEUE_H +#define BOOST_TASKS_UNBOUNDED_SMART_QUEUE_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasks { + +template< + typename Attr, + typename Comp, + typename Enq = detail::replace_oldest, + typename Deq = detail::take_oldest +> +class unbounded_smart_queue +{ +public: + typedef detail::has_attribute attribute_tag_type; + typedef Attr attribute_type; + + struct value_type + { + callable ca; + attribute_type attr; + + value_type( + callable const& ca_, + attribute_type const& attr_) : + ca( ca_), attr( attr_) + { BOOST_ASSERT( ! ca.empty() ); } + + void swap( value_type & other) + { + ca.swap( other.ca); + std::swap( attr, other.attr); + } + }; + +private: + typedef multi_index::multi_index_container< + value_type, + multi_index::indexed_by< + multi_index::ordered_non_unique< + multi_index::member< + value_type, + Attr, + & value_type::attr + >, + Comp + > + > + > queue_type; + typedef typename queue_type::template nth_index< 0 >::type queue_index; + + enum state + { + ACTIVE = 0, + DEACTIVE + }; + + atomic< state > state_; + queue_type queue_; + queue_index & idx_; + mutable shared_mutex mtx_; + Enq enq_op_; + Deq deq_op_; + fast_semaphore & fsem_; + + bool active_() const + { return ACTIVE == state_.load(); } + + void deactivate_() + { state_.store( DEACTIVE); } + + bool empty_() const + { return queue_.empty(); } + + void put_( value_type const& va) + { + if ( ! active_() ) + throw task_rejected("queue is not active"); + enq_op_( idx_, va); + fsem_.post(); + } + + bool try_take_( callable & ca) + { + if ( empty_() ) + return false; + deq_op_( idx_, ca); + return ! ca.empty(); + } + +public: + unbounded_smart_queue( fast_semaphore & fsem) : + state_( ACTIVE), + queue_(), + idx_( queue_.get< 0 >() ), + mtx_(), + enq_op_(), + deq_op_(), + fsem_( fsem) + {} + + bool active() const + { return active_(); } + + void deactivate() + { + unique_lock< shared_mutex > lk( mtx_); + deactivate_(); + } + + bool empty() const + { + shared_lock< shared_mutex > lk( mtx_); + return empty_(); + } + + void put( value_type const& va) + { + unique_lock< shared_mutex > lk( mtx_); + put_( va); + } + + bool try_take( callable & ca) + { + unique_lock< shared_mutex > lk( mtx_); + return try_take_( ca); + } +}; + +}} + +#include + +#endif // BOOST_TASKS_UNBOUNDED_SMART_QUEUE_H diff --git a/boost/task/utility.hpp b/boost/task/utility.hpp new file mode 100644 index 00000000..3cb64e57 --- /dev/null +++ b/boost/task/utility.hpp @@ -0,0 +1,44 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt)txt) + +#ifndef BOOST_TASKS_UTILITY_H +#define BOOST_TASKS_UTILITY_H + +#include +#include + +#include + +#include + +namespace boost { +namespace this_task { + +inline +void yield() +{ + tasks::detail::worker * w( tasks::detail::worker::tss_get() ); + BOOST_ASSERT( w); + w->yield(); +} + +inline +bool runs_in_pool() +{ return tasks::detail::worker::tss_get() != 0; } + +inline +thread::id worker_id() +{ + tasks::detail::worker * w( tasks::detail::worker::tss_get() ); + BOOST_ASSERT( w); + return w->get_id(); +} + +}} + +#include + +#endif // BOOST_TASKS_UTILITY_H diff --git a/boost/task/watermark.hpp b/boost/task/watermark.hpp new file mode 100644 index 00000000..829e1cc2 --- /dev/null +++ b/boost/task/watermark.hpp @@ -0,0 +1,54 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKS_WATER_MARK_H +#define BOOST_TASKS_WATER_MARK_H + +#include + +#include + +#include + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251 4275) +# endif + +namespace boost { +namespace tasks { + +class BOOST_TASKS_DECL high_watermark +{ +private: + std::size_t value_; + +public: + explicit high_watermark( std::size_t value); + + operator std::size_t () const; +}; + +class BOOST_TASKS_DECL low_watermark +{ +private: + std::size_t value_; + +public: + explicit low_watermark( std::size_t value); + + operator std::size_t () const; +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#include + +#endif // BOOST_TASKS_WATER_MARK_H diff --git a/boost/tasklet.hpp b/boost/tasklet.hpp new file mode 100644 index 00000000..d3901708 --- /dev/null +++ b/boost/tasklet.hpp @@ -0,0 +1,27 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLET_H +#define BOOST_TASKLET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // BOOST_TASKLET_H diff --git a/boost/tasklet/auto_reset_event.hpp b/boost/tasklet/auto_reset_event.hpp new file mode 100644 index 00000000..3cc9330c --- /dev/null +++ b/boost/tasklet/auto_reset_event.hpp @@ -0,0 +1,59 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_AUTO_RESET_EVENT_H +#define BOOST_TASKLETS_AUTO_RESET_EVENT_H + +#include +#include +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4355 4251 4275) +# endif + +namespace boost { +namespace tasklets { + +class BOOST_TASKLET_DECL auto_reset_event : private noncopyable +{ +private: + enum state + { + SET = 0, + RESET + }; + + atomic< state > state_; + +public: + explicit auto_reset_event( bool = false); + + void set(); + + void wait(); + + bool try_wait(); +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_AUTO_RESET_EVENT_H diff --git a/boost/tasklet/barrier.hpp b/boost/tasklet/barrier.hpp new file mode 100644 index 00000000..9efb14c9 --- /dev/null +++ b/boost/tasklet/barrier.hpp @@ -0,0 +1,56 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_BARRIER_H +#define BOOST_TASKLETS_BARRIER_H + +#include + +#include +#include + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4275) +# endif + +namespace boost { +namespace tasklets { + +class BOOST_TASKLET_DECL barrier : private noncopyable +{ +private: + std::size_t initial_; + std::size_t current_; + bool cycle_; + mutex mtx_; + condition cond_; + +public: + barrier( std::size_t); + + bool wait(); +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_BARRIER_H diff --git a/boost/tasklet/bounded_channel.hpp b/boost/tasklet/bounded_channel.hpp new file mode 100644 index 00000000..0af58560 --- /dev/null +++ b/boost/tasklet/bounded_channel.hpp @@ -0,0 +1,265 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_BOUNDED_CHANNEL_H +#define BOOST_TASKLETS_BOUNDED_CHANNEL_H + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace tasklets { +namespace detail { + +template< typename T > +struct bounded_channel_node +{ + typedef intrusive_ptr< bounded_channel_node > ptr; + + atomic< std::size_t > use_count; + T va; + ptr next; + + bounded_channel_node() : + use_count( 0), + va(), + next() + {} +}; + +template< typename T > +void intrusive_ptr_add_ref( bounded_channel_node< T > * p) +{ p->use_count.fetch_add( 1, memory_order_relaxed); } + +template< typename T > +void intrusive_ptr_release( bounded_channel_node< T > * p) +{ + if ( p->use_count.fetch_sub( 1, memory_order_release) == 1) + { + atomic_thread_fence( memory_order_acquire); + delete p; + } +} + +} + +template< typename T > +class bounded_channel : private noncopyable +{ +public: + typedef optional< T > value_type; + +private: + typedef detail::bounded_channel_node< value_type > node_type; + + template< typename X > + friend void intrusive_ptr_add_ref( bounded_channel< X > * p); + template< typename X > + friend void intrusive_ptr_release( bounded_channel< X > * p); + + enum state + { + ACTIVE = 0, + DEACTIVE + }; + + atomic< state > state_; + atomic< std::size_t > count_; + typename node_type::ptr head_; + mutable mutex head_mtx_; + typename node_type::ptr tail_; + mutable mutex tail_mtx_; + condition not_empty_cond_; + condition not_full_cond_; + unsigned int hwm_; + unsigned int lwm_; + atomic< std::size_t > use_count_; + + bool active_() const + { return ACTIVE == state_.load(); } + + void deactivate_() + { state_.store( DEACTIVE); } + + std::size_t size_() const + { return count_.load(); } + + bool empty_() const + { return head_ == get_tail_(); } + + bool full_() const + { return size_() >= hwm_; } + + typename node_type::ptr get_tail_() const + { + mutex::scoped_lock lk( tail_mtx_); + typename node_type::ptr tmp = tail_; + return tmp; + } + + typename node_type::ptr pop_head_() + { + typename node_type::ptr old_head = head_; + head_ = old_head->next; + count_.fetch_sub( 1); + return old_head; + } + +public: + bounded_channel( + std::size_t hwm, + std::size_t lwm) : + state_( ACTIVE), + count_( 0), + head_( new node_type() ), + head_mtx_(), + tail_( head_), + tail_mtx_(), + not_empty_cond_(), + not_full_cond_(), + hwm_( hwm), + lwm_( lwm), + use_count_( 0) + { + if ( hwm_ < lwm_) + throw invalid_watermark(); + } + + bounded_channel( std::size_t wm) : + state_( ACTIVE), + count_( 0), + head_( new node_type() ), + head_mtx_(), + tail_( head_), + tail_mtx_(), + not_empty_cond_(), + not_full_cond_(), + hwm_( wm), + lwm_( wm), + use_count_( 0) + {} + + std::size_t upper_bound() const + { return hwm_; } + + std::size_t lower_bound() const + { return lwm_; } + + bool active() const + { return active_(); } + + void deactivate() + { + mutex::scoped_lock head_lk( head_mtx_); + mutex::scoped_lock tail_lk( tail_mtx_); + deactivate_(); + not_empty_cond_.notify_all(); + not_full_cond_.notify_all(); + } + + bool empty() const + { + mutex::scoped_lock lk( head_mtx_); + return empty_(); + } + + void put( T const& t) + { + typename node_type::ptr new_node( new node_type() ); + { + mutex::scoped_lock lk( tail_mtx_); + + if ( full_() ) + { + while ( active_() && full_() ) + not_full_cond_.wait( lk); + } + + if ( ! active_() ) + throw std::runtime_error("queue is not active"); + + tail_->va = t; + tail_->next = new_node; + tail_ = new_node; + count_.fetch_add( 1); + } + not_empty_cond_.notify_one(); + } + + bool take( value_type & va) + { + mutex::scoped_lock lk( head_mtx_); + bool empty = empty_(); + if ( ! active_() && empty) + return false; + if ( empty) + { + try + { + while ( active_() && empty_() ) + not_empty_cond_.wait( lk); + } + catch ( tasklet_interrupted const&) + { return false; } + } + if ( ! active_() && empty_() ) + return false; + swap( va, head_->va); + pop_head_(); + if ( size_() <= lwm_) + { + if ( lwm_ == hwm_) + not_full_cond_.notify_one(); + else + // more than one producer could be waiting + // for submiting an action object + not_full_cond_.notify_all(); + } + return va; + } + + bool try_take( value_type & va) + { + mutex::scoped_lock lk( head_mtx_); + if ( empty_() ) + return false; + swap( va, head_->va); + pop_head_(); + bool valid = va; + if ( valid && size_() <= lwm_) + { + if ( lwm_ == hwm_) + not_full_cond_.notify_one(); + else + // more than one producer could be waiting + // in order to submit an task + not_full_cond_.notify_all(); + } + return valid; + } +}; + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_BOUNDED_CHANNEL_H diff --git a/boost/tasklet/condition.hpp b/boost/tasklet/condition.hpp new file mode 100644 index 00000000..692b9fc2 --- /dev/null +++ b/boost/tasklet/condition.hpp @@ -0,0 +1,198 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// based on boost::interprocess::sync::interprocess_condition + +#ifndef BOOST_TASKLETS_CONDITION_H +#define BOOST_TASKLETS_CONDITION_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4355 4251 4275) +# endif + +namespace boost { +namespace tasklets { + +class BOOST_TASKLET_DECL condition : private noncopyable +{ +private: + enum command + { + SLEEPING = 0, + NOTIFY_ONE, + NOTIFY_ALL + }; + + struct ordered_idx_tag {}; + struct sequenced_idx_tag {}; + + typedef boost::multi_index::multi_index_container< + tasklet, + boost::multi_index::indexed_by< + boost::multi_index::ordered_unique< + boost::multi_index::tag< ordered_idx_tag >, + boost::multi_index::const_mem_fun< + tasklet, tasklet::id, & tasklet::get_id + > + >, + boost::multi_index::sequenced< + boost::multi_index::tag< sequenced_idx_tag > + > + > + > container; + + typedef container::index< ordered_idx_tag >::type ordered_idx; + typedef container::index< sequenced_idx_tag >::type sequenced_idx; + + object::id oid_; + container waiting_tasklets_; + ordered_idx & oidx_; + sequenced_idx & sidx_; + atomic< command > cmd_; + atomic< std::size_t > waiters_; + mutex enter_mtx_; + mutex check_mtx_; + spin_mutex mtx_; + +public: + condition(); + + ~condition(); + + void notify_one(); + + void notify_all(); + + void wait( unique_lock< mutex > & lk) + { + if ( ! lk) + throw lock_error(); + wait( * lk.mutex() ); + } + + template< typename Pred > + void wait( unique_lock< mutex > & lk, Pred pred) + { + if ( ! lk) + throw lock_error(); + + while ( ! pred() ) + wait( * lk.mutex() ); + } + + template< typename LockType > + void wait( LockType & lt) + { + { + mutex::scoped_lock lk( enter_mtx_); + BOOST_ASSERT( lk); + waiters_.fetch_add( 1); + lt.unlock(); + } + + bool unlock_enter_mtx = false; + for (;;) + { + { + spin_mutex::scoped_lock lk( mtx_); + if ( SLEEPING == cmd_.load() ) + { + if ( this_tasklet::runs_as_tasklet() ) + { + tasklet * f( strategy::active_tasklet); + BOOST_ASSERT( f); + oidx_.insert( * f); + BOOST_ASSERT( f->impl_->attached_strategy() ); + f->impl_->attached_strategy()->wait_for_object( oid_, lk); + continue; + } + else + { + lk.unlock(); + this_thread::yield(); + continue; + } + } + } + + mutex::scoped_lock lk( check_mtx_); + BOOST_ASSERT( lk); + + command expected = NOTIFY_ONE; + cmd_.compare_exchange_strong( expected, SLEEPING); + if ( SLEEPING == expected) + continue; + else if ( NOTIFY_ONE == expected) + { + unlock_enter_mtx = true; + waiters_.fetch_sub( 1); + break; + } + else + { + BOOST_ASSERT( NOTIFY_ALL == expected); + unlock_enter_mtx = 1 == waiters_.fetch_sub( 1); + if ( unlock_enter_mtx) + { + expected = NOTIFY_ALL; + cmd_.compare_exchange_strong( expected, SLEEPING); + } + break; + } + } + + if ( unlock_enter_mtx) + enter_mtx_.unlock(); + + lt.lock(); + } + + template< + typename LockType, + typename Pred + > + void wait( LockType & lt, Pred pred) + { + while ( ! pred() ) + wait( lt); + } +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_CONDITION_H diff --git a/boost/tasklet/count_down_event.hpp b/boost/tasklet/count_down_event.hpp new file mode 100644 index 00000000..6479a26a --- /dev/null +++ b/boost/tasklet/count_down_event.hpp @@ -0,0 +1,60 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_COUNT_DOWN_EVENT_H +#define BOOST_TASKLETS_COUNT_DOWN_EVENT_H + +#include + +#include +#include +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4355 4251 4275) +# endif + +namespace boost { +namespace tasklets { + +class BOOST_TASKLET_DECL count_down_event : private noncopyable +{ +private: + std::size_t initial_; + atomic< std::size_t > current_; + +public: + explicit count_down_event( std::size_t); + + std::size_t initial() const; + + std::size_t current() const; + + bool is_set() const; + + void set(); + + void wait(); +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_COUNT_DOWN_EVENT_H diff --git a/boost/tasklet/detail/config.hpp b/boost/tasklet/detail/config.hpp new file mode 100644 index 00000000..4ccabc11 --- /dev/null +++ b/boost/tasklet/detail/config.hpp @@ -0,0 +1,41 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// this file is based on config.hpp of boost.thread + +#ifndef BOOST_TASKLETS_DETAIL_CONFIG_H +#define BOOST_TASKLETS_DETAIL_CONFIG_H + +#include +#include + +#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_TASKLET_DYN_LINK) +# if defined(BOOST_TASKLET_SOURCE) +# define BOOST_TASKLET_DECL BOOST_SYMBOL_EXPORT +# else +# define BOOST_TASKLET_DECL BOOST_SYMBOL_IMPORT +# endif +#else +# define BOOST_TASKLET_DECL +#endif + +#if ! defined(BOOST_TASKLET_SOURCE) && !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_TASKLET_NO_LIB) +# define BOOST_LIB_NAME boost_tasklet +# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_TASKLET_DYN_LINK) +# define BOOST_DYN_LINK +# endif +# include +#endif + +#if defined(_MSC_VER) +# define BOOST_TASKLET_TSSDECL __declspec(thread) +#elif defined(__GNUC__) +# define BOOST_TASKLET_TSSDECL __thread +#else +# error "this platform is not supported" +#endif + +#endif // BOOST_TASKLETS_DETAIL_CONFIG_H diff --git a/boost/tasklet/detail/future_traits.hpp b/boost/tasklet/detail/future_traits.hpp new file mode 100644 index 00000000..4d7d12e4 --- /dev/null +++ b/boost/tasklet/detail/future_traits.hpp @@ -0,0 +1,90 @@ +// (C) Copyright 2008-9 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_DETAIL_FUTURE_TRAITS_H +#define BOOST_TASKLETS_DETAIL_FUTURE_TRAITS_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace tasklets { +namespace detail { + +template +struct future_traits +{ + typedef boost::scoped_ptr storage_type; +#ifdef BOOST_HAS_RVALUE_REFS + typedef T const& source_reference_type; + struct dummy; + typedef typename boost::mpl::if_,dummy&,T&&>::type rvalue_source_type; + typedef typename boost::mpl::if_,T,T&&>::type move_dest_type; +#else + typedef T& source_reference_type; + typedef typename boost::mpl::if_, BOOST_RV_REF( T),T const&>::type rvalue_source_type; + typedef typename boost::mpl::if_,BOOST_RV_REF( T),T>::type move_dest_type; +#endif + + static void init(storage_type& storage,source_reference_type t) + { storage.reset(new T(t)); } + + static void init(storage_type& storage,rvalue_source_type t) + { storage.reset(new T(static_cast(t))); } + + static void cleanup(storage_type& storage) + { storage.reset(); } +}; + +template +struct future_traits +{ + typedef T* storage_type; + typedef T& source_reference_type; + struct rvalue_source_type {}; + typedef T& move_dest_type; + + static void init(storage_type& storage,T& t) + { storage=&t; } + + static void cleanup(storage_type& storage) + { storage=0; } +}; + +template<> +struct future_traits +{ + typedef bool storage_type; + typedef void move_dest_type; + + static void init(storage_type& storage) + { storage=true; } + + static void cleanup(storage_type& storage) + { storage=false; } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_DETAIL_FUTURE_TRAITS_H diff --git a/boost/tasklet/detail/interrupt_flags.hpp b/boost/tasklet/detail/interrupt_flags.hpp new file mode 100644 index 00000000..a880f168 --- /dev/null +++ b/boost/tasklet/detail/interrupt_flags.hpp @@ -0,0 +1,35 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_DETAIL_INTERRUPT_FLAGS_H +#define BOOST_TASKLETS_DETAIL_INTERRUPT_FLAGS_H + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace tasklets { +namespace detail { + +enum interrupt_t_ +{ + INTERRUPTION_DISABLED = 1 << 0, + INTERRUPTION_ENABLED = 1 << 1, + INTERRUPTION_BLOCKED = 1 << 2 +}; + +typedef char interrupt_type; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_DETAIL_INTERRUPT_FLAGS_H diff --git a/boost/tasklet/detail/state_flags.hpp b/boost/tasklet/detail/state_flags.hpp new file mode 100644 index 00000000..9f9c0787 --- /dev/null +++ b/boost/tasklet/detail/state_flags.hpp @@ -0,0 +1,40 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_DETAIL_STATE_FLAGS_H +#define BOOST_TASKLETS_DETAIL_STATE_FLAGS_H + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace tasklets { +namespace detail { + +enum state_t_ +{ + STATE_NOT_STARTED = 1 << 0, + STATE_READY = 1 << 1, + STATE_RUNNING = 1 << 2, + STATE_WAIT_FOR_TASKLET = 1 << 3, + STATE_WAIT_FOR_OBJECT = 1 << 4, + STATE_TERMINATED = 1 << 5 +}; + +typedef char state_type; + +#define IS_ALIVE_BIT_MASK 0x1e + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_DETAIL_STATE_FLAGS_H diff --git a/boost/tasklet/detail/tasklet_base.hpp b/boost/tasklet/detail/tasklet_base.hpp new file mode 100644 index 00000000..4cbdc967 --- /dev/null +++ b/boost/tasklet/detail/tasklet_base.hpp @@ -0,0 +1,97 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_DETAIL_TASKLET_BASE_H +#define BOOST_TASKLETS_DETAIL_TASKLET_BASE_H + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace tasklets { + +class strategy; + +namespace detail { + +BOOST_TASKLET_DECL void trampoline( void *); + +class BOOST_TASKLET_DECL tasklet_base : private noncopyable +{ +public: + typedef intrusive_ptr< tasklet_base > ptr; + + friend BOOST_TASKLET_DECL void trampoline( void *); + + template< typename AllocatorT > + tasklet_base( std::size_t stacksize, AllocatorT const& alloc) : + use_count_( 0), + fib_( trampoline, this, stacksize, alloc), + priority_( 0), + state_( STATE_NOT_STARTED), + interrupt_( INTERRUPTION_DISABLED), + st_( 0) + {} + + virtual ~tasklet_base() {} + + void run(); + + void yield(); + + int priority() const; + + void priority( int); + + state_type state() const; + + void state( state_type); + + interrupt_type & interrupt(); + + void interrupt( interrupt_type); + + strategy * attached_strategy(); + + void attached_strategy( strategy *); + + friend inline void intrusive_ptr_add_ref( tasklet_base * p) + { ++( p->use_count_); } + + friend inline void intrusive_ptr_release( tasklet_base * p) + { if ( 0 == --( p->use_count_) ) delete p; } + +protected: + virtual void exec() = 0; + +private: + std::size_t use_count_; + fiber fib_; + int priority_; + state_type state_; + interrupt_type interrupt_; + strategy * st_; +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_DETAIL_TASKLET_BASE_H diff --git a/boost/tasklet/detail/tasklet_object.hpp b/boost/tasklet/detail/tasklet_object.hpp new file mode 100644 index 00000000..091f7fad --- /dev/null +++ b/boost/tasklet/detail/tasklet_object.hpp @@ -0,0 +1,99 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_DETAIL_TASKLET_OBJECT_H +#define BOOST_TASKLETS_DETAIL_TASKLET_OBJECT_H + +#include + +#include +#include +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace tasklets { +namespace detail { + +template< typename Fn > +class tasklet_object : public tasklet_base +{ +private: + Fn fn_; + + tasklet_object( tasklet_object &); + tasklet_object & operator=( tasklet_object const&); + +public: + template< typename AllocatorT > + tasklet_object( Fn fn, std::size_t stacksize, AllocatorT const& alloc) : + tasklet_base( stacksize, alloc), + fn_( fn) + {} + + template< typename AllocatorT > + tasklet_object( BOOST_RV_REF( Fn) fn, std::size_t stacksize, AllocatorT const& alloc) : + tasklet_base( stacksize, alloc), + fn_( fn) + {} + + void exec() + { fn_(); } +}; + +template< typename Fn > +class tasklet_object< reference_wrapper< Fn > > : public tasklet_base +{ +private: + Fn & fn_; + + tasklet_object( tasklet_object &); + tasklet_object & operator=( tasklet_object const&); + +public: + template< typename AllocatorT > + tasklet_object( reference_wrapper< Fn > fn, std::size_t stacksize, AllocatorT const& alloc) : + tasklet_base( stacksize, alloc), + fn_( fn) + {} + + void exec() + { fn_(); } +}; + +template< typename Fn > +class tasklet_object< const reference_wrapper< Fn > > : public tasklet_base +{ +private: + Fn & fn_; + + tasklet_object( tasklet_object &); + tasklet_object & operator=( tasklet_object const&); + +public: + template< typename AllocatorT > + tasklet_object( const reference_wrapper< Fn > fn, std::size_t stacksize, AllocatorT const& alloc) : + tasklet_base( stacksize, alloc), + fn_( fn) + {} + + void exec() + { fn_(); } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_DETAIL_TASKLET_OBJECT_H diff --git a/boost/tasklet/exceptions.hpp b/boost/tasklet/exceptions.hpp new file mode 100644 index 00000000..c9958e57 --- /dev/null +++ b/boost/tasklet/exceptions.hpp @@ -0,0 +1,127 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_EXCEPTIONS_H +#define BOOST_TASKLETS_EXCEPTIONS_H + +#include +#include + +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace tasklets { + +class tasklet_error : public std::runtime_error +{ +public: + tasklet_error( std::string const& msg) : + std::runtime_error( msg) + {} +}; + +class tasklet_interrupted +{}; + +class tasklet_moved : public std::logic_error +{ +public: + tasklet_moved() : + std::logic_error("tasklet moved") + {} +}; + +class invalid_watermark : public std::runtime_error +{ +public: + invalid_watermark() : + std::runtime_error("invalid watermark") + {} +}; + +class lock_error : public std::logic_error +{ +public: + lock_error() : + std::logic_error("lock invalid") + {} +}; + +class scheduler_error : public std::runtime_error +{ +public: + scheduler_error( std::string const& msg) : + std::runtime_error( msg) + {} +}; + +class future_uninitialized : + public std::logic_error +{ +public: + future_uninitialized() : + std::logic_error("Future Uninitialized") + {} +}; + +class broken_promise : + public std::logic_error +{ +public: + broken_promise() : + std::logic_error("Broken promise") + {} +}; + +class future_already_retrieved : + public std::logic_error +{ +public: + future_already_retrieved() : + std::logic_error("Future already retrieved") + {} +}; + +class promise_already_satisfied : + public std::logic_error +{ +public: + promise_already_satisfied() : + std::logic_error("Promise already satisfied") + {} +}; + +class task_already_started : + public std::logic_error +{ +public: + task_already_started() : + std::logic_error("Task already started") + {} +}; + +class task_moved : + public std::logic_error +{ +public: + task_moved() : + std::logic_error("Task moved") + {} +}; + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_EXCEPTIONS_H diff --git a/boost/tasklet/future.hpp b/boost/tasklet/future.hpp new file mode 100644 index 00000000..7d4ed52e --- /dev/null +++ b/boost/tasklet/future.hpp @@ -0,0 +1,1080 @@ +// (C) Copyright 2008-9 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_FUTURE_HPP +#define BOOST_TASKLETS_FUTURE_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace tasklets { + +namespace future_state { + + enum state { uninitialized, waiting, ready, moved }; + +} + +namespace detail { + +struct future_object_base +{ + boost::exception_ptr exception; + bool done; + mutex mtx; + condition waiters; + typedef std::list waiter_list; + waiter_list external_waiters; + boost::function callback; + + future_object_base(): + done(false) + {} + + virtual ~future_object_base() + {} + + waiter_list::iterator register_external_waiter(condition& cv) + { + boost::unique_lock lock(mtx); + do_callback(lock); + return external_waiters.insert(external_waiters.end(),&cv); + } + + void remove_external_waiter(waiter_list::iterator it) + { + boost::lock_guard lock(mtx); + external_waiters.erase(it); + } + + void mark_finished_internal() + { + done=true; + waiters.notify_all(); + for(waiter_list::const_iterator it=external_waiters.begin(), + end=external_waiters.end();it!=end;++it) + { + (*it)->notify_all(); + } + } + + struct relocker + { + boost::unique_lock& lock; + + relocker(boost::unique_lock& lock_): + lock(lock_) + { + lock.unlock(); + } + ~relocker() + { + lock.lock(); + } + }; + + void do_callback(boost::unique_lock& lock) + { + if(callback && !done) + { + boost::function local_callback=callback; + relocker relock(lock); + local_callback(); + } + } + + void wait(bool rethrow=true) + { + boost::unique_lock lock(mtx); + do_callback(lock); + while(!done) + { + waiters.wait(lock); + } + if(rethrow && exception) + { + boost::rethrow_exception(exception); + } + } + + void mark_exceptional_finish_internal(boost::exception_ptr const& e) + { + exception=e; + mark_finished_internal(); + } + void mark_exceptional_finish() + { + boost::lock_guard lock(mtx); + mark_exceptional_finish_internal(boost::current_exception()); + } + + bool has_value() + { + boost::lock_guard lock(mtx); + return done && !exception; + } + bool has_exception() + { + boost::lock_guard lock(mtx); + return done && exception; + } + + template + void set_wait_callback(F f,U* u) + { + callback=boost::bind(f,boost::ref(*u)); + } + +private: + future_object_base(future_object_base const&); + future_object_base& operator=(future_object_base const&); +}; + +template +struct future_object: future_object_base +{ + typedef typename tasklets::detail::future_traits::storage_type storage_type; + typedef typename tasklets::detail::future_traits::source_reference_type source_reference_type; + typedef typename tasklets::detail::future_traits::rvalue_source_type rvalue_source_type; + typedef typename tasklets::detail::future_traits::move_dest_type move_dest_type; + + storage_type result; + + future_object(): + result(0) + {} + + void mark_finished_with_result_internal(source_reference_type result_) + { + tasklets::detail::future_traits::init(result,result_); + mark_finished_internal(); + } + void mark_finished_with_result_internal(rvalue_source_type result_) + { + tasklets::detail::future_traits::init(result,static_cast(result_)); + mark_finished_internal(); + } + + void mark_finished_with_result(source_reference_type result_) + { + boost::lock_guard lock(mtx); + mark_finished_with_result_internal(result_); + } + void mark_finished_with_result(rvalue_source_type result_) + { + boost::lock_guard lock(mtx); + mark_finished_with_result_internal(result_); + } + + move_dest_type get() + { + wait(); + return *result; + } + + future_state::state get_state() + { + boost::lock_guard guard(mtx); + if(!done) + { + return future_state::waiting; + } + else + { + return future_state::ready; + } + } + +private: + future_object(future_object const&); + future_object& operator=(future_object const&); +}; + +template<> +struct future_object: future_object_base +{ + future_object() + {} + + void mark_finished_with_result_internal() + { + mark_finished_internal(); + } + + void mark_finished_with_result() + { + boost::lock_guard lock(mtx); + mark_finished_with_result_internal(); + } + + void get() + { + wait(); + } + + future_state::state get_state() + { + boost::lock_guard guard(mtx); + if(!done) + { + return future_state::waiting; + } + else + { + return future_state::ready; + } + } + +private: + future_object(future_object const&); + future_object& operator=(future_object const&); +}; + +class future_waiter +{ + struct registered_waiter + { + boost::shared_ptr future; + detail::future_object_base::waiter_list::iterator wait_iterator; + unsigned index; + + registered_waiter(boost::shared_ptr const& future_, + detail::future_object_base::waiter_list::iterator wait_iterator_, + unsigned index_): + future(future_),wait_iterator(wait_iterator_),index(index_) + {} + + }; + + struct all_futures_lock + { + unsigned count; + boost::scoped_array > locks; + + all_futures_lock(std::vector& futures): + count(futures.size()),locks(new boost::unique_lock[count]) + { + for(unsigned i=0;i(futures[i].future->mtx); + } + } + + void lock() + { + boost::lock(locks.get(),locks.get()+count); + } + + void unlock() + { + for(unsigned i=0;i futures; + unsigned future_count; + +public: + future_waiter(): + future_count(0) + {} + + template + void add(F& f) + { + if(f.future) + { + futures.push_back(registered_waiter(f.future,f.future->register_external_waiter(cv),future_count)); + } + ++future_count; + } + + unsigned wait() + { + all_futures_lock lk(futures); + for(;;) + { + for(unsigned i=0;idone) + { + return futures[i].index; + } + } + cv.wait(lk); + } + } + + ~future_waiter() + { + for(unsigned i=0;iremove_external_waiter(futures[i].wait_iterator); + } + } + +}; + +} + +template +class unique_future; + +template +class shared_future; + +template +struct is_future_type +{ + BOOST_STATIC_CONSTANT(bool, value=false); +}; + +template +struct is_future_type > +{ + BOOST_STATIC_CONSTANT(bool, value=true); +}; + +template +struct is_future_type > +{ + BOOST_STATIC_CONSTANT(bool, value=true); +}; + +template +typename boost::disable_if,void>::type wait_for_all(Iterator begin,Iterator end) +{ + for(Iterator current=begin;current!=end;++current) + { + current->wait(); + } +} + +template +typename boost::enable_if,void>::type wait_for_all(F1& f1,F2& f2) +{ + f1.wait(); + f2.wait(); +} + +template +void wait_for_all(F1& f1,F2& f2,F3& f3) +{ + f1.wait(); + f2.wait(); + f3.wait(); +} + +template +void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4) +{ + f1.wait(); + f2.wait(); + f3.wait(); + f4.wait(); +} + +template +void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5) +{ + f1.wait(); + f2.wait(); + f3.wait(); + f4.wait(); + f5.wait(); +} + +template +typename boost::disable_if,Iterator>::type wait_for_any(Iterator begin,Iterator end) +{ + detail::future_waiter waiter; + for(Iterator current=begin;current!=end;++current) + { + waiter.add(*current); + } + return boost::next(begin,waiter.wait()); +} + +template +typename boost::enable_if,unsigned>::type wait_for_any(F1& f1,F2& f2) +{ + detail::future_waiter waiter; + waiter.add(f1); + waiter.add(f2); + return waiter.wait(); +} + +template +unsigned wait_for_any(F1& f1,F2& f2,F3& f3) +{ + detail::future_waiter waiter; + waiter.add(f1); + waiter.add(f2); + waiter.add(f3); + return waiter.wait(); +} + +template +unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4) +{ + detail::future_waiter waiter; + waiter.add(f1); + waiter.add(f2); + waiter.add(f3); + waiter.add(f4); + return waiter.wait(); +} + +template +unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5) +{ + detail::future_waiter waiter; + waiter.add(f1); + waiter.add(f2); + waiter.add(f3); + waiter.add(f4); + waiter.add(f5); + return waiter.wait(); +} + +template +class promise; + +template +class packaged_task; + +template +class unique_future +{ + typedef boost::shared_ptr > future_ptr; + + BOOST_MOVABLE_BUT_NOT_COPYABLE( unique_future); + + future_ptr future; + + friend class shared_future; + friend class promise; + friend class packaged_task; + friend class detail::future_waiter; + + typedef typename tasklets::detail::future_traits::move_dest_type move_dest_type; + + unique_future(future_ptr future_): + future(future_) + {} + +public: + typedef future_state::state state; + + unique_future() + {} + + ~unique_future() + {} + + unique_future( BOOST_RV_REF( unique_future) other): + future(other.future) + { other.future.reset(); } + + unique_future& operator=( BOOST_RV_REF( unique_future) other) + { + future=other.future; + other.future.reset(); + return *this; + } + + void swap(unique_future& other) + { future.swap(other.future); } + + // retrieving the value + move_dest_type get() + { + if(!future) + { + throw future_uninitialized(); + } + + return future->get(); + } + + // functions to check state, and wait for ready + state get_state() const + { + if(!future) + { + return future_state::uninitialized; + } + return future->get_state(); + } + + + bool is_ready() const + { + return get_state()==future_state::ready; + } + + bool has_exception() const + { + return future && future->has_exception(); + } + + bool has_value() const + { + return future && future->has_value(); + } + + void wait() const + { + if(!future) + { + throw future_uninitialized(); + } + future->wait(false); + } +}; + +template +class shared_future +{ + typedef boost::shared_ptr > future_ptr; + + BOOST_COPYABLE_AND_MOVABLE( shared_future); + + future_ptr future; + + friend class detail::future_waiter; + friend class promise; + friend class packaged_task; + + shared_future(future_ptr future_): + future(future_) + {} + +public: + shared_future(shared_future const& other): + future(other.future) + {} + + typedef future_state::state state; + + shared_future() + {} + + ~shared_future() + {} + + shared_future& operator=( BOOST_COPY_ASSIGN_REF( shared_future) other) + { + future=other.future; + return *this; + } + + shared_future( BOOST_RV_REF( shared_future) other): + future(other.future) + { + other.future.reset(); + } + + shared_future( BOOST_RV_REF( unique_future) other): + future(other.future) + { + other.future.reset(); + } + + shared_future& operator=(BOOST_RV_REF( shared_future) other) + { + future.swap(other.future); + other.future.reset(); + return *this; + } + shared_future& operator=( BOOST_RV_REF( unique_future) other) + { + future.swap(other.future); + other.future.reset(); + return *this; + } + + void swap(shared_future& other) + { + future.swap(other.future); + } + + // retrieving the value + R get() + { + if(!future) + { + throw future_uninitialized(); + } + + return future->get(); + } + + // functions to check state, and wait for ready + state get_state() const + { + if(!future) + { + return future_state::uninitialized; + } + return future->get_state(); + } + + bool is_ready() const + { + return get_state()==future_state::ready; + } + + bool has_exception() const + { + return future && future->has_exception(); + } + + bool has_value() const + { + return future && future->has_value(); + } + + void wait() const + { + if(!future) + { + throw future_uninitialized(); + } + future->wait(false); + } +}; + +template +class promise +{ + typedef boost::shared_ptr > future_ptr; + + BOOST_MOVABLE_BUT_NOT_COPYABLE( promise); + + future_ptr future; + bool future_obtained; + + void lazy_init() + { + if(!future) + { + future_obtained=false; + future.reset(new detail::future_object); + } + } + +public: +// template explicit promise(Allocator a); + + promise(): + future(),future_obtained(false) + {} + + ~promise() + { + if(future) + { + boost::lock_guard lock(future->mtx); + + if(!future->done) + { + future->mark_exceptional_finish_internal(boost::copy_exception(broken_promise())); + } + } + } + + promise( BOOST_RV_REF( promise) rhs): + future(rhs.future),future_obtained(rhs.future_obtained) + { + rhs.future.reset(); + } + promise & operator=( BOOST_RV_REF( promise) rhs) + { + future=rhs.future; + future_obtained=rhs.future_obtained; + rhs.future.reset(); + return *this; + } + + void swap(promise& other) + { + future.swap(other.future); + std::swap(future_obtained,other.future_obtained); + } + + // Result retrieval + unique_future get_future() + { + lazy_init(); + if(future_obtained) + { + throw future_already_retrieved(); + } + future_obtained=true; + return unique_future(future); + } + + void set_value(typename tasklets::detail::future_traits::source_reference_type r) + { + lazy_init(); + boost::lock_guard lock(future->mtx); + if(future->done) + { + throw promise_already_satisfied(); + } + future->mark_finished_with_result_internal(r); + } + +// void set_value(R && r); + void set_value(typename tasklets::detail::future_traits::rvalue_source_type r) + { + lazy_init(); + boost::lock_guard lock(future->mtx); + if(future->done) + { + throw promise_already_satisfied(); + } + future->mark_finished_with_result_internal(static_cast::rvalue_source_type>(r)); + } + + void set_exception(boost::exception_ptr p) + { + lazy_init(); + boost::lock_guard lock(future->mtx); + if(future->done) + { + throw promise_already_satisfied(); + } + future->mark_exceptional_finish_internal(p); + } + + template + void set_wait_callback(F f) + { + lazy_init(); + future->set_wait_callback(f,this); + } + +}; + +template <> +class promise +{ + typedef boost::shared_ptr > future_ptr; + + BOOST_MOVABLE_BUT_NOT_COPYABLE( promise); + + future_ptr future; + bool future_obtained; + + void lazy_init() + { + if(!future) + { + future_obtained=false; + future.reset(new detail::future_object); + } + } +public: + promise(): + future(),future_obtained(false) + {} + + ~promise() + { + if(future) + { + boost::lock_guard lock(future->mtx); + + if(!future->done) + { + future->mark_exceptional_finish_internal(boost::copy_exception(broken_promise())); + } + } + } + + promise( BOOST_RV_REF( promise) rhs): + future(rhs.future),future_obtained(rhs.future_obtained) + { + rhs.future.reset(); + } + promise & operator=( BOOST_RV_REF( promise) rhs) + { + future=rhs.future; + future_obtained=rhs.future_obtained; + rhs.future.reset(); + return *this; + } + + void swap(promise& other) + { + future.swap(other.future); + std::swap(future_obtained,other.future_obtained); + } + + // Result retrieval + unique_future get_future() + { + lazy_init(); + + if(future_obtained) + { + throw future_already_retrieved(); + } + future_obtained=true; + return unique_future(future); + } + + void set_value() + { + lazy_init(); + boost::lock_guard lock(future->mtx); + if(future->done) + { + throw promise_already_satisfied(); + } + future->mark_finished_with_result_internal(); + } + + void set_exception(boost::exception_ptr p) + { + lazy_init(); + boost::lock_guard lock(future->mtx); + if(future->done) + { + throw promise_already_satisfied(); + } + future->mark_exceptional_finish_internal(p); + } + + template + void set_wait_callback(F f) + { + lazy_init(); + future->set_wait_callback(f,this); + } +}; + +namespace detail { + +template +struct task_base: + detail::future_object +{ + bool started; + + task_base(): + started(false) + {} + + void run() + { + { + boost::lock_guard lk(this->mtx); + if(started) + { + throw task_already_started(); + } + started=true; + } + do_run(); + } + + void owner_destroyed() + { + boost::lock_guard lk(this->mtx); + if(!started) + { + started=true; + this->mark_exceptional_finish_internal(boost::copy_exception(broken_promise())); + } + } + + virtual void do_run()=0; +}; + +template +struct task_object: + task_base +{ + F f; + task_object(F const& f_): + f(f_) + {} + task_object( BOOST_RV_REF( F) f_): + f(f_) + {} + + void do_run() + { + try + { + this->mark_finished_with_result(f()); + } + catch(...) + { + this->mark_exceptional_finish(); + } + } +}; + +template +struct task_object: + task_base +{ + F f; + task_object(F const& f_): + f(f_) + {} + task_object( BOOST_RV_REF( F) f_): + f(f_) + {} + + void do_run() + { + try + { + f(); + this->mark_finished_with_result(); + } + catch(...) + { + this->mark_exceptional_finish(); + } + } +}; + +} + + +template +class packaged_task +{ + BOOST_MOVABLE_BUT_NOT_COPYABLE( packaged_task); + + boost::shared_ptr > task; + bool future_obtained; + +public: + packaged_task(): + future_obtained(false) + {} + + // construction and destruction + template + explicit packaged_task(F const& f): + task(new detail::task_object(f)),future_obtained(false) + {} + explicit packaged_task(R(*f)()): + task(new detail::task_object(f)),future_obtained(false) + {} + + template + explicit packaged_task( BOOST_RV_REF( F) f): + task(new detail::task_object(f)),future_obtained(false) + {} + +// template +// explicit packaged_task(F const& f, Allocator a); +// template +// explicit packaged_task(F&& f, Allocator a); + + ~packaged_task() + { + if(task) + { + task->owner_destroyed(); + } + } + + packaged_task( BOOST_RV_REF( packaged_task) other): + future_obtained(other.future_obtained) + { + task.swap(other.task); + other.future_obtained=false; + } + packaged_task& operator=( BOOST_RV_REF( packaged_task) other) + { + packaged_task temp(other); + swap(temp); + return *this; + } + + void swap(packaged_task& other) + { + task.swap(other.task); + std::swap(future_obtained,other.future_obtained); + } + + // result retrieval + unique_future get_future() + { + if(!task) + { + throw task_moved(); + } + else if(!future_obtained) + { + future_obtained=true; + return unique_future(task); + } + else + { + throw future_already_retrieved(); + } + } + + // execution + void operator()() + { + if(!task) + { + throw task_moved(); + } + task->run(); + } + + template + void set_wait_callback(F f) + { + task->set_wait_callback(f,this); + } +}; + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_FUTURE_H diff --git a/boost/tasklet/interruption.hpp b/boost/tasklet/interruption.hpp new file mode 100644 index 00000000..12c1d290 --- /dev/null +++ b/boost/tasklet/interruption.hpp @@ -0,0 +1,86 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_THIS_TASKLET_INTERRUPTION_H +#define BOOST_THIS_TASKLET_INTERRUPTION_H + +#include + +#include +#include + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace this_tasklet { + +class restore_interruption; + +class disable_interruption : private noncopyable +{ +private: + friend class restore_interruption; + + bool set_; + +public: + disable_interruption() : + set_( ( tasklets::strategy::interrupt_flags_() & tasklets::detail::INTERRUPTION_BLOCKED) != 0) + { + if ( ! set_) + tasklets::strategy::interrupt_flags_() |= tasklets::detail::INTERRUPTION_BLOCKED; + } + + ~disable_interruption() + { + try + { + if ( ! set_) + tasklets::strategy::interrupt_flags_() &= ~tasklets::detail::INTERRUPTION_BLOCKED; + } + catch (...) + {} + } +}; + +class restore_interruption : private noncopyable +{ +private: + disable_interruption & disabler_; + +public: + explicit restore_interruption( disable_interruption & disabler) : + disabler_( disabler) + { + if ( ! disabler_.set_) + tasklets::strategy::interrupt_flags_() &= ~tasklets::detail::INTERRUPTION_BLOCKED; + } + + ~restore_interruption() + { + try + { + if ( ! disabler_.set_) + tasklets::strategy::interrupt_flags_() |= tasklets::detail::INTERRUPTION_BLOCKED; + } + catch (...) + {} + } +}; + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_THIS_TASKLET_INTERRUPTION_H diff --git a/boost/tasklet/manual_reset_event.hpp b/boost/tasklet/manual_reset_event.hpp new file mode 100644 index 00000000..4ce4c761 --- /dev/null +++ b/boost/tasklet/manual_reset_event.hpp @@ -0,0 +1,66 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_MANUAL_RESET_EVENT_H +#define BOOST_TASKLETS_MANUAL_RESET_EVENT_H + +#include + +#include +#include +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4355 4251 4275) +# endif + +namespace boost { +namespace tasklets { + +class BOOST_TASKLET_DECL manual_reset_event : private noncopyable +{ +private: + enum state + { + SET = 0, + RESET + }; + + atomic< state > state_; + atomic< std::size_t > waiters_; + mutex enter_mtx_; + +public: + explicit manual_reset_event( bool = false); + + void set(); + + void reset(); + + void wait(); + + bool try_wait(); +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_MANUAL_RESET_EVENT_H diff --git a/boost/tasklet/mutex.hpp b/boost/tasklet/mutex.hpp new file mode 100644 index 00000000..75a589f3 --- /dev/null +++ b/boost/tasklet/mutex.hpp @@ -0,0 +1,101 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// based on boost::interprocess::sync::interprocess_spin_mutex + +#ifndef BOOST_TASKLETS_MUTEX_H +#define BOOST_TASKLETS_MUTEX_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4355 4251 4275) +# endif + +namespace boost { +namespace tasklets { + +class BOOST_TASKLET_DECL mutex : private noncopyable +{ +private: + enum state + { + LOCKED = 0, + UNLOCKED + }; + + struct ordered_idx_tag {}; + struct sequenced_idx_tag {}; + + typedef boost::multi_index::multi_index_container< + tasklet, + boost::multi_index::indexed_by< + boost::multi_index::ordered_unique< + boost::multi_index::tag< ordered_idx_tag >, + boost::multi_index::const_mem_fun< + tasklet, tasklet::id, & tasklet::get_id + > + >, + boost::multi_index::sequenced< + boost::multi_index::tag< sequenced_idx_tag > + > + > + > container; + + typedef container::index< ordered_idx_tag >::type ordered_idx; + typedef container::index< sequenced_idx_tag >::type sequenced_idx; + + object::id oid_; + state state_; + spin_mutex mtx_; + container waiting_tasklets_; + ordered_idx & oidx_; + sequenced_idx & sidx_; + +public: + typedef unique_lock< mutex > scoped_lock; + + mutex(); + + ~mutex(); + + void lock(); + + bool try_lock(); + + void unlock(); +}; + +typedef mutex try_mutex; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_MUTEX_H diff --git a/boost/tasklet/object/id.hpp b/boost/tasklet/object/id.hpp new file mode 100644 index 00000000..68679581 --- /dev/null +++ b/boost/tasklet/object/id.hpp @@ -0,0 +1,68 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_DETAIL_ID_H +#define BOOST_TASKLETS_DETAIL_ID_H + +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace tasklets { +namespace object { + +class id +{ +private: + void const* vp_; + +public: + template< typename T > + id( T const& t) : + vp_( static_cast< void const* >( & t) ) + {} + + bool operator==( id const& other) const + { return vp_ == other.vp_; } + + bool operator!=( id const& other) const + { return vp_ != other.vp_; } + + bool operator<( id const& other) const + { return vp_ < other.vp_; } + + bool operator>( id const& other) const + { return other.vp_ < vp_; } + + bool operator<=( id const& other) const + { return !( other.vp_ < vp_); } + + bool operator>=( id const& other) const + { return ! ( vp_ < other.vp_); } + + template< typename charT, class traitsT > + friend std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream< charT, traitsT > & os, id const& other) + { + if ( other.vp_) + return os << other.vp_; + else + return os << "{not-a-object}"; + } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_DETAIL_ID_H diff --git a/boost/tasklet/round_robin.hpp b/boost/tasklet/round_robin.hpp new file mode 100644 index 00000000..a0a8ff3d --- /dev/null +++ b/boost/tasklet/round_robin.hpp @@ -0,0 +1,138 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_ROUND_ROBIN_H +#define BOOST_TASKLETS_ROUND_ROBIN_H + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251 4275) +# endif + +namespace boost { +namespace tasklets { + +class BOOST_TASKLET_DECL round_robin : private noncopyable, + public strategy +{ +private: + struct schedulable + { + tasklet f; + std::list< tasklet::id > joining_tasklets; + optional< tasklet::id > waiting_on_tasklet; + optional< object::id > waiting_on_object; + + schedulable() : + f(), joining_tasklets(), + waiting_on_tasklet(), waiting_on_object() + {} + + schedulable( tasklet f_) : + f( f_), joining_tasklets(), + waiting_on_tasklet(), waiting_on_object() + {} + + schedulable( schedulable const& other) : + f( other.f), + joining_tasklets( other.joining_tasklets), + waiting_on_tasklet( other.waiting_on_tasklet), + waiting_on_object( other.waiting_on_object) + {} + + schedulable & + operator=( schedulable const& other) + { + if ( this == & other) return * this; + f = other.f; + joining_tasklets = other.joining_tasklets; + waiting_on_tasklet = other.waiting_on_tasklet; + waiting_on_object = other.waiting_on_object; + return * this; + } + }; + + typedef std::list< tasklet::id > tasklet_id_list; + typedef std::map< object::id, tasklet_id_list > object_map; + typedef std::map< tasklet::id, schedulable > tasklet_map; + typedef std::list< tasklet::id > runnable_queue; + typedef std::queue< tasklet::id > terminated_queue; + + mutable spin_mutex mtx_; + tasklet_map tasklets_; + object_map objects_; + runnable_queue runnable_tasklets_; + terminated_queue terminated_tasklets_; + +public: + round_robin(); + + void add( tasklet &); + + void join( tasklet &); + + void interrupt( tasklet &); + + void reschedule( tasklet &); + + void cancel( tasklet &); + + void yield(); + + void wait_for_object( object::id const&, spin_mutex::scoped_lock & lk); + + void object_notify_one( object::id const&); + + void object_notify_all( object::id const&); + + void release( tasklet &); + + void migrate( tasklet &); + + void detach_all(); + + bool run(); + + bool empty() const; + + std::size_t size() const; + + std::size_t ready() const; + + bool has_ready() const; +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_ROUND_ROBIN_H diff --git a/boost/tasklet/scheduler.hpp b/boost/tasklet/scheduler.hpp new file mode 100644 index 00000000..bd2f82c6 --- /dev/null +++ b/boost/tasklet/scheduler.hpp @@ -0,0 +1,96 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_SCHEDULER_H +#define BOOST_TASKLETS_SCHEDULER_H + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace tasklets { + +class auto_reset_event; +class condition; +class count_down_event; +class manual_reset_event; +class mutex; + +template< typename Strategy = round_robin > +class scheduler : private noncopyable +{ +private: + friend class auto_reset_event; + friend class condition; + friend class count_down_event; + friend class manual_reset_event; + friend class mutex; + + strategy * strategy_; + +public: + scheduler() : + strategy_( new Strategy() ) + {} + + ~scheduler() + { + strategy_->detach_all(); + delete strategy_; + } + + bool run() + { return strategy_->run(); } + + bool empty() const + { return strategy_->empty(); } + + std::size_t size() const + { return strategy_->size(); } + + std::size_t ready() const + { return strategy_->ready(); } + + bool has_ready() const + { return strategy_->has_ready(); } + + void submit_tasklet( tasklet t) + { strategy_->add( t); } + + void migrate_tasklet( tasklet & t) + { + if ( ! t) throw tasklet_moved(); + t.impl_->attached_strategy()->release( t); + strategy_->migrate( t); + } + + void swap( scheduler & other) + { std::swap( strategy_, other.strategy_); } +}; + +template< typename Strategy > +void swap( scheduler< Strategy > & l, scheduler< Strategy > & r) +{ l.swap( r); } + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_SCHEDULER_H diff --git a/boost/tasklet/spin_condition.hpp b/boost/tasklet/spin_condition.hpp new file mode 100644 index 00000000..98dd698c --- /dev/null +++ b/boost/tasklet/spin_condition.hpp @@ -0,0 +1,152 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// based on boost::interprocess::sync::interprocess_condition + +#ifndef BOOST_TASKLETS_SPIN_CONDITION_H +#define BOOST_TASKLETS_SPIN_CONDITION_H + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4355 4251 4275) +# endif + +namespace boost { +namespace tasklets { + +class BOOST_TASKLET_DECL spin_condition : private noncopyable +{ +private: + enum command + { + SLEEPING = 0, + NOTIFY_ONE, + NOTIFY_ALL + }; + + atomic< command > cmd_; + atomic< std::size_t > waiters_; + spin_mutex enter_mtx_; + spin_mutex check_mtx_; + + void notify_( command); + +public: + spin_condition(); + + void notify_one(); + + void notify_all(); + + void wait( unique_lock< spin_mutex > & lk) + { + if ( ! lk) + throw lock_error(); + wait( * lk.mutex() ); + } + + template< typename Pred > + void wait( unique_lock< spin_mutex > & lk, Pred pred) + { + if ( ! lk) + throw lock_error(); + + while ( ! pred() ) + wait( * lk.mutex() ); + } + + template< typename LockType > + void wait( LockType & lt) + { + { + spin_mutex::scoped_lock lk( enter_mtx_); + BOOST_ASSERT( lk); + waiters_.fetch_add( 1); + lt.unlock(); + } + + bool unlock_enter_mtx = false; + for (;;) + { + while ( SLEEPING == cmd_.load() ) + { + if ( this_tasklet::runs_as_tasklet() ) + this_tasklet::yield(); + else + this_thread::yield(); + } + + spin_mutex::scoped_lock lk( check_mtx_); + BOOST_ASSERT( lk); + + command expected = NOTIFY_ONE; + cmd_.compare_exchange_strong( expected, SLEEPING); + if ( SLEEPING == expected) + continue; + else if ( NOTIFY_ONE == expected) + { + unlock_enter_mtx = true; + waiters_.fetch_sub( 1); + break; + } + else + { + unlock_enter_mtx = 1 == waiters_.fetch_sub( 1); + if ( unlock_enter_mtx) + { + expected = NOTIFY_ALL; + cmd_.compare_exchange_strong( expected, SLEEPING); + } + break; + } + } + + if ( unlock_enter_mtx) + enter_mtx_.unlock(); + + lt.lock(); + } + + template< + typename LockType, + typename Pred + > + void wait( LockType & lt, Pred pred) + { + while ( ! pred() ) + wait( lt); + } +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_SPIN_CONDITION_H diff --git a/boost/tasklet/spin_mutex.hpp b/boost/tasklet/spin_mutex.hpp new file mode 100644 index 00000000..1f2166cc --- /dev/null +++ b/boost/tasklet/spin_mutex.hpp @@ -0,0 +1,66 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// based on boost::interprocess::sync::interprocess_spin_mutex + +#ifndef BOOST_TASKLETS_SPIN_MUTEX_H +#define BOOST_TASKLETS_SPIN_MUTEX_H + +#include +#include +#include +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4355 4251 4275) +# endif + +namespace boost { +namespace tasklets { + +class BOOST_TASKLET_DECL spin_mutex : private noncopyable +{ +private: + enum state + { + LOCKED = 0, + UNLOCKED + }; + + atomic< state > state_; + +public: + typedef unique_lock< spin_mutex > scoped_lock; + + spin_mutex(); + + void lock(); + + bool try_lock(); + + void unlock(); +}; + +typedef spin_mutex try_spin_mutex; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_SPIN_MUTEX_H diff --git a/boost/tasklet/strategy.hpp b/boost/tasklet/strategy.hpp new file mode 100644 index 00000000..8166d298 --- /dev/null +++ b/boost/tasklet/strategy.hpp @@ -0,0 +1,187 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_STRATEGY_H +#define BOOST_TASKLETS_STRATEGY_H + +#include + +#include +#include + +#include +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251) +# endif + +namespace boost { + +namespace this_tasklet { + +bool runs_as_tasklet(); +tasklet::id get_id(); +void yield(); +void cancel(); +int priority(); +void priority( int); +void interruption_point(); +bool interruption_requested(); +bool interruption_enabled(); +void submit_tasklet( tasklet); + +class disable_interruption; +class restore_interruption; + +} + +namespace tasklets { + +class auto_reset_event; +class condition; +class count_down_event; +class manual_reset_event; +class mutex; + +class BOOST_TASKLET_DECL strategy +{ +private: + friend bool this_tasklet::runs_as_tasklet(); + friend tasklet::id this_tasklet::get_id(); + friend void this_tasklet::yield(); + friend void this_tasklet::cancel(); + friend int this_tasklet::priority(); + friend void this_tasklet::priority( int); + friend void this_tasklet::interruption_point(); + friend bool this_tasklet::interruption_requested(); + friend bool this_tasklet::interruption_enabled(); + friend void this_tasklet::submit_tasklet( tasklet); + friend class this_tasklet::disable_interruption; + friend class this_tasklet::restore_interruption; + friend class auto_reset_event; + friend class condition; + friend class count_down_event; + friend class manual_reset_event; + friend class mutex; + + static bool runs_as_tasklet_(); + + static tasklet::id get_id_(); + + static void interruption_point_(); + + static bool interruption_requested_(); + + static detail::interrupt_type & interrupt_flags_(); + + static bool interruption_enabled_(); + + static int priority_(); + + static void priority_( int); + + static void yield_(); + + static void cancel_(); + + static void submit_tasklet_( tasklet); + +protected: + static BOOST_TASKLET_TSSDECL tasklet * active_tasklet; + + static void call( tasklet &); + + static void yield( tasklet &); + + static void detach( tasklet &); + + static void enable_interruption( tasklet &); + + static bool interruption_enabled( tasklet const&); + + static bool in_state_not_started( tasklet const&); + + static bool in_state_ready( tasklet const&); + + static bool in_state_running( tasklet const&); + + static bool in_state_wait_for_tasklet( tasklet const&); + + static bool in_state_wait_for_object( tasklet const&); + + static bool in_state_terminated( tasklet const&); + + static void set_state_ready( tasklet &); + + static void set_state_running( tasklet &); + + static void set_state_wait_for_tasklet( tasklet &); + + static void set_state_wait_for_object( tasklet &); + + static void set_state_terminated( tasklet &); + + void attach( tasklet &); + +public: + strategy(); + + virtual ~strategy(); + + virtual void add( tasklet &) = 0; + + virtual void join( tasklet &) = 0; + + virtual void interrupt( tasklet &) = 0; + + virtual void reschedule( tasklet &) = 0; + + virtual void cancel( tasklet &) = 0; + + virtual void yield() = 0; + + virtual void wait_for_object( object::id const&, spin_mutex::scoped_lock & lk) = 0; + + virtual void object_notify_one( object::id const&) = 0; + + virtual void object_notify_all( object::id const&) = 0; + + virtual void release( tasklet &) = 0; + + virtual void migrate( tasklet &) = 0; + + virtual void detach_all() = 0; + + virtual bool run() = 0; + + virtual bool empty() const = 0; + + virtual std::size_t size() const = 0; + + virtual std::size_t ready() const = 0; + + virtual bool has_ready() const = 0; +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_STRATEGY_H diff --git a/boost/tasklet/tasklet.hpp b/boost/tasklet/tasklet.hpp new file mode 100644 index 00000000..0976f7a5 --- /dev/null +++ b/boost/tasklet/tasklet.hpp @@ -0,0 +1,226 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_TASKLET_H +#define BOOST_TASKLETS_TASKLET_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4251) +# endif + +namespace boost { +namespace tasklets { + +class condition; +class mutex; +class strategy; +template< typename Strategy > +class scheduler; + +class BOOST_TASKLET_DECL tasklet +{ +private: + friend class condition; + friend class mutex; + friend class strategy; + template< typename Strategy > + friend class scheduler; + + BOOST_COPYABLE_AND_MOVABLE( tasklet); + + detail::tasklet_base::ptr impl_; + + template< typename Fn, typename AllocatorT > + static detail::tasklet_base::ptr make_impl_( + Fn fn, std::size_t stacksize, AllocatorT const& alloc) + { + return detail::tasklet_base::ptr( + new detail::tasklet_object< Fn >( fn, stacksize, alloc) ); + } + + template< typename Fn, typename AllocatorT > + static detail::tasklet_base::ptr make_impl_( + BOOST_RV_REF( Fn) fn, std::size_t stacksize, AllocatorT const& alloc) + { + return detail::tasklet_base::ptr( + new detail::tasklet_object< Fn >( fn, stacksize, alloc) ); + } + +public: + static std::size_t default_stacksize; + + class id; + + tasklet(); + + template< typename Fn, typename AllocatorT > + explicit tasklet( Fn fn, std::size_t stacksize, AllocatorT const& alloc) : + impl_( make_impl_( fn, stacksize, alloc) ) + {} + +#define BOOST_TASKLET_ARG(z, n, unused) \ + BOOST_PP_CAT(A, n) BOOST_PP_CAT(a, n) +#define BOOST_ENUM_TASKLET_ARGS(n) BOOST_PP_ENUM(n, BOOST_TASKLET_ARG, ~) + +#define BOOST_TASKLET_TASKLET_CTOR(z, n, unused) \ + template< typename Fn, BOOST_PP_ENUM_PARAMS(n, typename A), typename AllocatorT > \ + tasklet( Fn fn, BOOST_ENUM_TASKLET_ARGS(n), std::size_t stacksize, AllocatorT const& alloc) : \ + impl_( \ + make_impl_( \ + boost::bind( boost::type< void >(), fn, BOOST_PP_ENUM_PARAMS(n, a) ), \ + stacksize, alloc) ) \ + {} \ + +#ifndef BOOST_TASKLET_MAX_ARITY +#define BOOST_TASKLET_MAX_ARITY 10 +#endif + +BOOST_PP_REPEAT_FROM_TO( 1, BOOST_TASKLET_MAX_ARITY, BOOST_TASKLET_TASKLET_CTOR, ~) + +#undef BOOST_TASKLET_TASKLET_CTOR + + template< typename Fn, typename AllocatorT > + explicit tasklet( BOOST_RV_REF( Fn) fn, std::size_t stacksize, AllocatorT const& alloc) : + impl_( make_impl_( fn, stacksize, alloc) ) + {} + + tasklet( tasklet const& other); + + tasklet & operator=( BOOST_COPY_ASSIGN_REF( tasklet) other); + + tasklet( BOOST_RV_REF( tasklet) other); + + tasklet & operator=( BOOST_RV_REF( tasklet) other); + + typedef detail::tasklet_base::ptr::unspecified_bool_type unspecified_bool_type; + + operator unspecified_bool_type() const; + + bool operator!() const; + + void swap( tasklet & other); + + id get_id() const; + + bool operator==( tasklet const& other) const; + bool operator!=( tasklet const& other) const; + + bool is_alive() const; + + int priority() const; + + void priority( int); + + void interrupt(); + + bool interruption_requested() const; + + void cancel(); + + void join(); +}; + +class BOOST_TASKLET_DECL tasklet::id +{ +private: + friend class tasklet; + + uint64_t id_; + + explicit id( detail::tasklet_base::ptr info) : + id_( reinterpret_cast< uint64_t >( info.get() ) ) + {} + +public: + id() : + id_( 0) + {} + + bool operator==( id const& other) const + { return id_ == other.id_; } + + bool operator!=( id const& other) const + { return id_ != other.id_; } + + bool operator<( id const& other) const + { return id_ < other.id_; } + + bool operator>( id const& other) const + { return other.id_ < id_; } + + bool operator<=( id const& other) const + { return !( other.id_ < id_); } + + bool operator>=( id const& other) const + { return ! ( id_ < other.id_); } + + template< typename charT, class traitsT > + friend std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream< charT, traitsT > & os, id const& other) + { + if ( 0 != other.id_) + return os << other.id_; + else + return os << "{not-a-tasklet}"; + } +}; + +template< typename Fn > +tasklet make_tasklet( Fn fn, std::size_t stacksize) +{ return tasklet( fn, stacksize); } + +#define BOOST_TASKLET_MAKE_TASKLET_FUNCTION(z, n, unused) \ +template< typename Fn, BOOST_PP_ENUM_PARAMS(n, typename A) > \ +tasklet make_tasklet( Fn fn, BOOST_ENUM_TASKLET_ARGS(n), std::size_t stacksize) \ +{ return tasklet( fn, BOOST_PP_ENUM_PARAMS(n, a), stacksize); } + +BOOST_PP_REPEAT_FROM_TO( 1, BOOST_TASKLET_MAX_ARITY, BOOST_TASKLET_MAKE_TASKLET_FUNCTION, ~) + +#undef BOOST_TASKLET_MAKE_TASKLET_FUNCTION +#undef BOOST_ENUM_TASKLET_ARGS +#undef BOOST_TASKLET_ARG +#undef BOOST_TASKLET_MAX_ARITY + +} + +using tasklets::tasklet; + +inline +void swap( tasklet & l, tasklet & r) +{ return l.swap( r); } + +} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_TASKLET_H diff --git a/boost/tasklet/unbounded_channel.hpp b/boost/tasklet/unbounded_channel.hpp new file mode 100644 index 00000000..59789802 --- /dev/null +++ b/boost/tasklet/unbounded_channel.hpp @@ -0,0 +1,197 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TASKLETS_UNBOUNDED_CHANNEL_H +#define BOOST_TASKLETS_UNBOUNDED_CHANNEL_H + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace tasklets { +namespace detail { + +template< typename T > +struct unbounded_channel_node +{ + typedef intrusive_ptr< unbounded_channel_node > ptr; + + atomic< std::size_t > use_count; + T va; + ptr next; + + unbounded_channel_node() : + use_count( 0), + va(), + next() + {} +}; + +template< typename T > +void intrusive_ptr_add_ref( unbounded_channel_node< T > * p) +{ p->use_count.fetch_add( 1, memory_order_relaxed); } + +template< typename T > +void intrusive_ptr_release( unbounded_channel_node< T > * p) +{ + if ( p->use_count.fetch_sub( 1, memory_order_release) == 1) + { + atomic_thread_fence( memory_order_acquire); + delete p; + } +} + +} + +template< typename T > +class unbounded_channel : private noncopyable +{ +public: + typedef optional< T > value_type; + +private: + typedef detail::unbounded_channel_node< value_type > node_type; + + template< typename X > + friend void intrusive_ptr_add_ref( unbounded_channel< X > * p); + template< typename X > + friend void intrusive_ptr_release( unbounded_channel< X > * p); + + enum state + { + ACTIVE = 0, + DEACTIVE + }; + + atomic< state > state_; + typename node_type::ptr head_; + mutable mutex head_mtx_; + typename node_type::ptr tail_; + mutable mutex tail_mtx_; + condition not_empty_cond_; + atomic< std::size_t > use_count_; + + bool active_() const + { return ACTIVE == state_.load(); } + + void deactivate_() + { state_.store( DEACTIVE); } + + bool empty_() const + { return head_ == get_tail_(); } + + typename node_type::ptr get_tail_() const + { + mutex::scoped_lock lk( tail_mtx_); + typename node_type::ptr tmp = tail_; + return tmp; + } + + typename node_type::ptr pop_head_() + { + typename node_type::ptr old_head = head_; + head_ = old_head->next; + return old_head; + } + +public: + unbounded_channel() : + state_( ACTIVE), + head_( new node_type() ), + head_mtx_(), + tail_( head_), + tail_mtx_(), + not_empty_cond_(), + use_count_( 0) + {} + + bool active() const + { return active_(); } + + void deactivate() + { + mutex::scoped_lock lk( head_mtx_); + deactivate_(); + not_empty_cond_.notify_all(); + } + + bool empty() const + { + mutex::scoped_lock lk( head_mtx_); + return empty_(); + } + + void put( T const& t) + { + typename node_type::ptr new_node( new node_type() ); + { + mutex::scoped_lock lk( tail_mtx_); + + if ( ! active_() ) + throw std::runtime_error("queue is not active"); + + tail_->va = t; + tail_->next = new_node; + tail_ = new_node; + } + not_empty_cond_.notify_one(); + } + + bool take( value_type & va) + { + mutex::scoped_lock lk( head_mtx_); + bool empty = empty_(); + if ( ! active_() && empty) + return false; + if ( empty) + { + try + { + while ( active_() && empty_() ) + not_empty_cond_.wait( lk); + } + catch ( tasklet_interrupted const&) + { return false; } + } + if ( ! active_() && empty_() ) + return false; + swap( va, head_->va); + pop_head_(); + return va; + } + + bool try_take( value_type & va) + { + mutex::scoped_lock lk( head_mtx_); + if ( empty_() ) + return false; + swap( va, head_->va); + pop_head_(); + return va; + } +}; + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_TASKLETS_UNBOUNDED_CHANNEL_H diff --git a/boost/tasklet/utility.hpp b/boost/tasklet/utility.hpp new file mode 100644 index 00000000..1565c2cc --- /dev/null +++ b/boost/tasklet/utility.hpp @@ -0,0 +1,71 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_THIS_TASKLET_UTILITY_H +#define BOOST_THIS_TASKLET_UTILITY_H + +#include +#include +#include +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace this_tasklet { + +inline +bool runs_as_tasklet() +{ return tasklets::strategy::runs_as_tasklet_(); } + +inline +tasklet::id get_id() +{ return tasklets::strategy::get_id_(); } + +inline +int priority() +{ return tasklets::strategy::priority_(); } + +inline +void priority( int prio) +{ tasklets::strategy::priority_( prio); } + +inline +void interruption_point() +{ tasklets::strategy::interruption_point_(); } + +inline +bool interruption_requested() +{ return tasklets::strategy::interruption_requested_(); } + +inline +bool interruption_enabled() +{ return tasklets::strategy::interruption_enabled_(); } + +inline +void yield() +{ tasklets::strategy::yield_(); } + +inline +void cancel() +{ tasklets::strategy::cancel_(); } + +inline +void submit_tasklet( tasklet f) +{ tasklets::strategy::submit_tasklet_( f); } + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_THIS_TASKLET_UTILITY_H diff --git a/libs/atomic/doc/Jamfile.v2 b/libs/atomic/doc/Jamfile.v2 new file mode 100644 index 00000000..bae9033f --- /dev/null +++ b/libs/atomic/doc/Jamfile.v2 @@ -0,0 +1,33 @@ +# Boost.Atomiclibrary documentation Jamfile +# +# Copyright Helge Bahmann 2006. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# +# See http://www.boost.org/libs/atomic for documentation. + + +import quickbook ; +import doxygen ; + +doxygen autodoc + : + [ glob atomic.hpp ] + : + HIDE_UNDOC_MEMBERS=YES + ; + + +xml atomic : atomic.qbk ; + +boostbook standalone + : + atomic + : + boost.root=../../../.. + boost.libraries=../../../../libs/libraries.htm + generate.section.toc.level=3 + chunk.first.sections=1 + autodoc + ; diff --git a/libs/atomic/doc/atomic.hpp b/libs/atomic/doc/atomic.hpp new file mode 100644 index 00000000..329eeb2a --- /dev/null +++ b/libs/atomic/doc/atomic.hpp @@ -0,0 +1,526 @@ +/** \file boost/atomic.hpp */ + +// Copyright (c) 2009 Helge Bahmann +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +/* this is just a pseudo-header file fed to doxygen +to more easily generate the class documentation; will +be replaced by proper documentation down the road */ + +namespace boost { + +/** + \brief Memory ordering constraints + + This defines the relative order of one atomic operation + and other memory operations (loads, stores, other atomic operations) + executed by the same thread. + + The order of operations specified by the programmer in the + source code ("program order") does not necessarily match + the order in which they are actually executed on the target system: + Both compiler as well as processor may reorder operations + quite arbitrarily. Specifying the wrong ordering + constraint will therefore generally result in an incorrect program. +*/ +enum memory_order { + /** + \brief No constraint + Atomic operation and other memory operations may be reordered freely. + */ + memory_order_relaxed, + /** + \brief Data dependence constraint + Atomic operation must strictly precede any memory operation that + computationally depends on the outcome of the atomic operation. + */ + memory_order_consume, + /** + \brief Acquire memory + Atomic operation must strictly precede all memory operations that + follow in program order. + */ + memory_order_acquire, + /** + \brief Release memory + Atomic operation must strictly follow all memory operations that precede + in program order. + */ + memory_order_release, + /** + \brief Acquire and release memory + Combines the effects of \ref memory_order_acquire and \ref memory_order_release + */ + memory_order_acq_rel, + /** + \brief Sequentially consistent + Produces the same result \ref memory_order_acq_rel, but additionally + enforces globally sequential consistent execution + */ + memory_order_seq_cst +}; + +/** + \brief Atomic datatype + + An atomic variable. Provides methods to modify this variable atomically. + Valid template parameters are: + + - integral data types (char, short, int, ...) + - pointer data types + - any other data type that has a non-throwing default + constructor and that can be copied via memcpy + + Unless specified otherwise, any memory ordering constraint can be used + with any of the atomic operations. +*/ +template +class atomic { +public: + /** + \brief Create uninitialized atomic variable + Creates an atomic variable. Its initial value is undefined. + */ + atomic(); + /** + \brief Create an initialize atomic variable + \param value Initial value + Creates and initializes an atomic variable. + */ + atomic(Type value); + + /** + \brief Read the current value of the atomic variable + \param order Memory ordering constraint, see \ref memory_order + \return Current value of the variable + + Valid memory ordering constraints are: + - @c memory_order_relaxed + - @c memory_order_consume + - @c memory_order_acquire + - @c memory_order_seq_cst + */ + Type load(memory_order order=memory_order_seq_cst) const; + + /** + \brief Write new value to atomic variable + \param value New value + \param order Memory ordering constraint, see \ref memory_order + + Valid memory ordering constraints are: + - @c memory_order_relaxed + - @c memory_order_release + - @c memory_order_seq_cst + */ + void store(Type value, memory_order order=memory_order_seq_cst); + + /** + \brief Atomically compare and exchange variable + \param expected Expected old value + \param desired Desired new value + \param order Memory ordering constraint, see \ref memory_order + \return @c true if value was changed + + Atomically performs the following operation + + \code + if (variable==expected) { + variable=desired; + return true; + } else { + expected=variable; + return false; + } + \endcode + + This operation may fail "spuriously", i.e. the state of the variable + is unchanged even though the expected value was found (this is the + case on architectures using "load-linked"/"store conditional" to + implement the operation). + + The established memory order will be @c order if the operation + is successful. If the operation is unsuccesful, the + memory order will be + + - @c memory_order_relaxed if @c order is @c memory_order_acquire , + @c memory_order_relaxed or @c memory_order_consume + - @c memory_order_release if @c order is @c memory_order_acq_release + or @c memory_order_release + - @c memory_order_seq_cst if @c order is @c memory_order_seq_cst + */ + bool compare_exchange_weak( + Type &expected, + Type desired, + memory_order order=memory_order_seq_cst); + + /** + \brief Atomically compare and exchange variable + \param expected Expected old value + \param desired Desired new value + \param success_order Memory ordering constraint if operation + is successful + \param failure_order Memory ordering constraint if operation is unsuccesful + \return @c true if value was changed + + Atomically performs the following operation + + \code + if (variable==expected) { + variable=desired; + return true; + } else { + expected=variable; + return false; + } + \endcode + + This operation may fail "spuriously", i.e. the state of the variable + is unchanged even though the expected value was found (this is the + case on architectures using "load-linked"/"store conditional" to + implement the operation). + + The constraint imposed by @c success_order may not be + weaker than the constraint imposed by @c failure_order. + */ + bool compare_exchange_weak( + Type &expected, + Type desired, + memory_order success_order, + memory_order failure_order); + /** + \brief Atomically compare and exchange variable + \param expected Expected old value + \param desired Desired new value + \param order Memory ordering constraint, see \ref memory_order + \return @c true if value was changed + + Atomically performs the following operation + + \code + if (variable==expected) { + variable=desired; + return true; + } else { + expected=variable; + return false; + } + \endcode + + In contrast to \ref compare_exchange_weak, this operation will never + fail spuriously. Since compare-and-swap must generally be retried + in a loop, implementors are advised to prefer \ref compare_exchange_weak + where feasible. + + The established memory order will be @c order if the operation + is successful. If the operation is unsuccesful, the + memory order will be + + - @c memory_order_relaxed if @c order is @c memory_order_acquire , + @c memory_order_relaxed or @c memory_order_consume + - @c memory_order_release if @c order is @c memory_order_acq_release + or @c memory_order_release + - @c memory_order_seq_cst if @c order is @c memory_order_seq_cst + */ + bool compare_exchange_strong( + Type &expected, + Type desired, + memory_order order=memory_order_seq_cst); + + /** + \brief Atomically compare and exchange variable + \param expected Expected old value + \param desired Desired new value + \param success_order Memory ordering constraint if operation + is successful + \param failure_order Memory ordering constraint if operation is unsuccesful + \return @c true if value was changed + + Atomically performs the following operation + + \code + if (variable==expected) { + variable=desired; + return true; + } else { + expected=variable; + return false; + } + \endcode + + In contrast to \ref compare_exchange_weak, this operation will never + fail spuriously. Since compare-and-swap must generally be retried + in a loop, implementors are advised to prefer \ref compare_exchange_weak + where feasible. + + The constraint imposed by @c success_order may not be + weaker than the constraint imposed by @c failure_order. + */ + bool compare_exchange_strong( + Type &expected, + Type desired, + memory_order success_order, + memory_order failure_order); + /** + \brief Atomically exchange variable + \param value New value + \param order Memory ordering constraint, see \ref memory_order + \return Old value of the variable + + Atomically exchanges the value of the variable with the new + value and returns its old value. + */ + Type exchange(Type value, memory_order order=memory_order_seq_cst); + + /** + \brief Atomically add and return old value + \param operand Operand + \param order Memory ordering constraint, see \ref memory_order + \return Old value of the variable + + Atomically adds operand to the variable and returns its + old value. + */ + Type fetch_add(Type operand, memory_order order=memory_order_seq_cst); + /** + \brief Atomically subtract and return old value + \param operand Operand + \param order Memory ordering constraint, see \ref memory_order + \return Old value of the variable + + Atomically subtracts operand from the variable and returns its + old value. + + This method is available only if \c Type is an integral type + or a non-void pointer type. If it is a pointer type, + @c operand is of type @c ptrdiff_t and the operation + is performed following the rules for pointer arithmetic + in C++. + */ + Type fetch_sub(Type operand, memory_order order=memory_order_seq_cst); + + /** + \brief Atomically perform bitwise "AND" and return old value + \param operand Operand + \param order Memory ordering constraint, see \ref memory_order + \return Old value of the variable + + Atomically performs bitwise "AND" with the variable and returns its + old value. + + This method is available only if \c Type is an integral type + or a non-void pointer type. If it is a pointer type, + @c operand is of type @c ptrdiff_t and the operation + is performed following the rules for pointer arithmetic + in C++. + */ + Type fetch_and(Type operand, memory_order order=memory_order_seq_cst); + + /** + \brief Atomically perform bitwise "OR" and return old value + \param operand Operand + \param order Memory ordering constraint, see \ref memory_order + \return Old value of the variable + + Atomically performs bitwise "OR" with the variable and returns its + old value. + + This method is available only if \c Type is an integral type. + */ + Type fetch_or(Type operand, memory_order order=memory_order_seq_cst); + + /** + \brief Atomically perform bitwise "XOR" and return old value + \param operand Operand + \param order Memory ordering constraint, see \ref memory_order + \return Old value of the variable + + Atomically performs bitwise "XOR" with the variable and returns its + old value. + + This method is available only if \c Type is an integral type. + */ + Type fetch_xor(Type operand, memory_order order=memory_order_seq_cst); + + /** + \brief Implicit load + \return Current value of the variable + + The same as load(memory_order_seq_cst). Avoid using + the implicit conversion operator, use \ref load with + an explicit memory ordering constraint. + */ + operator Type(void) const; + /** + \brief Implicit store + \param value New value + \return Copy of @c value + + The same as store(value, memory_order_seq_cst). Avoid using + the implicit conversion operator, use \ref store with + an explicit memory ordering constraint. + */ + Type operator=(Type v); + + /** + \brief Atomically perform bitwise "AND" and return new value + \param operand Operand + \return New value of the variable + + The same as fetch_and(operand, memory_order_seq_cst)&operand. + Avoid using the implicit bitwise "AND" operator, use \ref fetch_and + with an explicit memory ordering constraint. + */ + Type operator&=(Type operand); + + /** + \brief Atomically perform bitwise "OR" and return new value + \param operand Operand + \return New value of the variable + + The same as fetch_or(operand, memory_order_seq_cst)|operand. + Avoid using the implicit bitwise "OR" operator, use \ref fetch_or + with an explicit memory ordering constraint. + + This method is available only if \c Type is an integral type. + */ + Type operator|=(Type operand); + + /** + \brief Atomically perform bitwise "XOR" and return new value + \param operand Operand + \return New value of the variable + + The same as fetch_xor(operand, memory_order_seq_cst)^operand. + Avoid using the implicit bitwise "XOR" operator, use \ref fetch_xor + with an explicit memory ordering constraint. + + This method is available only if \c Type is an integral type. + */ + Type operator^=(Type operand); + + /** + \brief Atomically add and return new value + \param operand Operand + \return New value of the variable + + The same as fetch_add(operand, memory_order_seq_cst)+operand. + Avoid using the implicit add operator, use \ref fetch_add + with an explicit memory ordering constraint. + + This method is available only if \c Type is an integral type + or a non-void pointer type. If it is a pointer type, + @c operand is of type @c ptrdiff_t and the operation + is performed following the rules for pointer arithmetic + in C++. + */ + Type operator+=(Type operand); + + /** + \brief Atomically subtract and return new value + \param operand Operand + \return New value of the variable + + The same as fetch_sub(operand, memory_order_seq_cst)-operand. + Avoid using the implicit subtract operator, use \ref fetch_sub + with an explicit memory ordering constraint. + + This method is available only if \c Type is an integral type + or a non-void pointer type. If it is a pointer type, + @c operand is of type @c ptrdiff_t and the operation + is performed following the rules for pointer arithmetic + in C++. + */ + Type operator-=(Type operand); + + /** + \brief Atomically increment and return new value + \return New value of the variable + + The same as fetch_add(1, memory_order_seq_cst)+1. + Avoid using the implicit increment operator, use \ref fetch_add + with an explicit memory ordering constraint. + + This method is available only if \c Type is an integral type + or a non-void pointer type. If it is a pointer type, + the operation + is performed following the rules for pointer arithmetic + in C++. + */ + Type operator++(void); + /** + \brief Atomically increment and return old value + \return Old value of the variable + + The same as fetch_add(1, memory_order_seq_cst). + Avoid using the implicit increment operator, use \ref fetch_add + with an explicit memory ordering constraint. + + This method is available only if \c Type is an integral type + or a non-void pointer type. If it is a pointer type, + the operation + is performed following the rules for pointer arithmetic + in C++. + */ + Type operator++(int); + /** + \brief Atomically subtract and return new value + \return New value of the variable + + The same as fetch_sub(1, memory_order_seq_cst)-1. + Avoid using the implicit increment operator, use \ref fetch_sub + with an explicit memory ordering constraint. + + This method is available only if \c Type is an integral type + or a non-void pointer type. If it is a pointer type, + the operation + is performed following the rules for pointer arithmetic + in C++. + */ + Type operator--(void); + /** + \brief Atomically subtract and return old value + \return Old value of the variable + + The same as fetch_sub(1, memory_order_seq_cst). + Avoid using the implicit increment operator, use \ref fetch_sub + with an explicit memory ordering constraint. + + This method is available only if \c Type is an integral type + or a non-void pointer type. If it is a pointer type, + the operation + is performed following the rules for pointer arithmetic + in C++. + */ + Type operator--(int); + +private: + /** \brief Deleted copy constructor */ + atomic(const atomic &); + /** \brief Deleted copy assignment */ + void operator=(const atomic &); +}; + +/** + \brief Insert explicit fence + \param order Memory ordering constraint + + Inserts an explicit fence. The exact semantic depends on the + type of fence inserted: + + - \c memory_order_relaxed: No operation + - \c memory_order_release: Performs a "release" operation + - \c memory_order_acquire or \c memory_order_consume: Performs an + "acquire" operation + - \c memory_order_acq_rel: Performs both an "acquire" and a "release" + operation + - \c memory_order_seq_cst: Performs both an "acquire" and a "release" + operation and in addition there exists a global total order of + all \c memory_order_seq_cst operations + +*/ +void atomic_thread_fence(memory_order order); + +} diff --git a/libs/atomic/doc/atomic.qbk b/libs/atomic/doc/atomic.qbk new file mode 100644 index 00000000..d642c604 --- /dev/null +++ b/libs/atomic/doc/atomic.qbk @@ -0,0 +1,366 @@ +[/ + / Copyright (c) 2009 Helge Bahmann + / + / Distributed under the Boost Software License, Version 1.0. (See accompanying + / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + /] + +[library Boost.Atomic + [quickbook 1.4] + [authors [Bahmann, Helge]] + [copyright 2009 Helge Bahmann] + [id atomic] + [dirname atomic] + [purpose Atomic operations] + [license + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + [@http://www.boost.org/LICENSE_1_0.txt]) + ] +] + +[section:introduction Introduction] + +[section:introduction_presenting Presenting Boost.Atomic] + +[*Boost.Atomic] is a library that provides [classref boost::atomic atomic] +data types and operations on these data types. Additionally it +also supports the concept of "memory order" for coordinating +multiple threads through atomic variables. +It implements the interface defined for the proposed +C++0x standard, but makes this feature available for +platforms lacking system/compiler support for this particular C++0x +feature. + +The implementation makes use of processor-specific instructions where +possible (via inline assembler, platform libraries or compiler +intrinsics), and falls back to "emulating" atomic operations via locking. + +[endsect] + +[section:introduction_purpose Purpose] + +Operations on "ordinary" variables are not guaranteed to be atomic. +This means that with [^int n=0] initially, two threads concurrently +executing + +[c++] + + void function() + { + n++; + } + +might result in [^n==1] instead of 2: Each thread will read the +old value into a processor register, increment it and write the result +back. Both threads may therefore write [^1], unaware that the other thread +is doing likewise. + +Declaring [^atomic n=0] instead, the same operation on +this variable will always result in [^n==2] as each operation on this +variable is ['atomic]: This means that each operation is strictly +in order without interference and completes before a new operation +is begun. + +Such atomic variables are useful as a faster alternative to +using a "lock" for access to simple variables. They can also +be used to coordinate multiple threads that want to access a +larger shared data structure. This has some benefits over +"mutual exclusion" as true concurrent access becomes possible, +instead of being sequentialized by a lock. The downside of +such custom coordination protocols is complexity, so +you should take a look at the [link atomic.usage_examples examples] section +for common patterns. + +[endsect] + +[endsect] + +[section:interface Programming interface] + +Atomic variables are declared as instances of `boost::atomic` +where `T` is an integral or suitable user-defined type. Operations +to be performed on such a variable are implemented as member functions +of this type. + +In addition to atomic variables, the library also provides a mechanism +for ensuring ordered access to shared data structures, using either +the free-standing [funcref boost::atomic_thread_fence atomic_thread_fence] +function, or specified along with an operation on an atomic variable. + +[section:interface_atomic_object Atomic objects] + +[section:interface_atomic_generic [^boost::atomic<['T]>] template class] + +[classref boost::atomic boost::atomic<['T]>] provides methods for atomically accessing +variables of a suitable type [^['T]]. The type is suitable if +it satisfies one of the following constraints: + +* it is an integer, boolean, enum or pointer type +* it is any other data-type ([^class] or [^struct]) that has + a non-throwing default constructor, that is copyable via + [^memcpy] and comparable via [^memcmp]. + +Note that all classes having a trivial default constructor, +no destructor and no virtual methods satisfy the second condition +according to C++98. On a given platform, other data-types ['may] +also satisfy this constraint, however you should exercise +caution as the behaviour becomes implementation-defined. Also be warned +that structures with "padding" between data members may compare +non-equal via [^memcmp] even though all members are equal. + +Each atomic object supports the following operations: + +* [^atomic()] (default constructor): Initializes the atomic + variable to an unspecified value +* [^atomic(T initial_value)]: Initializes the atomic + variable to the given value +* [memberref boost::atomic::load load]: Reads the + current value of the atomic variable +* [memberref boost::atomic::store store]: + Stores a new value for the atomic variable +* [memberref boost::atomic::exchange exchange]: + Exchanges the value of the atomic variable with a new + value and returns the old value +* [memberref boost::atomic::compare_exchange_weak compare_exchange_weak]: + Tests if the value of the atomic variable with an + "expected" value, and if it matches, exchanges it + with a new value (allows for spurious failures). +* [memberref boost::atomic::compare_exchange_strong compare_exchange_strong]: + Tests if the value of the atomic variable with an + "expected" value, and if it matches, exchanges it + with a new value. + +In addition to these explicit operations, each +[classref boost::atomic atomic<['T]>] object also supports +implicit [^store] and [^load] through the use of "assignment" +and "conversion to [^T]" operators. Avoid using these operators, +as they do not allow explicit specification of a memory ordering +constraint. + +[endsect] + +[section:interface_atomic_integral [^boost::atomic<['integral]>] template class] + +In addition to the operations listed in the previous section, +[classref boost::atomic boost::atomic<['I]>] for integral +types [^['I]] supports the following operations: + +* [memberref boost::atomic::fetch_add fetch_add]: Adds value + to atomic variable and returns old value +* [memberref boost::atomic::fetch_sub fetch_sub]: Subtracts value + from atomic variable and returns old value +* [memberref boost::atomic::fetch_sub fetch_and]: Combines + atomic variable bitwise "and" with operand and returns old value +* [memberref boost::atomic::fetch_sub fetch_or]: Combines + atomic variable bitwise "or" with operand and returns old value +* [memberref boost::atomic::fetch_sub fetch_xor]: Combines + atomic variable bitwise "xor" with operand and returns old value + +In addition to these explicit operations, each +[classref boost::atomic boost::atomic<['I]>] object also +supports implicit pre-/post- increment/decrement, as well +as the operators [^+=], [^-=], [^&=], [^|=] and [^^=]. +Avoid using these operators, +as they do not allow explicit specification of a memory ordering +constraint. + +[endsect] + +[endsect] + +[section:interface_memory_order Memory order] + +When implementing custom thread coordination protocols, +atomic variables can be used to prevent two threads from +performing ['conflicting] access to shared data. At the +very least, any write to a non-atomic variable must be +considered to conflict with any other read or write to +the same variable, depending on the use case other types +of concurrent access may conflict as well. + +[section:interface_acq_rel Release/acquire consistency] + +[^Boost.Atomic] provides one basic mechanism to +guarantee that an operation A in thread #1 +['happens before] an operation B in thread #2 +(thus operations A and B are not executed +concurrently): + +* thread #1 performs operation A + +* thread #1 performs a ['release] operation + +* thread #1 writes an atomic variable + +* thread #2 reads the value written by thread #1 from + the atomic variable (or any "updated" value) + +* thread #2 performs an ['acquire] operation + +* thread #2 performs operation B + +(The ['happens before] relation is transitive). +['Release] and ['Acquire] operations may be performed +calling the free-standing function +[funcref boost::atomic_thread_fence atomic_thread_fence] +with [^memory_order_release] and [^memory_order_acquire] +parameter, respectively. The implementation also +provides a mechanism for specifying a memory ordering +constraints together with an atomic operation, so that +in most cases the above sequence will be written as: + +* thread #1 performs operation A + +* thread #1 writes an atomic variable with ['release] + memory ordering constraint + +* thread #2 reads the value written by thread #1 from + the atomic variable (or any "updated" value) with + ['acquire] memory ordering constraint + +* thread #2 performs operation B + +Note that in itself, both ['acquire] and ['release] +are meaningless, the guarantee only holds +if both threads access the same atomic variable, +and it does not hold for non-atomic variables. + +Refer to [link atomic.usage_examples this] section for examples +demonstrating correct usage of ['acquire] and ['release] +operations. + +[endsect] + +[section:interface_consume Release/consume consistency] + +It is sometimes permissible to use a ['consume] operation +instead of ['acquire] as in the previous section. This +is possible if (and only if) ['the operation B +computationally depends on the value read from the +atomic variable]. This is satisfied if: + +* the atomic variable is a pointer, and the operation B + is a dereference of this pointer + +* the atomic variable is an integral type that is used + to compute an address that is dereferenced by B (e.g. + index into an array) + +Use ['acquire] in all other cases[footnote +Nominally, ['consume] should also be applicable +if B is an arithmetic expression depending on the +atomic value. Due to optimizations that might +eliminate this dependence this can however not +be guaranteed without compiler support. Therefore, +avoid using this constraint for anything +else but pointer dereferences]. If the +above requirement is met, then the following +sequence + +* thread #1 performs operation A + +* thread #1 performs a ['release] operation + +* thread #1 writes an atomic variable + +* thread #2 reads the value written by thread #1 from + the atomic variable (or any "updated" value) + +* thread #2 performs an ['consume] operation + +* thread #2 performs operation B + +also guarantees that A ['happens before] B. (The +relationship is transitive and may also be written +in the same short-hand notation, as before). + +See the [link boost_atomic.usage_examples.mp_queue wait-free queue] and +[link boost_atomic.usage_examples.singleton singleton pattern] for valid +uses of ['consume]. + +[endsect] + +[section:interface_memory_order_constraints Permissible constraints] + +The following memory ordering constraints can be +specified with atomic operations or as parameter to +the free-standing [funcref boost::atomic_thread_fence atomic_thread_fence] +function: + +* [^boost::memory_order_relaxed]: No ordering is implied. + Informally speaking, following operations may be moved before, + preceding operations may be moved after the atomic + operation. This constraint is suitable only when + either a) further operations do not depend on the outcome + of the atomic operation or b) ordering is enforced through + other mechanisms (e.g. [funcref boost::atomic_thread_fence atomic_thread_fence]). + +* [^boost::memory_order_release]: Forces all earlier operations performed + by this thread in program order to become visible to another thread + that has read the value written by this atomic operation. Used + in conjunction with [^boost::memory_order_acquire] to enforce + inter-thread ordering of operations. + +* [^boost::memory_order_acquire]: Forces all later operations performed by + this thread in program order to "happen after" the corresponding + release operation(s). Used in conjunction with [^boost::memory_order_release] + to enforce inter-thread ordering of operations. + +* [^boost::memory_order_consume]: Forces all later operations performed by + this thread in program order that computationally depend on the value obtained + from the atomic variable to "happen after" the corresponding + release operation(s). This is a weaker (and more efficient) form of + "acquire" that is suitable in certain situations. See the discussion in + section FIXME. + +* [^boost::memory_order_acq_rel]: Combines the constraints of + [^boost::memory_order_acquire] and [^boost::memory_order_release]. + +* [^boost::memory_order_seq_cst]: Behaves like + [^boost::memory_order_acq_rel], + but additionally requires that there exists a total order for all + operations qualified with [^memory_order_seq_cst]. + +The default ordering constraint (if none is specified) of +every atomic operation is [^boost::memory_order_seq_cst] +which implies both "release" and an "acquire". This means that, +failing to specify any other constraint, each operation on +an atomic variable already acts as ['release] and ['acquire] +operation. + +[endsect] + +[endsect] + +[endsect] + +[section:usage_examples Usage examples] + +[include examples.qbk] + +[endsect] + +[section:platform_support Implementing support for additional platforms] + +[include platform.qbk] + +[endsect] + +[xinclude autodoc.xml] + +[section:tested_compilers Tested compilers] + +[*Boost.Atomic] has been tested on and is known to work on +the following compilers/platforms: + +* GCC 4.3.2/Linux x86_32 (aka i386), x86_64 (aka amd64), ppc32, alpha +* GCC 4.3.2/FreeBSD x86_32 (aka i386) +* Visual Studio Express 2008/Windows XP, x86_32 (aka i386) + +If you have an unsupported platform, contact me and I will +work to add support for it. + +[endsect] + + diff --git a/libs/atomic/doc/examples.qbk b/libs/atomic/doc/examples.qbk new file mode 100644 index 00000000..a5ff63b7 --- /dev/null +++ b/libs/atomic/doc/examples.qbk @@ -0,0 +1,397 @@ +[section:example_reference_counters Reference counting] + +The purpose of a ['reference counter] is to count the number +of pointers to an object. The object can be destroyed as +soon as the reference counter reaches zero. + +[section Implementation] + +[c++] + + #include + #include + + class X { + public: + typedef boost::intrusive_ptr pointer; + X() : refcount_(0) {} + + private: + mutable boost::atomic refcount_; + friend void intrusive_ptr_add_ref(const X * x) + { + x->refcount_.fetch_add(1, boost::memory_order_relaxed); + } + friend void intrusive_ptr_release(const X * x) + { + if (x->refcount_.fetch_sub(1, boost::memory_order_release)==1) { + boost::atomic_thread_fence(boost::memory_order_acquire); + delete x; + } + } + }; + +[endsect] + +[section Usage] + +[c++] + + X::pointer x=new X; + +[endsect] + +[section Discussion] + +Increasing the reference counter can always be done with +[^memory_order_relaxed]: New references to an object can only +be formed from an existing reference, an passing an existing +reference from one thread to another must already provide any +required synchronization. + +It is important to enforce any possible access to the object in +one thread (through an existing reference) to ['happen before] +deleting the object in a different thread. This is achieved +by a "release" operation after dropping a reference (any +access to the object through this reference must obviously +happened before), and an "acquire" operation before +deleting the object. + +It would be possible to use [^memory_order_acq_rel] for the +[^fetch_sub] operation, but this results in unneeded "acquire" +operations when the reference counter does not yet reach zero +and may impose a performance penalty. + +[endsect] + +[endsect] + +[section:example_reference_counters_weak Reference counting with weak references] + +The purpose of a ['reference counter] is to count the number +of pointers to an object. The object can be destroyed as +soon as the reference counter reaches zero. There may be +"weak references" to the object that do not count towards preventing +the object from being freed. "True" references may be formed from +"weak" references unless the object has been deleted already. + +FIXME: The point to make here is that for upgrading "weak" to "full" +references requires memory_order_acquire. + +[endsect] + +[section:example_spinlock Spinlock] + +The purpose of a ['spin lock] is to prevent multiple threads +from concurrently accessing a shared data structure. In contrast +to a mutex, threads will busy-wait and waste CPU cycles instead +of yielding the CPU to another thread. ['Do not use spinlocks +unless you are certain that you understand the consequences.] + +[section Implementation] + +[c++] + + #include + + class spinlock { + private: + typedef enum {Locked, Unlocked} LockState; + boost::atomic state_; + + public: + spinlock() : state_(Unlocked) {} + + lock() + { + while(state_.exchange(Locked, boost::memory_order_acquire)==Locked) ; + } + unlock() + { + state_.store(Unlocked, boost::memory_order_release); + } + }; + +[endsect] + +[section Usage] + +[c++] + + spinlock s; + + s.lock(); + // access data structure here + s.unlock(); + +[endsect] + +[section Discussion] + +The purpose of the spinlock is to make sure that one access +to the shared data structure always strictly "happens before" +another. The usage of acquire/release in lock/unlock is required +and sufficient to guarantee this ordering. + +It would be correct to write the "lock" operation in the following +way: + +[c++] + + lock() + { + while(state_.exchange(Locked, boost::memory_order_relaxed)==Locked) ; + atomic_thread_fence(boost::memory_order_acquire); + } + +This "optimization" is however a) useless and b) may in fact hurt: +a) Since the thread will be busily spinning on a blocked spinlock, +it does not matter if it will waste the CPU cycles with just +"exchange" operations or with both useless "exchange" and "acquire" +operations. b) A tight "exchange" loop without any +processor-synchronizing instruction introduced through an "acquire" +operation will on some systems monopolize the memory subsystem +and degrade the performance of other system components. + +[endsect] + +[endsect] + +[section:singleton Double-checked singleton pattern] + +The purpose of the ['double-cheked singleton pattern] is to ensure +that at most one instance of a particular object is created. +If one instance has been created already, access to the existing +object should be as light-weight as possible. + +[section Implementation] + +[c++] + + #include + #include + + class X { + public: + static X * instance() + { + X * tmp=instance_.load(boost::memory_order_consume); + if (!tmp) { + boost::mutex::scoped_lock l(instantiation_mutex); + tmp=instance_.load(boost::memory_order_consume); + if (!tmp) { + tmp=new X; + instance_.store(tmp, boost::memory_order_release); + } + } + return tmp; + } + private: + static boost::atomic instance_; + static boost::mutex instantion_mutex; + } + + boost::atomic X::instance_(0); + +[endsect] + +[section Usage] + +[c++] + + X * x=X::instance(); + // dereference x + +[endsect] + +[section Discussion] + +The mutex makes sure that only one instance of the object is +ever created. The [^instance] method must make sure that any +dereference of the object strictly "happens after" creating +the instance in another thread. The use of [^memory_order_release] +after creating and initializing the object and [^memory_order_consume] +before dereferencing the object provides this guarantee. + +It would be permissible to use [^memory_order_acquire] instead of +[^memory_order_consume], but this provides a stronger guarantee +than is required since only operations depending on the value of +the pointer need to be ordered. + +[endsect] + +[endsect] + +[section:example_ringbuffer Wait-free ring buffer] + +A ['wait-free ring buffer] provides a mechanism for relaying objects +from one single "producer" thread to one single "consumer" thread without +any locks. The operations on this data structure are "wait-free" which +means that each operation finites within a constant number of steps. +This makes this data structure suitable for use in hard real-time systems +or for communication with interrupt/signal handlers. + +[section Implementation] + +[c++] + + #include + + template + class ringbuffer { + public: + ringbuffer() : head_(0), tail_(0) {} + + bool push(const T & value) + { + size_t head=head_.load(boost::memory_order_relaxed); + size_t next_head=next(head); + if (next_head==tail_.load(boost::memory_order_acquire)) return false; + ring_[head]=value; + head_.store(next_head, boost::memory_order_release); + return true; + } + bool pop(T & value) + { + size_t tail=tail_.load(boost::memory_order_relaxed); + if (tail==head_.load(boost::memory_order_acquire)) return false; + value=ring_[tail]; + tail_.store(next(tail), boost::memory_odrder_release)); + return true; + } + private: + size_t next(size_t current) {return current+1;} + T ring_[Size]; + boost::atomic head_, tail_; + } + +[endsect] + +[section Usage] + +[c++] + + ringbuffer r; + + // try to insert an element + if (r.push(42)) { /* succeeded */ } + else { /* buffer full */ } + + // try to retrieve an element + int value; + if (r.pop(value)) { /* succeeded */ } + else { /* buffer empty */ } + +[endsect] + +[section Discussion] + +The implementation makes sure that the ring indices do +not "lap-around" each other to ensure that no elements +are either lost or read twice. + +Furthermore it must guarantee that read-access to a +particular object in [^pop] "happens after" it has been +written in [^push]. This is achieved by writing [^head_] +with "release" and reading it with "acquire". Conversely +the implementation also ensures that read access to +a particular ring element "happens before" before +rewriting this element with a new value by accessing [^tail_] +with appropriate ordering constraints. + +[endsect] + +[endsect] + +[section:mp_queue Wait-free multi-producer queue] + +The purpose of the ['wait-free multi-producer queue] is to allow +an arbitrary number of producers to enqueue objects which are +retrieved and processed in FIFO order by a single consumer. + +[section Implementation] + +[c++] + + // assume that every "T" object has a pointer "T * T::next" + template + class waitfree_queue { + public: + struct node { + T data; + node * next; + } + void push(const T &data) + { + node * n=new node; + n.data=data; + node * stale_head=head_.load(boost::memory_order_relaxed); + do { + node->next=stale_head; + } while(!head_.compare_exchange_weak(stale_head, node, boost::memory_order_release); + } + + node * pop_all(void) + { + T *last=pop_all_reverse(), *first=0; + while(last) { + T * tmp=last; + last=last->next; + tmp->next=first; + first=tmp; + } + return first; + } + + waitfree_queue() : head_(0) {} + + // alternative interface if ordering is of no importance + node * pop_all_reverse(void) + { + return head_.exchange(0, boost::memory_order_consume); + } + private: + boost::atomic head_; + } + +[endsect] + +[section Usage] + +[c++] + + waitfree_queue q; + + // insert elements + q.push(42); + q.push(2); + + // pop elements + waitfree_queue::node * x=q.pop_all() + while(x) { + X * tmp=x; + x=x->next; + // process tmp->data, probably delete it afterwards + delete tmp; + } + +[endsect] + +[section Discussion] + +The implementation guarantees that all objects enqueued are +processed in the order they were enqueued by building a singly-linked +list of object in reverse processing order. The queue is atomically +emptied by the consumer and brought into correct order. + +It must be guaranteed that any access to an object to be enqueued +by the producer "happens before" any access by the consumer. This +is assured by inserting objects into the list with ['release] and +dequeuing them with ['consume] memory order. It is not +necessary to use ['acquire] memory order in [^waitfree_queue::pop_all] +because all operations involved depend on the value of +the atomic pointer through dereference + +[endsect] + +[endsect] diff --git a/libs/atomic/doc/platform.qbk b/libs/atomic/doc/platform.qbk new file mode 100644 index 00000000..d2d251ad --- /dev/null +++ b/libs/atomic/doc/platform.qbk @@ -0,0 +1,305 @@ +[section:template_organization Organization of class template layers] + +The implementation uses multiple layers of template classes that +inherit from the next lower level each and refine or adapt the respective +underlying class: + +* [^boost::atomic] is the topmost-level, providing + the external interface. Implementation-wise, it does not add anything + (except for hiding copy constructor and assignment operator). + +* [^boost::detail::atomic::internal_atomic& >]: + This layer is mainly responsible for providing the overloaded operators + mapping to API member functions (e.g. [^+=] to [^fetch_add]). + The defaulted template parameter [^I] allows + to expose the correct API functions (via partial template + specialization): For non-integral types, it only + publishes the various [^exchange] functions + as well as load and store, for integral types it + additionally exports arithmetic and logic operations. + [br] + Depending on whether the given type is integral, it + inherits from either [^boost::detail::atomic::platform_atomic] + or [^boost::detail::atomic::platform_atomic_integral]. + There is however some special-casing: for non-integral types + of size 1, 2, 4 or 8, it will coerce the datatype into an integer representation + and delegate to [^boost::detail::atomic::platform_atomic_integral] + -- the rationale is that platform implementors only need to provide + integer-type operations. + +* [^boost::detail::atomic::platform_atomic_integral] + must provide the full set of operations for an integral type T + (i.e. [^load], [^store], [^exchange], + [^compare_exchange_weak], [^compare_exchange_strong], + [^fetch_add], [^fetch_sub], [^fetch_and], + [^fetch_or], [^fetch_xor], [^is_lock_free]). + The default implementation uses locking to emulate atomic operations, so + this is the level at which implementors should provide template specializations + to add support for platform-specific atomic operations. + [br] + The two separate template parameters allow separate specialization + on size and type (which, with fixed size, cannot + specify more than signedness/unsignedness). The rationale is that + most platform-specific atomic operations usually depend only on the + operand size, so that common implementations for signed/unsigned + types are possible. Signedness allows to properly to choose sign-extending + instructions for the [^load] operation, avoiding later + conversion. The expectation is that in most implementations this will + be a normal assignment in C, possibly accompanied by memory + fences, so that the compiler can automatically choose the correct + instruction. + +* At the lowest level, [^boost::detail::atomic::platform_atomic] + provides the most basic atomic operations ([^load], [^store], + [^exchange], [^compare_exchange_weak], + [^compare_exchange_strong]) for arbitrarily generic data types. + The default implementation uses locking as a fallback mechanism. + Implementors generally do not have to specialize at this level + (since these will not be used for the common integral type sizes + of 1, 2, 4 and 8 bytes), but if s/he can if s/he so wishes to + provide truly atomic operations for "odd" data type sizes. + Some amount of care must be taken as the "raw" data type + passed in from the user through [^boost::atomic] + is visible here -- it thus needs to be type-punned or otherwise + manipulated byte-by-byte to avoid using overloaded assigment, + comparison operators and copy constructors. + +[endsect] + + +[section:platform_atomic_implementation Implementing platform-specific atomic operations] + +In principle implementors are responsible for providing the +full range of named member functions of an atomic object +(i.e. [^load], [^store], [^exchange], +[^compare_exchange_weak], [^compare_exchange_strong], +[^fetch_add], [^fetch_sub], [^fetch_and], +[^fetch_or], [^fetch_xor], [^is_lock_free]). +These must be implemented as partial template specializations for +[^boost::detail::atomic::platform_atomic_integral]: + +[c++] + + template + class platform_atomic_integral + { + public: + explicit platform_atomic_integral(T v) : i(v) {} + platform_atomic_integral(void) {} + + T load(memory_order order=memory_order_seq_cst) const volatile + { + // platform-specific code + } + void store(T v, memory_order order=memory_order_seq_cst) volatile + { + // platform-specific code + } + + private: + volatile T i; + }; + +As noted above, it will usually suffice to specialize on the second +template argument, indicating the size of the data type in bytes. + +[section:automatic_buildup Templates for automatic build-up] + +Often only a portion of the required operations can be +usefully mapped to machine instructions. Several helper template +classes are provided that can automatically synthesize missing methods to +complete an implementation. + +At the minimum, an implementor must provide the +[^load], [^store], +[^compare_exchange_weak] and +[^is_lock_free] methods: + +[c++] + + template + class my_atomic_32 { + public: + my_atomic_32() {} + my_atomic_32(T initial_value) : value(initial_value) {} + + T load(memory_order order=memory_order_seq_cst) volatile const + { + // platform-specific code + } + void store(T new_value, memory_order order=memory_order_seq_cst) volatile + { + // platform-specific code + } + bool compare_exchange_weak(T &expected, T desired, + memory_order success_order, + memory_order_failure_order) volatile + { + // platform-specific code + } + bool is_lock_free() const volatile {return true;} + protected: + // typedef is required for classes inheriting from this + typedef T integral_type; + private: + T value; + }; + +The template [^boost::detail::atomic::build_atomic_from_minimal] +can then take care of the rest: + +[c++] + + template + class platform_atomic_integral + : public boost::detail::atomic::build_atomic_from_minimal > + { + public: + typedef build_atomic_from_minimal > super; + + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} + }; + +There are several helper classes to assist in building "complete" +atomic implementations from different starting points: + +* [^build_atomic_from_minimal] requires + * [^load] + * [^store] + * [^compare_exchange_weak] (4-operand version) + +* [^build_atomic_from_exchange] requires + * [^load] + * [^store] + * [^compare_exchange_weak] (4-operand version) + * [^compare_exchange_strong] (4-operand version) + * [^exchange] + +* [^build_atomic_from_add] requires + * [^load] + * [^store] + * [^compare_exchange_weak] (4-operand version) + * [^compare_exchange_strong] (4-operand version) + * [^exchange] + * [^fetch_add] + +* [^build_atomic_from_typical] (supported on gcc only) requires + * [^load] + * [^store] + * [^compare_exchange_weak] (4-operand version) + * [^compare_exchange_strong] (4-operand version) + * [^exchange] + * [^fetch_add_var] (protected method) + * [^fetch_inc] (protected method) + * [^fetch_dec] (protected method) + + This will generate a [^fetch_add] method + that calls [^fetch_inc]/[^fetch_dec] + when the given parameter is a compile-time constant + equal to +1 or -1 respectively, and [^fetch_add_var] + in all other cases. This provides a mechanism for + optimizing the extremely common case of an atomic + variable being used as a counter. + + The prototypes for these methods to be implemented is: + [c++] + + template + class my_atomic { + public: + T fetch_inc(memory_order order) volatile; + T fetch_dec(memory_order order) volatile; + T fetch_add_var(T counter, memory_order order) volatile; + }; + +These helper templates are defined in [^boost/atomic/detail/builder.hpp]. + +[endsect] + +[section:automatic_buildup_small Build sub-word-sized atomic data types] + +There is one other helper template that can build sub-word-sized +atomic data types even though the underlying architecture allows +only word-sized atomic operations: + +[c++] + + template + class platform_atomic_integral : + public build_atomic_from_larger_type, T> + { + public: + typedef build_atomic_from_larger_type, T> super; + + explicit platform_atomic_integral(T v) : super(v) {} + platform_atomic_integral(void) {} + }; + +The above would create an atomic data type of 1 byte size, and +use masking and shifts to map it to 32-bit atomic operations. +The base type must implement [^load], [^store] +and [^compare_exchange_weak] for this to work. + +[endsect] + +[section:other_sizes Atomic data types for unusual object sizes] + +In unusual circumstances, an implementor may also opt to specialize +[^public boost::detail::atomic::platform_atomic] +to provide support for atomic objects not fitting an integral size. +If you do that, keep the following things in mind: + +* There is no reason to ever do this for object sizes + of 1, 2, 4 and 8 +* Only the following methods need to be implemented: + * [^load] + * [^store] + * [^compare_exchange_weak] (4-operand version) + * [^compare_exchange_strong] (4-operand version) + * [^exchange] + +The type of the data to be stored in the atomic +variable (template parameter [^T]) +is exposed to this class, and the type may have +overloaded assignment and comparison operators -- +using these overloaded operators however will result +in an error. The implementor is responsible for +accessing the objects in a way that does not +invoke either of these operators (using e.g. +[^memcpy] or type-casts). + +[endsect] + +[endsect] + +[section:platform_atomic_fences Fences] + +Platform implementors need to provide a function performing +the action required for [funcref boost::atomic_thread_fence atomic_thread_fence] +(the fallback implementation will just perform an atomic operation +on an integer object). This is achieved by specializing the +[^boost::detail::atomic::platform_atomic_thread_fence] template +function in the following way: + +[c++] + + template<> + void platform_atomic_thread_fence(memory_order order) + { + // platform-specific code here + } + +[endsect] + +[section:platform_atomic_puttogether Putting it altogether] + +The template specializations should be put into a header file +in the [^boost/atomic/detail] directory, preferrably +specifying supported compiler and architecture in its name. + +The file [^boost/atomic/platform.hpp] must +subsequently be modified to conditionally include the new +header. + +[endsect] diff --git a/libs/atomic/test/simple.cpp b/libs/atomic/test/simple.cpp new file mode 100644 index 00000000..6778ef7b --- /dev/null +++ b/libs/atomic/test/simple.cpp @@ -0,0 +1,251 @@ +#include +#include +#include + +#include + +using namespace boost; + +template +void test_atomic_arithmetic(void) +{ + atomic i(41); + + T n; + + printf("Type=%s, size=%ld, atomic_size=%ld, lockfree=%d\n", + typeid(T).name(), (long)sizeof(n), (long)sizeof(i), i.is_lock_free()); + + assert(sizeof(i)>=sizeof(n)); + + bool success; + + n=i++; + assert(i==42); + assert(n==41); + + n=i--; + assert(n==42); + assert(i==41); + + n=++i; + assert(i==42); + assert(n==42); + + n=--i; + assert(n==41); + assert(i==41); + + n=i.fetch_and(15); + assert(n==41); + assert(i==9); + + n=i.fetch_or(17); + assert(n==9); + assert(i==25); + + n=i.fetch_xor(3); + assert(n==25); + assert(i==26); + + n=i.exchange(12); + assert(n==26); + assert(i==12); + + n=12; + success=i.compare_exchange_strong(n, 17); + assert(success); + assert(n==12); + assert(i==17); + + n=12; + success=i.compare_exchange_strong(n, 19); + assert(!success); + assert(n==17); + assert(i==17); +} + +template +void test_atomic_base(void) +{ + atomic i; + T n; + + printf("Type=%s, size=%ld, atomic_size=%ld, lockfree=%d\n", + typeid(T).name(), (long)sizeof(n), (long)sizeof(i), i.is_lock_free()); + + assert(sizeof(i)>=sizeof(n)); + + bool success; + + i.store((T)0); + n=(T)40; + success=i.compare_exchange_strong(n, (T)44 /*boost::memory_order_relaxed*/); + assert(!success); + assert(n==(T)0); + assert(i.load()==(T)0); + + n=(T)0; + success=i.compare_exchange_strong(n, (T)44); + assert(success); + assert(n==(T)0); + assert(i.load()==(T)44); + + n=i.exchange((T)20); + assert(n==(T)44); + assert(i.load()==(T)20); +} + +template +void test_atomic_ptr(void) +{ + test_atomic_base(); + + T array[10], *p; + atomic ptr; + + ptr=&array[0]; + + p=ptr++; + assert(p==&array[0]); + assert(ptr==&array[1]); + p=++ptr; + assert(p==&array[2]); + assert(ptr==&array[2]); + + p=ptr.fetch_add(4); + assert(p==&array[2]); + assert(ptr==&array[6]); + + p=ptr.fetch_sub(4); + assert(p==&array[6]); + assert(ptr==&array[2]); + + p=ptr--; + assert(p==&array[2]); + assert(ptr==&array[1]); + p=--ptr; + assert(p==&array[0]); + assert(ptr==&array[0]); +} + +template<> +void test_atomic_base(void) +{ + atomic i; + bool n; + + printf("Type=bool, size=%ld, atomic_size=%ld, lockfree=%d\n", + (long)sizeof(n), (long)sizeof(i), i.is_lock_free()); + + assert(sizeof(i)>=sizeof(n)); + + bool success; + + i=false; + n=true; + success=i.compare_exchange_strong(n, true); + assert(!success); + assert(n==false); + assert(i==false); + + n=false; + success=i.compare_exchange_strong(n, true); + assert(success); + assert(n==false); + assert(i==true); + + n=i.exchange(false); + assert(n==true); + assert(i==false); +} + +void test_atomic_flag() +{ + atomic_flag f(0); + + assert(!f.test_and_set()); + assert(f.test_and_set()); + f.clear(); + assert(!f.test_and_set()); +} + +struct Compound { + int i; + + inline bool operator==(const Compound &c) const {return i==c.i;} +}; + +void test_atomic_struct(void) +{ + atomic i; + Compound n; + + Compound zero={0}, one={1}, two={2}; + + assert(sizeof(i)>=sizeof(n)); + + bool success; + + i.store(zero); + n=one; + success=i.compare_exchange_strong(n, two); + assert(!success); + assert(n==zero); + assert(i.load()==zero); + + n=zero; + success=i.compare_exchange_strong(n, two); + assert(success); + assert(n==zero); + assert(i.load()==two); + + n=i.exchange(one); + assert(n==two); + assert(i.load()==one); +} + +enum TestEnum { + Foo, Bar +}; + +void test_fence() +{ + atomic_thread_fence(memory_order_acquire); +} + +int main() +{ + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + test_atomic_arithmetic(); + + test_atomic_struct(); + + test_atomic_base(); + test_atomic_ptr(); + test_atomic_base(); + test_atomic_base(); + + atomic_thread_fence(memory_order_seq_cst); + + test_fence(); + + test_atomic_flag(); +} diff --git a/libs/extension/Jamrules b/libs/extension/Jamrules new file mode 100644 index 00000000..e69de29b diff --git a/libs/extension/Release 5-29-07.zip b/libs/extension/Release 5-29-07.zip new file mode 100644 index 00000000..7b93c13c Binary files /dev/null and b/libs/extension/Release 5-29-07.zip differ diff --git a/libs/extension/benchmarks/Jamfile.v2 b/libs/extension/benchmarks/Jamfile.v2 new file mode 100644 index 00000000..ad6f2ce5 --- /dev/null +++ b/libs/extension/benchmarks/Jamfile.v2 @@ -0,0 +1,41 @@ +# Boost.Extension - Benchmarks Jamfile +# +# Copyright 2007 Mariano G. Consoni +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# +# See http://www.boost.org/ for latest version. +# + +import type : change-generated-target-suffix ; +import type : change-generated-target-prefix ; +type.change-generated-target-suffix SHARED_LIB : : extension ; +type.change-generated-target-prefix SHARED_LIB : : lib ; +import os ; + +local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; +project + : requirements + ../../../ + $(BOOST_ROOT) + gcc:dl + boost_filesystem + : + ; + +exe PlainOldApproach : plain_old_approach.cpp ; +exe PlainOldApproachBF : plain_old_approach_bf.cpp ; +exe MultipleCalls : multiple_calls.cpp ; +exe MultipleLibraries : multiple_libraries.cpp ; +lib PlainOldHelloWorldLib : hello_world_plain_old.cpp : shared ; +lib HelloWorldLib : ../examples/hello_world.cpp : shared ; + +install ../bin : + PlainOldApproach PlainOldHelloWorldLib HelloWorldLib + : + # on + # EXE + # SHARED_LIB + # LIB + ; diff --git a/libs/extension/benchmarks/hello_world_plain_old.cpp b/libs/extension/benchmarks/hello_world_plain_old.cpp new file mode 100644 index 00000000..d12687f0 --- /dev/null +++ b/libs/extension/benchmarks/hello_world_plain_old.cpp @@ -0,0 +1,37 @@ +/* + * Boost.Extension / hello_world_plain_old benchmark + * hello world plugin implemented in dl* style + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include "../examples/word.hpp" + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +# define EXPORT_DECL __declspec(dllexport) +#else +# define EXPORT_DECL +#endif + + +class world : public word +{ +public: + virtual const char * get_val(){return "world!";} +}; +class hello : public word +{ +public: + virtual const char * get_val(){return "hello";} +}; + +extern "C" void EXPORT_DECL extension_export_words(word **h, word **w) +{ + *h = new hello; + *w = new world; +} diff --git a/libs/extension/benchmarks/multiple_calls.cpp b/libs/extension/benchmarks/multiple_calls.cpp new file mode 100644 index 00000000..8c784ec8 --- /dev/null +++ b/libs/extension/benchmarks/multiple_calls.cpp @@ -0,0 +1,142 @@ +/* + * Boost.Extension / multiple method class benchmark + * This benchmark calls a lot of times a method of an implementation + * (comparing dl* and extensions) + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif + +#ifndef WINDOWS_LEAN_AND_MEAN +#define WINDOWS_LEAN_AND_MEAN +#endif + +#include +# pragma comment(lib, "kernel32.lib") +#else +#include +#endif + + +#include "../examples/word.hpp" + + +int main(void) +{ + using namespace boost::extensions; + + const unsigned int times = 1000000; + + // boost.extensions style + boost::timer extensions_style; + { + shared_library l((std::string("libHelloWorldLib") + ".extension").c_str()); + l.open(); + { + factory_map fm; + functor load_func = l.get("extension_export_word"); + load_func(fm); + + std::map > & factory_list = fm.get(); + + for(unsigned int c = 0; c < times; ++c) { + + for (std::map >::iterator current_word = + factory_list.begin(); + current_word != factory_list.end(); + ++current_word) + { + std::auto_ptr word_ptr(current_word->second.create()); + + // do something with the word + std::string s(word_ptr->get_val()); + s += "\n"; + } + + } + } + l.close(); + } + std::cout << "Boost.extensions style: " << extensions_style.elapsed() + << std::endl; + + + // plain old style + boost::timer old_style; + { + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + HMODULE library = LoadLibrary("libPlainOldHelloWorldLib.extension"); +#else + void *library = dlopen("libPlainOldHelloWorldLib.extension", RTLD_LAZY); +#endif + + if(library == 0) { + std::cerr << "Cannot open Hello World Library." << std::endl; + return 1; + } + + { + typedef void (*export_words_function_type)(word **, word **); + export_words_function_type export_words; + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + export_words = + (export_words_function_type) GetProcAddress(library, + "extension_export_words"); +#else + *(void **) (&export_words) = dlsym(library, "extension_export_words"); +#endif + + if(export_words == 0) { + std::cerr << "Cannot get exported symbol." << std::endl; + return 1; + } + + for(unsigned int c = 0; c < times; ++c) { + // retrieve the words + word *first_word, *second_word; + (*export_words)(&first_word, &second_word); + + // do something with the word + std::string f(first_word->get_val()); + f += "\n"; + + std::string s(second_word->get_val()); + s += "\n"; + + delete first_word; + delete second_word; + } + } +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + FreeLibrary(library); +#else + dlclose(library); +#endif + } + std::cout << "Plain old style: " << old_style.elapsed() << std::endl; + + return 0; +} diff --git a/libs/extension/benchmarks/multiple_libraries.cpp b/libs/extension/benchmarks/multiple_libraries.cpp new file mode 100644 index 00000000..efbcf6a3 --- /dev/null +++ b/libs/extension/benchmarks/multiple_libraries.cpp @@ -0,0 +1,184 @@ +/* + * Boost.Extension / multiple libraries benchmark + * This benchmark loads a lot of libraries (comparing dl* and extensions) + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif + +#ifndef WINDOWS_LEAN_AND_MEAN +#define WINDOWS_LEAN_AND_MEAN +#endif + +#include +# pragma comment(lib, "kernel32.lib") +#else +#include +#endif + + +#include "../examples/word.hpp" + + +// copy the original library qty times to be loaded in the main routine +void copy_libraries(const std::string &lib_base, unsigned int qty) +{ + for(unsigned int i = 1; i <= qty; ++i) { + std::string library_copy = lib_base + + boost::lexical_cast(i) + + ".extension"; + + if(boost::filesystem::exists(lib_base + ".extension") && + !boost::filesystem::exists(library_copy)) { + + boost::filesystem::copy_file(lib_base + ".extension", library_copy); + } + } +} + + +// remove the libraries after using them +void remove_libraries(const std::string &lib_base, unsigned int qty) +{ + for(unsigned int i = 1; i <= qty; ++i) { + std::string library_copy = lib_base + + boost::lexical_cast(i) + + ".extension"; + + if(boost::filesystem::exists(library_copy)) { + boost::filesystem::remove(library_copy); + } + } +} + + +int main(void) +{ + using namespace boost::extensions; + + unsigned int libs = 500; + + copy_libraries("libHelloWorldLib", libs); + copy_libraries("libPlainOldHelloWorldLib", libs); + + + // boost.extensions style + boost::timer extensions_style; + for(unsigned int lib_number = 1; lib_number <= libs; ++lib_number) { + + shared_library l(std::string("libHelloWorldLib" + + boost::lexical_cast(lib_number) + + ".extension").c_str()); + + l.open(); + { + factory_map fm; + void (*load_func)(factory_map &) = + l.get("extension_export_word"); + + load_func(fm); + + std::map > & factory_list = fm.get(); + for (std::map >::iterator current_word = + factory_list.begin(); current_word != factory_list.end(); + ++current_word) + { + // Using auto_ptr to avoid needing delete. Using smart_ptrs is + // recommended. + // Note that this has a zero argument constructor - currently constructors + // with up to six arguments can be used. + std::auto_ptr word_ptr(current_word->second.create()); + std::string cheese = word_ptr->get_val(); + } + } + l.close(); + } + std::cout << "Boost.extensions style: " << extensions_style.elapsed() + << std::endl; + + + // plain old style + boost::timer old_style; + for(unsigned int lib_number = 1; lib_number <= libs; ++lib_number) { + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + HMODULE library = LoadLibrary(std::string("libPlainOldHelloWorldLib" + + boost::lexical_cast(lib_number) + + ".extension").c_str()); +#else + void *library = dlopen(std::string("libPlainOldHelloWorldLib" + + boost::lexical_cast(lib_number) + + ".extension").c_str(), RTLD_LAZY); +#endif + + if(library == 0) { + std::cerr << "Cannot open Hello World Library (libPlainOldHelloWorldLib" + << lib_number << ".extension)" << std::endl; + + return 1; + } + typedef void (*export_words_function_type)(word **, word **); + export_words_function_type export_words; + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + export_words = (export_words_function_type) GetProcAddress(library, + "extension_export_words"); +#else + *(void **) (&export_words) = dlsym(library, "extension_export_words"); +#endif + + if(export_words == 0) { + std::cerr << "Cannot get exported symbol." << std::endl; + return 1; + } + + // retrieve the words + word *first_word, *second_word; + (*export_words)(&first_word, &second_word); + + // do something with the word + std::string f(first_word->get_val()); + f += "\n"; + + std::string s(second_word->get_val()); + s += "\n"; + + delete first_word; + delete second_word; + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + FreeLibrary(library); +#else + dlclose(library); +#endif + } + std::cout << "Plain old style: " << old_style.elapsed() << std::endl; + + + remove_libraries("libHelloWorldLib", libs); + remove_libraries("libPlainOldHelloWorldLib", libs); + + return 0; +} diff --git a/libs/extension/benchmarks/plain_old_approach.cpp b/libs/extension/benchmarks/plain_old_approach.cpp new file mode 100644 index 00000000..b692c32f --- /dev/null +++ b/libs/extension/benchmarks/plain_old_approach.cpp @@ -0,0 +1,128 @@ +/* + * Boost.Extension / dl* / extensions compare benchmark + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif + +#ifndef WINDOWS_LEAN_AND_MEAN +#define WINDOWS_LEAN_AND_MEAN +#endif + +#include +# pragma comment(lib, "kernel32.lib") +#else +#include +#endif + + +#include "../examples/word.hpp" + + +int main(void) +{ + using namespace boost::extensions; + + const unsigned int times = 1000; + + // boost.extensions style + boost::timer extensions_style; + for(unsigned int c = 0; c < times; ++c) { + + shared_library l((std::string("libHelloWorldLib") + ".extension").c_str()); + l.open(); + { + factory_map fm; + functor load_func = + l.get("extension_export_word"); + load_func(fm); + + std::map > & factory_list = fm.get(); + for (std::map >::iterator current_word = + factory_list.begin(); current_word != factory_list.end(); + ++current_word) { + std::auto_ptr word_ptr(current_word->second.create()); + + // do something with the word + std::string s(word_ptr->get_val()); + s += "\n"; + } + } + l.close(); + } + std::cout << "Boost.extensions style: " << extensions_style.elapsed() + << std::endl; + + + // plain old style + boost::timer old_style; + for(unsigned int c = 0; c < times; ++c) { + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + HMODULE library = LoadLibrary("libPlainOldHelloWorldLib.extension"); +#else + void *library = dlopen("libPlainOldHelloWorldLib.extension", RTLD_LAZY); +#endif + + if(library == 0) { + std::cerr << "Cannot open Hello World Library." << std::endl; + return 1; + } + typedef void (*export_words_function_type)(word **, word **); + export_words_function_type export_words; + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + export_words = (export_words_function_type) GetProcAddress(library, + "extension_export_words"); +#else + *(void **) (&export_words) = dlsym(library, "extension_export_words"); +#endif + + if(export_words == 0) { + std::cerr << "Cannot get exported symbol." << std::endl; + return 1; + } + + // retrieve the words + word *first_word, *second_word; + (*export_words)(&first_word, &second_word); + + // do something with the word + std::string f(first_word->get_val()); + f += "\n"; + + std::string s(second_word->get_val()); + s += "\n"; + + delete first_word; + delete second_word; + +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + FreeLibrary(library); +#else + dlclose(library); +#endif + } + std::cout << "Plain old style: " << old_style.elapsed() << std::endl; + + return 0; +} diff --git a/libs/extension/benchmarks/plain_old_approach_bf.cpp b/libs/extension/benchmarks/plain_old_approach_bf.cpp new file mode 100644 index 00000000..73748315 --- /dev/null +++ b/libs/extension/benchmarks/plain_old_approach_bf.cpp @@ -0,0 +1,18 @@ +/* + * Boost.Extension / dl* / extensions compare benchmark (with boost::function) + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#define BOOST_EXTENSION_USE_BOOST_FUNCTION 1 + +// We include here a .cpp to be able to compile it with +// the BOOST_EXTENSION_USE_BOOST_FUNCTION variable defined. +// In this way we can compare Boost.Extension with and +// without boost::function. +#include "plain_old_approach.cpp" diff --git a/libs/extension/boost.png b/libs/extension/boost.png new file mode 100644 index 00000000..b4d51fcd Binary files /dev/null and b/libs/extension/boost.png differ diff --git a/libs/extension/doc/Jamfile.v2 b/libs/extension/doc/Jamfile.v2 new file mode 100644 index 00000000..ebf6d4cc --- /dev/null +++ b/libs/extension/doc/Jamfile.v2 @@ -0,0 +1,58 @@ +# Boost.Extension - documentation Jamfile +# +# Copyright 2008 Jeremy Pack +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# +# See http://www.boost.org/ for latest version. +# + +import doxygen ; +import quickbook ; + +import os ; +local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; + +doxygen autodoc_extension + : + [ glob ../../../boost/reflection/*.hpp ] + [ glob ../../../boost/extension/*.hpp ] + : + EXTRACT_ALL=NO + "PREDEFINED=\"BOOST_EXTENSION_DOXYGEN_INVOKED\" \\ + \"BOOST_REFLECTION_DOXYGEN_INVOKED\" \\ + \"U_EXPORT2=\"" + HIDE_UNDOC_MEMBERS=NO + EXTRACT_PRIVATE=NO + ENABLE_PREPROCESSING=YES + MACRO_EXPANSION=YES + EXPAND_ONLY_PREDEF=YES + SEARCH_INCLUDES=YES + INCLUDE_PATH=$(BOOST_ROOT) + INLINE_SOURCES=YES + SOURCE_BROWSER=YES + VERBATIM_HEADERS=YES + REFERENCES_RELATION=YES + REFERENCES_LINK_SOURCE=YES + ; + +xml extension : extension.qbk ; + +boostbook standalone + : + extension + : + toc.max.depth=2 + toc.section.depth=2 + chunk.section.depth=2 + autodoc_extension + ; + +install css : [ glob $(BOOST_ROOT)/doc/src/*.css ] + : html ; +install images : [ glob $(BOOST_ROOT)/doc/src/images/*.png ] + : html/images ; + +install main_image : [ glob $(BOOST_ROOT)/boost.png ] + : ../ ; \ No newline at end of file diff --git a/libs/extension/doc/appendices.qbk b/libs/extension/doc/appendices.qbk new file mode 100644 index 00000000..4dce17c8 --- /dev/null +++ b/libs/extension/doc/appendices.qbk @@ -0,0 +1,17 @@ +[/ Boost.Extension - appendices ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:appendices Appendices] +[section:appendices_description Contents in Appendices] +The reference documentation for both the Boost.Extension and Boost.Reflection +libraries are contained here, until it is decided where (and if) to split the +two libraries, since they share many pieces of functionality. +[endsect] +[xinclude autodoc_extension.xml] +[include appendix_a.qbk] +[include appendix_b.qbk] +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/appendix_a.qbk b/libs/extension/doc/appendix_a.qbk new file mode 100644 index 00000000..0537cc99 --- /dev/null +++ b/libs/extension/doc/appendix_a.qbk @@ -0,0 +1,36 @@ +[/ Boost.Extension - windows export and import decs ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:appendix_a Appendix A - Windows Export and Import Declarations] +On the Windows platform, special export declarations are required to make +functions or classes in a .dll file accessible to the outside world. For +normal usage of Boost.Extension, the only place this is required is in the +one function that is exported from each .dll. The Hello World sample program +illustrates how this is done using `BOOST_EXTENSION_EXPORT`. + +For implementation inheritance across shared libraries, such as that in the +Multiple Inheritance sample, each class that is not header-only that needs to be +exported must have these declarations included. To make it more difficult, +they must be different depending on whether the class is being imported into or +exported from the current module. The Multiple Inheritance sample shows how this +is done. + +[h3 Warning] +Now for a word of warning that many of you will ignore: In the great majority +of cases, even if non-header-only implementation inheritance across shared +libraries seems like a good idea, there is probably a better solution. Although +it certainly has its uses, it can cause problems like the following: + + +* Version problems - if the original implementation changes at all, every +dependent shared library must be recompiled with the new implementation. +* Tight coupling between shared libraries. One of the primary reasons for using +shared libraries is to decrease coupling, and promote reuse. +* It can lead to overly complicated, over-designed class hierarchies. + + +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/appendix_b.qbk b/libs/extension/doc/appendix_b.qbk new file mode 100644 index 00000000..70d53423 --- /dev/null +++ b/libs/extension/doc/appendix_b.qbk @@ -0,0 +1,23 @@ +[/ Boost.Extension - optional libraries ] +[/ Copyright 2008 Mariano G. Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:appendix_b Appendix B - Optional Dependencies] + +As suggested by Boost guidelines and our thoughts, we tried to keep the number +of dependencies of Boost.Extension at the minimum. But we also wanted to take +advantage of several boost libraries that have additional features and are easy +to use. + +For basic use of the library, currently only Boost.Preprocessor is required. +There are certain convenience headers that rely on other libraries, such +as Boost.Filesystem. + +Also, the `shared_library` class has been designed to be easy to use with +Boost.Function. It returns function pointers that can be loaded directly +into Boost.Function objects. + +[endsect] diff --git a/libs/extension/doc/appendix_c.qbk b/libs/extension/doc/appendix_c.qbk new file mode 100644 index 00000000..e69de29b diff --git a/libs/extension/doc/extension-reflection.qbk b/libs/extension/doc/extension-reflection.qbk new file mode 100644 index 00000000..4bbd09a8 --- /dev/null +++ b/libs/extension/doc/extension-reflection.qbk @@ -0,0 +1,138 @@ +[/ Boost.Reflection - main doc ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:extension-reflection Boost.Reflection/Boost.Extension Interoperability] +Reflections are designed to work with Boost.Extension, or with +shared libraries in general. A simple example is included in +examples/extension/. + +Declaring the reflected class itself is similar to the +process for doing the same in Boost.Extension. + +Here's how the Jamfile for these libraries is defined. Note +that, like in Boost.Extension, one can rename the shared library +extensions, for consistency across platforms. Here, we use the +.reflection suffix for each shared library. +`` +import type : change-generated-target-suffix ; +import type : change-generated-target-prefix ; +type.change-generated-target-suffix SHARED_LIB : : reflection ; +type.change-generated-target-prefix SHARED_LIB : : lib ; +exe extension-reflection : extension/extension.cpp ; +lib car_lib : extension/car_lib.cpp : shared ; +`` + +The code in the shared library is defined in +libs/reflection/examples/extension/car_lib.cpp. + +We define two classes to export as reflections. +Note that they are not derived from the same base class. +If we were using Boost.Extension factories, this would be required. +`` +class suv +{ +public: + suv(const char * name) : car(name) {} + const char * get_type(void) { return "It's an SUV."; } + ~suv(void) {} +}; + +class compact +{ +public: + compact(const char * name) : car(name) {} + const char * get_type(void) { return "It's a compact."; } + ~compact(void) {} +}; +`` + +Just like Boost.Extension, an external function needs to be +defined that will be called by the main module. + +extern "C" +void BOOST_EXTENSION_EXPORT_DECL +extension_export_car(std::map reflection_map) { + reflection_map["suv"] + .reflect() + .constructor() + .function(&suv::get_type, "get_type"); + reflection_map["suv"] + .reflect() + .constructor() + .function(&compact::get_type, "get_type"); +} + +This is all that is necessary to export one constructor and one +function for each class. + +Now, in extension.cpp, we combine Boost.Extension and +Boost.Reflection code to load and use the reflections declared +in the shared library. + +Create a mapping of reflections to strings that +will be populated inside the shared library. +`` +std::map reflection_map; +`` +Load the shared library using Boost.Extension. +`` +boost::extensions::shared_library lib + ((std::string(BOOST_EXTENSION_DIR_START) + + "libcar_lib.extension").c_str()); +lib.open(); +`` +Call an exported function to populate +reflection_map. +`` +lib.get &> + ("extension_export_car")(reflection_map); +if (reflection_map.size() != size_t(2)) { + std::cout << "Could not load reflections!"; + return 1; +} +`` +Pull out two reflections that were named "suv" and +"compact" respectively. +`` +reflection & first_reflection = + reflection_map["suv"]; +reflection & second_reflection = + reflection_map["compact"]; +`` +Use the get_constructor function to find a constructor +that takes one argument, a const char*. +`` +instance_constructor first_constructor = + first_reflection.get_constructor(); +`` +Use the constructor retrieved to create an instance. +Warning! instances should only be used with functions +and constructors generated by a single reflection object. +`` +instance first_instance = + first_constructor("First Instance"); +`` +Get a function to call on this instance. +`` +boost::reflections::function first_function = + first_reflection.get_function("get_type"); +std::cout << "First reflection: " << first_function(first_instance) + << std::endl; + `` +Repeat the steps for the second reflection. +`` +instance_constructor second_constructor = + second_reflection.get_constructor(); +instance second_instance = + second_constructor("Second Instance"); +boost::reflections::function second_function = + second_reflection.get_function("get_type"); +std::cout << "Second reflection: " << second_function(second_instance) + << std::endl; +`` +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/extension.qbk b/libs/extension/doc/extension.qbk new file mode 100644 index 00000000..ac663bef --- /dev/null +++ b/libs/extension/doc/extension.qbk @@ -0,0 +1,59 @@ +[/ Boost.Extension - main doc ] +[/ Copyright 2008-2008 Jeremy Pack and Mariano Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[library Boost.Extension + [quickbook 1.4] + [copyright 2008 Jeremy Pack] + [authors [Pack, Jeremy]] + [purpose + Factory management, reflection and facilities for using shared libraries] + [license + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + [@http://www.boost.org/LICENSE_1_0.txt http://www.boost.org/LICENSE_1_0.txt]) + ] + [category Programming Interfaces] + [last-revision $Date: 2008/7/25 7:36:00 $] +] + + +[def _shared_library_ [^[classref boost::extensions::shared_library shared_library]]] +[def _shared_library_get_ [^[memberref boost::extensions::shared_library::get shared_library::get]]] +[def _factory_map_ [^[classref boost::extensions::factory_map factory_map]]] +[def _factory_ [^[classref boost::extensions::factory factory]]] +[def _type_map_ [^[classref boost::extensions::type_map type_map]]] +[def _adaptable_factory_ [^[classref boost::extensions::adaptable_factory adaptable_factory]]] +[def _reflection_ [^[classref boost::reflections::reflection reflection]]] +[def _parameter_map_ [^[classref boost::reflections::parameter_map parameter_map]]] +[def _basic_parameter_map_ [^[classref boost::reflections::basic_parameter_map basic_parameter_map]]] +[def _parameter_ [^[classref boost::reflections::parameter parameter]]] + +[section:recent_changes Recent Changes] + +The most recent changes to Boost.Extension, in preparation for this library's submission +to Boost for review: + + +* Deprecation of _factory_map_ in favor of _type_map_. +* All reflection documentation will be contained within the Extension docs. +During review, or perhaps shortly before, it will be determined whether or not +these should be two separate libraries, and if so, where the split should occur - +since they share significant functionality. +* The _adaptable_factory_ class has been added to facilitate calling factories when +the parameters of the constructor are unknown. +* The addition of the source code for a runtime compilation example. This involves compiling +a shared library at runtime, and calling code from it. Examples will come soon. + +[endsect] +[include introduction.qbk] +[include motivation.qbk] +[include shared_libraries.qbk] +[include tutorials.qbk] +[include extension_reflection.qbk] +[include type_safety.qbk] +[include faq.qbk] +[include appendices.qbk] diff --git a/libs/extension/doc/extension_reflection.qbk b/libs/extension/doc/extension_reflection.qbk new file mode 100644 index 00000000..172334d4 --- /dev/null +++ b/libs/extension/doc/extension_reflection.qbk @@ -0,0 +1,138 @@ +[/ Boost.Reflection - main doc ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:extension_reflection Boost.Reflection/Boost.Extension Interoperability] +Reflections are designed to work with Boost.Extension, or with +shared libraries in general. A simple example is included in +examples/extension/. + +Declaring the reflected class itself is similar to the +process for doing the same in Boost.Extension. + +Here's how the Jamfile for these libraries is defined. Note +that, like in Boost.Extension, one can rename the shared library +extensions, for consistency across platforms. Here, we use the +.reflection suffix for each shared library. +`` +import type : change-generated-target-suffix ; +import type : change-generated-target-prefix ; +type.change-generated-target-suffix SHARED_LIB : : reflection ; +type.change-generated-target-prefix SHARED_LIB : : lib ; +exe extension-reflection : extension/extension.cpp ; +lib car_lib : extension/car_lib.cpp : shared ; +`` + +The code in the shared library is defined in +libs/reflection/examples/extension/car_lib.cpp. + +We define two classes to export as reflections. +Note that they are not derived from the same base class. +If we were using Boost.Extension factories, this would be required. +`` +class suv +{ +public: + suv(const char * name) : car(name) {} + const char * get_type(void) { return "It's an SUV."; } + ~suv(void) {} +}; + +class compact +{ +public: + compact(const char * name) : car(name) {} + const char * get_type(void) { return "It's a compact."; } + ~compact(void) {} +}; +`` + +Just like Boost.Extension, an external function needs to be +defined that will be called by the main module. + +extern "C" +void BOOST_EXTENSION_EXPORT_DECL +extension_export_car(std::map reflection_map) { + reflection_map["suv"] + .reflect() + .constructor() + .function(&suv::get_type, "get_type"); + reflection_map["suv"] + .reflect() + .constructor() + .function(&compact::get_type, "get_type"); +} + +This is all that is necessary to export one constructor and one +function for each class. + +Now, in extension.cpp, we combine Boost.Extension and +Boost.Reflection code to load and use the reflections declared +in the shared library. + +Create a mapping of reflections to strings that +will be populated inside the shared library. +`` +std::map reflection_map; +`` +Load the shared library using Boost.Extension. +`` +boost::extensions::shared_library lib + ((std::string(BOOST_EXTENSION_DIR_START) + + "libcar_lib.extension").c_str()); +lib.open(); +`` +Call an exported function to populate +reflection_map. +`` +lib.get &> + ("extension_export_car")(reflection_map); +if (reflection_map.size() != size_t(2)) { + std::cout << "Could not load reflections!"; + return 1; +} +`` +Pull out two reflections that were named "suv" and +"compact" respectively. +`` +reflection & first_reflection = + reflection_map["suv"]; +reflection & second_reflection = + reflection_map["compact"]; +`` +Use the get_constructor function to find a constructor +that takes one argument, a const char*. +`` +instance_constructor first_constructor = + first_reflection.get_constructor(); +`` +Use the constructor retrieved to create an instance. +Warning! instances should only be used with functions +and constructors generated by a single reflection object. +`` +instance first_instance = + first_constructor("First Instance"); +`` +Get a function to call on this instance. +`` +boost::reflections::function first_function = + first_reflection.get_function("get_type"); +std::cout << "First reflection: " << first_function(first_instance) + << std::endl; + `` +Repeat the steps for the second reflection. +`` +instance_constructor second_constructor = + second_reflection.get_constructor(); +instance second_instance = + second_constructor("Second Instance"); +boost::reflections::function second_function = + second_reflection.get_function("get_type"); +std::cout << "Second reflection: " << second_function(second_instance) + << std::endl; +`` +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/factories.qbk b/libs/extension/doc/factories.qbk new file mode 100644 index 00000000..da3615b7 --- /dev/null +++ b/libs/extension/doc/factories.qbk @@ -0,0 +1,30 @@ +[/ Boost.Extension - factories doc ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:factories Factories] + +In normal usage, a `factory` is initialized in a shared library, then returned +to an application with a reference to a function that can be used to create +the given implementation of the `factory` interface. + +A factory has the following template options: + +`factory` + +Where `T` is the interface that it returns, and `ARGS` is a variable length +list of the argument types to the factory function. The maximum possible +length of the arglist is set by `BOOST_EXTENSION_MAX_FUNCTOR_PARAMS`, which +defaults to six. + +The functions available in `factory` are: +[h3 void set()] +The `set` function takes a template argument describing the class that will +be created by this factory, and that implements the `T` interface. This +function may be called multiple times, to change the factory function. +[h3 bool is_valid()] +If set has been called, this returns true. Otherwise, it returns false. +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/faq.qbk b/libs/extension/doc/faq.qbk new file mode 100644 index 00000000..8d55eac2 --- /dev/null +++ b/libs/extension/doc/faq.qbk @@ -0,0 +1,124 @@ +[/ Boost.Extension - faq ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:faq FAQ] +[section Factories] +[section What if I have multiple versions of the same interface (I've added new +member functions as they've become necessary), and I want to keep track of which +version is implemented by different plugins?] + +Answer: The simplest way is to change the name of your interface (or the +namespace it is in) each time its contents change. This is actually what I would +recommend. However, it is also possible to include such information as part +of the _factory_. A _factory_ can contain arbitrary information about the plugin +that it loads through the Info type. In the `hello_world` sample, the `Info` +type is an `int`, and the `hello` class has a number (it could be a version) of +1, and the `world` class has a version of 2. In the multiple_inheritance sample, +this type is a string. This will be detailed in greater detail in this +documentation. + +[endsect] +[section Do my factory classes need to be based on classes with only +pure virtual functions?] +No. The classes work just as well if they contain data or functions that +aren't pure virtual. Using virtual destructors in base classes is highly +recommended though. + +It is actually possible to define the implementation of a base class's +functions in a .cpp file. Check out [link boost_extension.tutorials.tutorial03 +Tutorial 3]. + +In general it can often be a good idea to use pure virtual methods, +just for simplicity, but it is not a requirement. +[endsect] +[endsect] +[section Shared Libraries] +[section How can I have shared libraries automatically close when the +_shared_library_ instance is destroyed?] +The paramter `auto_close` in the _shared_library_ constructor is set to false +by default. Set it to true. + +Note, however, that this is usually unnecessary, and can complicate your code. + +For instance, many types of objects, (std::map and std::set, for instance), can +refer to code inside a shared library if they are modified by code in that shared +library. If the program keeps using those objects after the shared library is +closed, the program may crash. +[endsect] +[section Is _shared_library_ type-safe?] +See [link boost_extension.type_safety Type Safety]. +[endsect] +[section Is there a portable way to support loading libraries from a memory buffer +instead of from the file system?] +Short answer: No, it can't be done easily. + +When a shared library is loaded, the executable code in memory is shared by +all processes that have loaded the shared library. It is also unloaded in +the way memory can be handled by a garbage collecter - some time after all +references to it have been removed. + +As such, the first process to load it is, in effect, buffering it into memory, +and other processes can then load it very cheaply. + +As far as loading it directly from a memory buffer, that would require copying +a number of routines from the platform-specific functions that fix pointer +references, function references, type information etc. within a shared library +when it is loaded (see +[link boost_extension.shared_libraries.shared_library_inefficiencies +Shared Library Inefficiences]). + +If you're sending shared libraries over a network connection between computers, +loading the shared library from a buffer might possibly be more optimal. But if +you're shooting to make your program that heavily optimized, I'd recommend +assembly language. + +So, yes, it is possible. No, I don't think it is worth attempting. +[endsect] +[section Do I need to use a separate _type_map_ for each shared library?] +If you close any shared libraries after you are done using them, they need +to have used their own _type_map_. This is because they may have caused allocations +to be done into the type_map with data referring to functions in the shared library. +If the shared library is closed, those functions will be inaccessible, and could +cause the program to crash. + +If you do not close any shared libraries, then you can share a single instance +of _type_map_. +[endsect] +[section What if I want to have a library close automatically when I've +destroyed all of the plugins in it?] + +I have not yet found a general enough way to do this that doesn't introduce too +many constraints for programmers. I recommend developing a specific solution for your +use case for now if you really need this. + +[endsect] +[section Does this library address the problems associated with calling conventions +when using different compilers? Can I compile an application with one +compiler and allow users to provide plugins compiled with another?] + +No, not really. And it's much worse than just the difference between +calling conventions. + +The issue is that C++ classes (and other things...) can be of different +formats when compiled by different compilers, or even different compiler +settings. For this reason, if you want to develop plugins that can be +compiled by different compilers safely, you should probably stick to +functions that pass around simple types (int, float, const char* etc.). + +Since one can't pass around classes safely, this means no reflection, +no factories, no shared objects etc. Since these are what Extension is +designed for, I'd recommend another solution. Though you can use the +shared_library class in Boost.Extension for this, you really won't get +the library's full benefit. + +What you could do is run the plugin in a separate process, and use +inter-process communication, and serialize data between them. Then +calling conventions wouldn't matter. It would certainly be a lot +more complicated though. +[endsect] +[endsect] +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/info.qbk b/libs/extension/doc/info.qbk new file mode 100644 index 00000000..237013da --- /dev/null +++ b/libs/extension/doc/info.qbk @@ -0,0 +1,177 @@ +[/ Boost.Extension - info class doc ] +[/ Copyright 2008 Marano G. Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:info Info Classes] + +In this section we explain how to use Info classes in Boost.Extension. + +Info classes is a mechanism that allows you to store important information about +each derived class. The type and utility of this information is arbitrary as +the Info class is a template argument of the factories where the user registers +its own implementations, and for that reason any kind of information could be +stored. + +Let's return to the first example - Hello World. Well, let's imagine that we +now want a version of Hello World that supports multiple languages. + +First, we define an implementation of the =word= interface for each combination +of word and language. Then we have for example the class =monde= that implements +the `word` interface for the french language in this case. + +Let's see the code: + +`` +class world : public word +{ +public: + virtual const char * get_val(){return "world!";} +}; + +class mundo : public word +{ +public: + virtual const char * get_val(){return "mundo!";} +}; + +class monde : public word +{ +public: + virtual const char * get_val(){return "monde!";} +}; + +class mondo : public word +{ +public: + virtual const char * get_val(){return "mondo!";} +}; + + +class hello : public word +{ +public: + virtual const char * get_val(){return "hello";} +}; + +class hola : public word +{ +public: + virtual const char * get_val(){return "hola";} +}; + +class bonjour : public word +{ +public: + virtual const char * get_val(){return "bonjour";} +}; + +class buonasera : public word +{ +public: + virtual const char * get_val(){return "buonasera";} +}; +`` + +Now we have the word 'hello' and 'world' implemented in several languages. + + +And then, how can we specify, given an implementation of =word= interface, in which language is the translation and +which is the original word (in a reference language, english in this case)? Well, as we want to store implementation +specific information we could use the Info class for this implementations. + +We define a new class called =word_description= where we can define the language and the original word. This will +be our Info class: + +`` +struct word_description +{ + std::string language; + std::string english_translation; + + word_description(std::string language, std::string english_translation) + : language(language), english_translation(english_translation) {} +}; +`` +We also need a less-than operator for this class, so it can be used as a map key. +`` +inline bool operator<(const word_description & first, + const word_description & second) { + return first.language < second.language || + (first.language == second.language && + first.english_translation < second.english_translation); +} +`` + +Finally, just store a =word_description= instance when adding the words to the factory map, describing the original +word and the language: + +`` +extern "C" void BOOST_EXTENSION_EXPORT_DECL +extension_export_multilanguage_word(boost::extensions::factory_map & fm) +{ + fm.get()[word_description("spanish", "hello")] + .set(); + fm.get()[word_description("spanish", "world!")] + .set(); + + fm.get()[word_description("french", "hello")] + .set(); + fm.get()[word_description("french", "world!")] + .set(); + fm.get()[word_description("italian", "hello")] + .set(); + fm.get()[word_description("italian", "world!")] + .set(); + fm.get()[word_description("english", "hello")] + .set(); + fm.get()[word_description("english", "world!")] + .set(); +} + +`` + +Now we could start using that information in the main code. Retrieving the `Info` +instance for a factory is simple, since it is part of the `std::pair` in the +map of factories. + +Let's see some code using the multilanguage hello world words: + +`` + for (std::map >::iterator current_word = + factory_list.begin(); current_word != factory_list.end(); + ++current_word) { + std::auto_ptr word_ptr(current_word->second.create()); + std::cout << word_ptr->get_val() << " is " + << current_word->first.english_translation + << " in " << current_word->first.language + << std::endl; + } + std::cout << std::endl; +`` + +And we get the following output: + +[table +[[[^hola is hello in spanish\n +mundo! is world! in spanish\n +bonjour is hello in french\n +monde! is world! in french\n +buonasera is hello in italian\n +mondo! is world! in italian\n +hello is hello in english\n +world! is world! in english\n]]]] + +Now that we have described our example the major parts could be summarized: + +# We just define our interface and implementations normally. +# We define the Info class. +# When we add the implementations to the factory_map we construct a new Info class instance +for each implementation, storing the needed data. +# Finally, we could use the =get_val= method in the main file to retrieve the data. + +Tutorial 5 describes a more complex example of the use of the Info class. + +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/introduction.qbk b/libs/extension/doc/introduction.qbk new file mode 100644 index 00000000..99f06cd8 --- /dev/null +++ b/libs/extension/doc/introduction.qbk @@ -0,0 +1,45 @@ +[/ Boost.Extension - intro ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:introduction Introduction] + +The Boost.Extension library has been developed to ease the development of +plugins and similar extensions to software using shared libraries. +Classes, functions and data can be made available from shared +libraries and loaded by the application. + +It consists of the following parts: + + +* Shared library handling +* Heterogeneous data containers +* Factories +* Reflection + + +Among others, the library has the following features: + + +* Multiple and virtual inheritance are supported for factories and reflections. +* Library users do not need to use any macros (besides those required by +Windows for exported functions). +* Only one external function required per shared library to export classes. +* RTTI is used by default, but it is possible to use user-defined type identification. +* Classes do not need to be modified to be loadable through factories or reflection. +* Constructors can be called even when the signature is unknown, for both +factories and reflection. +* Run-time compilation and loading of shared libraries. + + +This library is currently in development in preparation for a review for inclusion in the +Boost C++ Libraries. For ongoing status updates, check +[@http://boost-extension.blogspot.com/ C++ Plugins and Reflection]. + +The most up-to-date portions of this documentation are the header references. Other +sections of the documentation will be updated in the coming weeks. + +[endsect] diff --git a/libs/extension/doc/motivation.qbk b/libs/extension/doc/motivation.qbk new file mode 100644 index 00000000..1e276e38 --- /dev/null +++ b/libs/extension/doc/motivation.qbk @@ -0,0 +1,42 @@ +[/ Boost.Extension - Original Motivation ] +[/ Copyright 2008 Jeremy Pack] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:motivation Motivation] + +The original motivation for Boost.Extension was a moving-object-tracking application. +This application was to be used for both real-time and post-processing analysis +of a series of images. The types of cameras used for tracking were anything from +low-speed, low-resolution IR cameras, to medium-speed color camcorders to high-speed, +multi-spectral IR cameras. + +Each client or potential client of the application had different input types, and +different types of data they needed at the end. There were, in general, the following +major problems: + +* Some clients had proprietary image formats that needed to be input, or proprietary +algorithms they wished to use for the data. We could not give these algorithms to +other clients. +* Clients wanted to be able to adapt the software to work with new requirements, +such as a new camera type, in the future. To do this, one client in particular +wanted full ownership of the source code. +* Our code had become very entangled, and our application had become a mess of +menus covering the various bits of functionality. + +Initially, we just delivered different builds to each customer. This was tedious, +and only solved the first problem. + +For this reason, work was begun on using shared libraries which would each contain +a related set of modules. Each customer would be given two sets of files: + +* Shared libraries for their specific data types, UI needs, algorithms, etc. +* An SDK to develop plugins in the future to handle any new requirements for +the software. + +Boost.Extension is designed to make this type of solution simple, portable, +and easily extensible. + +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/old_tutorial1.qbk b/libs/extension/doc/old_tutorial1.qbk new file mode 100644 index 00000000..80f0ad52 --- /dev/null +++ b/libs/extension/doc/old_tutorial1.qbk @@ -0,0 +1,130 @@ +[/ Boost.Extension - first tutorial ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + + +[section:tutorial01 Tutorial 1] + +Create a class that we will later subclass and put it into a header file called +word.hpp. +`` +class word +{ +public: + virtual ~word(){} + virtual const char * get_val(){return "";} +}; +`` +This file will be included by both the shared library in which classes implement +this interface, and in the executable that will load these implementations. The +destructor ought to be virtual, just to ensure proper deletion. We create one +virtual function. We could actually add data to this class as well. + +Now create a file called hello_world.cpp. This will be compiled into a shared +library: + +`` +#include "word.hpp" +#include + +class world : public word +{ +public: + virtual const char * get_val(){return "world!";} +}; +class hello : public word +{ +public: + virtual const char * get_val(){return "hello";} +}; +`` +Now we have two classes that are derived from word, and that both implement the +virtual function get_val. All that's left is to declare them for exporting. +Normally, we just put one function in the entire shared library that exports +all exportable classes in the library. +`` +extern "C" void BOOST_EXTENSION_EXPORT_DECL +extension_export_word(boost::extensions::factory_map & fm) +{ + fm.get()[1].set(); + fm.get()[2].set(); +} +`` +`extern "C"` is required to keep the function name from being mangled. +`BOOST_EXTENSION_EXPORT_DECL` is only necessary on Windows - on other platforms +it does nothing. On Windows, it declares that the function should be exported. +This is the default in shared libraries on other platforms. + +The `factory_map::get` function takes two template arguments: the type of +factory to get (the interface returned by it), and what type of info is used +to identify it (which can be an arbitrary type). + +This returns a `std::map`, in this case, `std::map >`. +We then index the map to find (and perhaps create) the appropriate factory, +and set the type of class it instantiates to the template argument of the +`set` function. The argument is the value for that identification info. + +Now we can write the executable. + +`` +#include +#include +#include +#include +#include "word.hpp" +int main() +{ + using namespace boost::extensions; + // Create the factory_map object - it will hold all of the available + // constructors. Multiple factory_maps can be constructed. + factory_map fm; + // load the shared library with + load_single_library(fm, "libHelloWorldLib.extension", + "extension_export_word"); + // Get a reference to the list of constructors for words. + std::list > & factory_list = fm.get(); + if (factory_list.size() < 2) + std::cout << "Error - the classes were not found."; + for (std::list >::iterator current_word = + factory_list.begin(); current_word != factory_list.end(); + ++current_word) { + // Using auto_ptr to avoid needing delete. Using smart_ptrs is recommended. + // Note that this has a zero argument constructor - currently constructors + // with up to six arguments can be used. + std::auto_ptr word_ptr(current_word->create()); + std::cout << word_ptr->get_val() << " "; + } + std::cout << "\n"; + return 0; +} +`` +Now we just need to write the build file. You may have noticed above that the +name of the library is assumed to be libHelloWorld.extension - we'll need to +rename whatever shared library is compiled to that. Boost.Build can do this +automatically. + +`` +import type : change-generated-target-suffix ; +import type : change-generated-target-prefix ; +type.change-generated-target-suffix SHARED_LIB : : extension ; +type.change-generated-target-prefix SHARED_LIB : : lib ; +import os ; + +local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; +project + : requirements + ../../../ + $(BOOST_ROOT) + : + ; + +exe HelloWorld : main.cpp ; +lib HelloWorldLib : hello_world.cpp : shared ; +install ../bin : HelloWorld HelloWorldLib ; +`` +If you use another build system, you'll need to make sure that the output +shared library (dll, so, dylib etc.) is renamed correctly. +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/performance_analysis.qbk b/libs/extension/doc/performance_analysis.qbk new file mode 100644 index 00000000..21af1b70 --- /dev/null +++ b/libs/extension/doc/performance_analysis.qbk @@ -0,0 +1,92 @@ +[/ Boost.Extension - performance analysis ] +[/ Copyright 2008 Mariano G. Consoni and Jeremy Pack] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:performance_analysis Performance Analysis] + +In this section we will discuss the performance of the library. We'll focus +on the overhead added in comparison with [^ dlopen/dlclose] or +[^ LoadLibrary/CloseLibrary] approaches, describing three benchmarks +and their results. + +Finally we will discuss a small benchmark comparing two ways of using the +library, using the optional boost::function. + +Note that only minor optimization has been done of the code thus far. Once the API is finalized (ie after the library is reviewed for acceptance into Boost) more optimization will be performed. + +[h1 First comparison] + +The first benchmarks show how much overhead is incurred by using Boost.Extension +instead of just using standard shared library opening and calling functions. + +The extra work done by Boost.Extension is primarily for registering classes - this +benchmark assumes that without Boost.Extension, no classes are registered. + +Then we made a benchmark opening the same library with both approaches +and calling a method of the exported interface. This is done a lot of times in +order to be able to measure the times. This benchmark is implemented in +[^ benchmarks/plain_old_approach.cpp ]. + +For 1000 iterations results (in an AMD XP 2500) are: + +# [*Direct system calls]: 0.09 secs + +# [*Boost.Extension]: 0.33 secs + +So Boost.Extension is (aprox.) three times slower. + + +[h1 Multiple calls and libraries] + +Having a first idea about the comparison we advanced with two new benchmarks. + +The first opens a library one time and then calls a method of the implementation +multiple times. We wanted to see with this benchmark if the call overhead of +one individual method is different between both approaches. + +Results are very similar, they vary from run to run so the overhead is minimum. + +This benchmark is implemented in [^ benchmarks/multiple_class.hpp]. + +The second addresses a different issue. The overhead of opening a library. In order to +be measurable we had to open a lot of libraries (and then calls a method of each one). +To avoid generating a lot of code we just generated a library and copied it (using +[^ boost::filesystem]) with different names. We tested the code loading 500, 1.000 +and 2.000 libraries. + +Results (for 500 libraries): + +# [*Direct system calls]: 0.07 secs + +# [*Boost.Extension]: 0.21 secs + +The difference is similar to the first example + +We can conclude that the overhead is mainly in the library loading part not in the calls. +Moreover, we have done some profiling and discovered +that most of the overhead is spent in memory allocations caused by the use of +STL strings to identify the implementations. There are various ways that this overhead +can be eliminated. Strings are just the default type information stored, and this can +be replaced with other types of unique identifiers. On some platforms (current OS X, for instance), +this overhead can be avoided because of the way RTTI works across shared libraries. + +[h1 boost::function overhead] + +Besides the comparison of Boost.Extension vs. direct system calls we evaluated +different alternatives among features of the library. + +As our philosophy is to try to depend of the fewest libraries (including Boost +libraries) we present the option to use or not boost::function in Boost.Extension. + +In [^plain_old_approach_bf.cpp] we use our first benchmark to compare the usage +of boost::function in the implementation of Boost.Extension. + +Results are that overhead is not noticeable. Because of this, it is quite possible +that we will remove the non-Boost.Function option completely, once we've analyzed +how much of the rest of Boost would then be required for using Boost.Extension. + + +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/quick_start.qbk b/libs/extension/doc/quick_start.qbk new file mode 100644 index 00000000..a52d95d8 --- /dev/null +++ b/libs/extension/doc/quick_start.qbk @@ -0,0 +1,13 @@ +[/ Boost.Extension - quick start ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:quick_start Quick Start] + +For basic usage of the library, check out the tutorials + +[endsect] + diff --git a/libs/extension/doc/shared_libraries.qbk b/libs/extension/doc/shared_libraries.qbk new file mode 100644 index 00000000..32a252b3 --- /dev/null +++ b/libs/extension/doc/shared_libraries.qbk @@ -0,0 +1,97 @@ +[/ Boost.Extension - shared libraries ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:shared_libraries Shared Libraries] + +A _shared_library_ refers to a loadable module, the type of which depends +on the platform: + +* Windows: .dll +* Most Linux or Unix: .so +* Mac OS X: .dylib or .bundle + +Normally, shared libraries are loaded by an application when it starts. +This is commonly used by Boost libraries that can be built as shared libraries, +such as Boost.Thread or Boost.Filesystem. + +Shared libraries can also be loaded at will, during program execution. + +Whenever a shared library is loaded, either at load time or later during +a program's execution, the function and variable locations in the library +are resolved. + +[section:name_resolution Name Resolution] + +A loaded shared library can be searched for functions or data. In C++, +this requires that the function be marked as follows: + +\code extern "C" void MyFunction() { + // do stuff +} +\endcode + +The reason for this is the name-mangling that C++ performs, to give +different names to, for instance, multiple overloads with the same +function name. + +Because different compilers mangle names differently (and in ways +that are often undocumented), it would be very difficult to create +a cross-platform solution to avoid the use of extern "C" declarations. +This is the primary cause of the inherent lack of type-safety with +calls in the _shared_library_ class. + +[endsect] + +[section:shared_library_issues Other Issues with Shared Libraries] + +* Separate symbol tables are required in each shared library. +* RTTI merging: Some compilers do it. Some don't. +* Optimizations are harder because the code must be position +independent. +* The following may break binary compatibility between an application and +a shared library (among other things): + * Any part of shared class changes. Variable ordering, public/private/ +protected, virtual function implementations, add/remove variables/ +functions etc. + * Different compiler or compiler options used, or headers included +differently. + +[endsect] + +[section:shared_library_inefficiencies Possible Inefficiencies in Shared Libraries] + +* Position Independent Code: + * Since the shared library could be loaded to any part of the address space, it +cannot hard code addresses of its functions and data. This must be resolved +when the library is loaded. + * They can take a really long time to load. + * OS's attempt to mitigate this in various ways. +* Extra levels of indirection in non-virtual function calls. + * No inlining across library boundaries. + +[endsect] + +[section shared_library class] + +Provides for automatic loading of named modules, and retrieval of functions +in those modules: + +``` +shared_library m("my_module_name"); +// Call a function that returns an int and takes a float parameter. +int result = m.get("function_name")(5.0f); +m.close(); +``` + +The `get()` function returns a function +pointer that can be wrapped in Boost.Function objects or used by itself. + +Reference: _shared_library_ + +[endsect] + +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/tutorial1.qbk b/libs/extension/doc/tutorial1.qbk new file mode 100644 index 00000000..e4edee1f --- /dev/null +++ b/libs/extension/doc/tutorial1.qbk @@ -0,0 +1,207 @@ +[/ Boost.Extension - first tutorial ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + + +[section:tutorial01 Tutorial 1 - Using the `shared_library` class] + +The _shared_library_ class facilitates loading shared libraries +and calling functions from them. + +This tutorial will describe building a shared library with +a single exported function, and then creating an executable +that loads the shared library and calls the function. + +For purposes of this tutorial, Boost.Build will be used +as the build system. + +The options put into this build file are boilerplate that will +be reused for the other examples in this documentation. + +Shared libraries on different platforms can have different +prefixes and/or extensions, for example: + + +* .dll (Windows) +* .so (Unix/Linux) +* .dylib or .bundle (OS X) + + +In order to simplify loading of these libraries, it is common +to use a custom naming scheme. For these examples, all shared +libraries are prefixed with "lib" and end with ".extension", +but it is fine to use a different naming scheme. + +To achieve this in Boost.Build, add the following lines at the +top of the Jamfile: + + +`` +import type : change-generated-target-suffix ; +import type : change-generated-target-prefix ; +type.change-generated-target-suffix SHARED_LIB : : extension ; +type.change-generated-target-prefix SHARED_LIB : : lib ; +`` + +This example uses code from both the main Boost tree, and from +the Extension headers - which are currently located in the sandbox. +Make sure that BOOST_ROOT and BOOST_SANDBOX_ROOT are set to the locations +of these two trees - or use the related options for your compiler or IDE. + +Here, both of those directories are added as include paths. + +`` +import os ; + +local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; +local BOOST_SANDBOX_ROOT = [ os.environ BOOST_SANDBOX_ROOT ] ; +project + : requirements + $(BOOST_SANDBOX_ROOT) + $(BOOST_ROOT) + : + ; +`` + +This final section of the Jamfile compiles both the shared library +and main executable, and installs them into the binaries/ subdirectory. + +It is usually simplest to place all shared libraries used by an application +in a common directory. + + +`` +lib tutorial_1 : tutorial_1/hello_world.cpp : shared ; +exe tutorial_1_bin : tutorial_1/main.cpp ; + +install binaries : + tutorial_1 tutorial_1_bin + ; +`` + + +The hello_world.cpp file is the source of the shared library. It consists +of a single exported function which prints out a message a number of times, +according to the `repetitions` parameter to the function. + +The header is included for `std::cout`. +The header is used for the +`BOOST_EXTENSION_EXPORT_DECL` macro. + +`` +#include +#include +`` + +In C, each function name in a compilation unit is unique. In C++, however, +function overloading is permitted - as are various types of templated functions, +namespaces etc. Because of this, C++ function names are mangled for linking. In +order to find a function in a shared library, its name must be known. This leaves +two options: + + +* Determine the mangled name. +* Declare that the function has C linkage, using the `extern "C"` declaration. + + +Extension uses the second approach. Though this eases portability, it also poses +a risk to type safety. A simple, cross-platform solution to this problem would +probably require direction from the C++ Standards Committee. Until that time, +the [link boost_extension.type_safety Type Safety] section has advice on avoding type safety issues +when using the _shared_library_ class. + +`` +// Any exported function or variable must be declared +// extern "C" to avoid C++ name mangling. +extern "C" + +`` + +Compilers have a number of options for exporting functions, variables +and type information when a shared library is loaded. For some compilers, or +with certain compiler settings, functions are not exported by default. Using +the `BOOST_EXTENSION_EXPORT_DECL macro, declared in extension.hpp, puts any +necessary export declarations in the function definition. + +`` +// Depending on the compiler and settings, +// it may be necessary to add a specific export +// declaration. The BOOST_EXTENSION_EXPORT_DECL +// adds this if necessary. +void BOOST_EXTENSION_EXPORT_DECL +boost_extension_hello_world(int repetitions) { + for (int i = 0; i < repetitions; ++i) { + std::cout << "Hello World" << std::endl; + } +} +`` + +Now that the shared library contents are set, the main executable needs +to be written to load it, and find the boost_extension_hello_world function. + +* The `` header is included for error output. +* The `` header is included for the +_shared_library_ class. +* The `` header is included for boost::function. Though +ordinary function pointers can be used with the _shared_library_ class, Boost.Function +provides a more straightforward interface. + +`` +#include +#include +#include +`` + +Create a _shared_library_ object, and open it. + +`` +int main() { + using namespace boost::extensions; + + // In the Jamfile, shared libraries are set to have the same + // prefix and extension, even on different operating systems. + // This is for convenience in writing cross-platform code, but + // is not required. All shared libraries are set to start with + // "lib" and end with "extension". + std::string library_path = "libtutorial_1.extension"; + + // Create shared_library object with the relative or absolute + // path to the shared library. + shared_library lib(library_path); + + // Attempt to open the shared library. + if (!lib.open()) { + std::cerr << "Library failed to open: " << library_path << std::endl; + return 1; + } +`` + +If it opened successfully, find a call the `boost_extension_hello_world` function. + +`` + // Retrieve a function from the library, and store it in a Boost.Function + // object. It is also possible to use function pointers, but the syntax + // for Boost.Function is easier to understand. This retrieves a function + // called "boost_extension_hello_world" with a void return type and a single + // parameter of type int. + boost::function + f(lib.get("boost_extension_hello_world")); + + // Check that the function was found. + if (!f) { + std::cerr << "Function not found!" << std::endl; + return 1; + } + + // Call the function from the shared library with + // an integer parameter. + f(4); +} +`` + +This will print out "Hello World" four times. + +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/tutorial2.qbk b/libs/extension/doc/tutorial2.qbk new file mode 100644 index 00000000..8179d0d6 --- /dev/null +++ b/libs/extension/doc/tutorial2.qbk @@ -0,0 +1,212 @@ +[/ Boost.Extension - second tutorial ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:tutorial02 Tutorial 2 - Basic Factories] + +The easiest method for creating plugins in C++ is to use factories. +Consider the following base class: + +`` +class animal { +public: + animal(int age) : age_(age) { + } + virtual ~animal(void) { + } + virtual std::string get_name(void) { + return "A generic animal"; + } + int get_age(void) { + return age_; + } +protected: + int age_; +}; +`` + +The goal here is to create a main executable which creates and uses +instances of the `animal` class. A shared library will then be created +which includes classes derived from the `animal` class, which the main +executable will also be able to load and use as `animal`s. + +The main executable will use a number of Extension and other Boost headers: + +`` + +#include +#include + +#include +#include +#include +#include +#include +`` + +It will also use the `animal` header, which will also be used by the +shared library: + +`` +#include "animal.hpp" +`` + +The main function is similar to that from +[link boost_extension.tutorials.tutorial01 Tutorial 1]. + +`` +int main() { + using namespace boost::extensions; + std::string library_path = "libtutorial_2.extension"; + + // Create shared_library object with the relative or absolute + // path to the shared library. + shared_library lib(library_path); + + // Attempt to open the shared library. + if (!lib.open()) { + std::cerr << "Library failed to open: " << library_path << std::endl; + return 1; + } +`` + +Here though, instead of using the _shared_library_get_ function, we use the +_shared_library_call_ function. It expects a function with the following signature: + +`` +extern "C" void boost_extension_exported_type_map_function + (boost::extensions::type_map& types); +`` + +A _type_map_ is a generic container with items indexed by type - only one +item of each type can be contained in it. To retrieve (or add) an integer +to it, do the following: + +`` +int& my_int(my_type_map.get()); +`` + +For the rationale of the _type_map_ class, see the discussion on +[link boost_extension.type_safety type safety]. + +`` + + // Use the shared_library::call to automatically call an + // Extension-specific function in the shared library, + // which takes a type_map. + type_map types; + if (!lib.call(types)) { + std::cerr << "Function not found!" << std::endl; + return 1; + } +`` + +Now, a `std::map` of factories, indexed by `std::string` is retrieved from +the _type_map_ that was sent to the shared library. + +The _factory_ is declared as `factory` meaning it returns pointers +to instances of the `animal` class, and that it is used for constructors taking +integer arguments. + +`` + // Retrieve a map of all animal factories taking an int and indexed + // by a string from the type_map. + std::map >& factories(types.get()); + if (factories.empty()) { + std::cerr << "Animals not found!" << std::endl; + return 1; + } +`` + +Now, iterate through the factory, creating each available `animal`. + +`` + // Create each animal. + int age = 1; + for (std::map >::iterator it + = factories.begin(); + it != factories.end(); ++it) { + ++age; + std::cout << "Creating an animal using factory: " << it->first << std::endl; + boost::scoped_ptr current_animal(it->second.create(age)); + std::cout << "Created an animal: " << current_animal->get_name() + << " Age: " << current_animal->get_age() << std::endl; + } +} +`` + +Now, the shared library will define a number of animals. It will +use the following headers: + +`` +#include +#include + +#include +#include +#include + +#include "animal.hpp" +`` + +The following animals will be exported: + +`` +class puma : public animal { + public: + puma(int age) : animal(age) {} + virtual std::string get_name() { + return "puma"; + } +}; + +class leopard : public animal { + public: + leopard(int age) : animal(age) {} + virtual std::string get_name() { + return "leopard"; + } +}; + +class wildcat : public animal { + public: + wildcat(int age) : animal(age) {} + virtual std::string get_name() { + return "wildcat"; + } +}; + +class cougar : public animal { + public: + cougar(int age) : animal(age) {} + virtual std::string get_name() { + return "cougar"; + } +}; +`` + +The `BOOST_EXTENSION_TYPE_MAP_FUNCTION` declaration can be used in place +of a function declaration to automatically insert the function definition +referred to above, that takes a _type_map_. See `` +for more details. + +`` +BOOST_EXTENSION_TYPE_MAP_FUNCTION { + using namespace boost::extensions; + std::map >& + animal_factories(types.get()); + animal_factories["Puma factory"].set(); + animal_factories["Leopard factory"].set(); + animal_factories["Wildcat factory"].set(); + animal_factories["Cougar factory"].set(); +} +`` + +Now, compile the shared library and main executable. Then run the main executable +in the directory with the compiled library. If all is successful, a message will +be printed out for each animal. + +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/tutorial3.qbk b/libs/extension/doc/tutorial3.qbk new file mode 100644 index 00000000..df5067d0 --- /dev/null +++ b/libs/extension/doc/tutorial3.qbk @@ -0,0 +1,189 @@ +[/ Boost.Extension - second tutorial ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:tutorial03 Tutorial 3 - Multiple and Implementation Inheritance] + +This tutorial shows more advanced class loading. The example described here is +contrived, and such a convoluted design is not used as an example of good +object-oriented program, but as a way to illustrate some of the possibilities +with this library. + +Most complications with this type of factory usage only occur on Windows, or with +less popular compiler options. Refer to +[link boost_extension.appendices.appendix_a Appendix A]. + +Let's design a class hierarchy based on the following: + + +* There is a vehicle class +* There is a computer class +* A car is a vehicle +* A boat is a vehicle +* A plane is a vehicle +* A flying_car is a plane and a car +* A car_of_the_future is a flying_car, a boat, and a computer. + + +In addition, we are not going to have any of these classes be interfaces. They +will each have an implementation in a .cpp file. This will require multiple +inheritance, as well as virtual inheritance (because otherwise a +car_of_the_future would consist of three separate vehicle base classes!). + + +In addition, each class will be compiled into a separate shared library. This +is not necessary, certainly, but will serve to illustrate some of the more +advanced capabilities of the library, as well as some of the techniques +necessary in this situation. + + +To begin with, let's look at the Jamfile: + + +`` +import type : change-generated-target-suffix ; +import type : change-generated-target-prefix ; +type.change-generated-target-suffix SHARED_LIB : : extension ; +type.change-generated-target-prefix SHARED_LIB : : lib ; +`` + +As usual, first we must rename the generated libraries (this is required for +cross-platform use - but the prefix and suffix are arbitrary). + +Next, some special options are needed for certain compilers, to make sure +that the shared libraries are found correctly. This is only needed for this +example because we have shared libraries that are linked by the linker after +compilation to other shared libraries, rather than loaded by the shared_library +class at runtime. Library paths and includes need to be set as follows: + +`` +local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; +project + : requirements + ../../../ + $(BOOST_ROOT) + gcc:dl + gcc:"-Wl,-rpath,'$ORIGIN'" + darwin:DYLD_LIBRARY_PATH=./ + : + ; + +`` + +The rules for building the first two libraries are the same +as those from earlier tutorials. + +`` +lib Vehicle : multiple_inheritance/vehicle.cpp : shared ; +lib Car : multiple_inheritance/car.cpp Vehicle : shared ; +`` + + +Notice here that the Car library is linked to the Vehicle library - this is +necessary because the Car class needs the implementation of the Vehicle class. +This is not specific to the Boost.Extension library, but just a standard +requirement when inheriting from shared library classes. The same will be done +with the other shared libraries. + + +`` +lib Plane : multiple_inheritance/plane.cpp Vehicle : shared ; +lib Boat : multiple_inheritance/boat.cpp Vehicle : shared ; +lib Computer : multiple_inheritance/computer.cpp : shared ; +lib FlyingCar : multiple_inheritance/flying_car.cpp Plane Car Vehicle + : shared ; +lib CarOfTheFuture : + multiple_inheritance/car_of_the_future.cpp + Plane Car Vehicle + FlyingCar Computer Boat + : + shared +; + +install ../bin : + HelloWorld HelloWorldLib Vehicle Boat FlyingCar + CarOfTheFuture MultipleInheritance + : + ; +`` + + +Refer to the examples/multiple_inheritance folder for the source code of these +classes. Only the most complex will be described here - the rest are similar. + + +On the Windows platform, there are special declarations that are required when +a dll must use a class that is defined in another dll. For interface only +classes this is unnecessary, and is not needed on other platforms. The macros +BOOST_EXTENSION_IMPORT_DECL and BOOST_EXTENSION_EXPORT_DECL can be used +to insert the proper declaration. This is detailed in +[link boost_extension.appendices.appendix_a Appendix A]. + +The macros here are defined in such a way that in the `car_of_the_future` +class, the classes it depends on from other shared libraries are +declared as imports. The BOOST_EXTENSION_CAR_OF_THE_FUTURE_DECL must be +defined by any source file using this class - as `BOOST_EXTENSION_IMPORT_DECL` +by the shared library containing the `car_of_the_future`, and as +`BOOST_EXTENSION_EXPORT_DECL` by any shared library or executable linking +directly to that shared library. + +`` +#define BOOST_EXTENSION_FLYING_CAR_DECL BOOST_EXTENSION_IMPORT_DECL +#define BOOST_EXTENSION_BOAT_DECL BOOST_EXTENSION_IMPORT_DECL +#define BOOST_EXTENSION_COMPUTER_DECL BOOST_EXTENSION_IMPORT_DECL +#include "flying_car.hpp" +#include "boat.hpp" +#include "computer.hpp" +#include +#include +class BOOST_EXTENSION_CAR_OF_THE_FUTURE_DECL + car_of_the_future : public flying_car, public boat, public computer +{ +public: + car_of_the_future(void){std::cout << "\nCreated a Car of the Future";} + ~car_of_the_future(void){std::cout << "\nDestroyed a Car of the Future";} + virtual std::string list_capabilities(void); +}; +`` + +In the implementation file for the `car_of_the_future`, it is exported +as an implementation of each of its base classes. + +`` +std::string car_of_the_future::list_capabilities() { + return boat::list_capabilities() + flying_car::list_capabilities() + + computer::list_capabilities() + "\nCosts an arm and a leg"; +} + +using boost::extensions::factory; + +BOOST_EXTENSION_TYPE_MAP_FUNCTION { + types.get > >() + ["A car of the future exported as a vehicle"].set(); + types.get > >() + ["A car of the future exported as a car"].set(); + types.get > >() + ["A car of the future exported as a plane"].set(); + types.get > >() + ["A car of the future exported as a flying car"].set(); + types.get > >() + ["A car of the future exported as a boat"].set(); + types.get > >() + ["A car of the future exported as a computer"].set(); + types.get > >() + ["A car of the future exported as a car of the future"].set(); +} +`` + +The main executable is relatively straightforward, but does introduce a new +function: `load_single_library`, which takes a _type_map_ and the location +of the shared library, and loads the library and calls the function declared +as `BOOST_EXTENSION_TYPE_MAP_FUNCTION`. + +Now, place all of the compiled libraries and the main executable in a directory together, +and run the executable. + +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/tutorial4.qbk b/libs/extension/doc/tutorial4.qbk new file mode 100644 index 00000000..c466a12d --- /dev/null +++ b/libs/extension/doc/tutorial4.qbk @@ -0,0 +1,95 @@ +[/ Boost.Extension - fourth tutorial ] +[/ Copyright 2008 Mariano G. Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:tutorial04 Tutorial 4 - Multi-type Containers] +There are two types of containers in the library that can hold objects +of heterogeneous types. The _type_map_ is designed for use with +_shared_library_ so that a standard function signature, taking a +_type_map_, can be provided by each shared library, helping to avoid +type safety problems. + +The second type, _parameter_map_, is designed for use with reflections +or factories where the type of the function is not known at compile time. +A _parameter_map_ can be passed into such a factory or reflection, and +then searched for needed parameters, and return a list of missing parameters +if necessary. + +The _type_map_ is the simplest of the two, as it can hold exactly one +element of each type. It can hold multiple objects, but they will each +be of a different type. + +Here's an example showing a _type_map_ holding an `int` and a +`std::map`. + +`` +using namespace boost::extensions; + +// A type_map can hold one of each type, constructed +// as needed. +type_map types; +int& first_int(types.get()); +first_int = 100; + +// This actually points to the same int as first_int. +int& second_int(types.get()); + +second_int = 500; + +// This will print out 500. +std::cout << "first_int: " << first_int << std::endl; + +// Arbitrary default-constructible types can be held in a type_map. +std::map& string_pairs(types.get()); +`` + +Note that pulling an integer out of it twice results in two references +to the same integer. The same occurs with any type placed into the _type_map_. + +A _parameter_map_ on the other hand, can hold any number of elements of any +type, and can automatically convert objects to compatible types. + +The basic type stored in a _parameter_map_ is a _parameter_. It is possible +to declare a _parameter_ as being convertible to other types. + +By default, a `static_cast` is used to convert the types, but any conversion +function can be provided. +`` +parameter* p = new parameter(5); +p->converts_to(); +p->converts_to(); +`` + +To have a float change to its ceiling when converted to +an integer, one could write: + +`` +void FloatCeilingToInt(float* f, int* i) { + *i = static_cast(std::ceil(*f)); +} +`` +`` +parameter* p = new parameter(4.9f); +p->converts_to_with_func(&FloatCeilingToInt); +p->cast(); // returns 5 +`` + +Once created, a _parameter_ can be placed into a _parameter_map_, +along with some sort of name. This name can be of arbitrary type: +_parameter_map_ is a specialized _basic_parameter_map_ with the +std::string type as the name, but other types are also possible. + +A _parameter_map_ works much like a `std::multi_map` - since it is built +on one. It has a few specialty methods though. It is possible, for instance, +to retrieve all values that match a certain type and name, or the first +value that matches. +`` +parameter_map map; +std::vector*> int_vector(map.get("some_int_name")); +generic_parameter* first_int(map.get_first("some_int_name")); +`` + +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/tutorial5.qbk b/libs/extension/doc/tutorial5.qbk new file mode 100644 index 00000000..89fac724 --- /dev/null +++ b/libs/extension/doc/tutorial5.qbk @@ -0,0 +1,22 @@ +[/ Boost.Extension - fifth tutorial ] +[/ Copyright 2008 Mariano G. Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:tutorial05 Tutorial 5 - Adaptable Factories] + +In some cases, it may be desirable to construct an object that depends on +other objects having been created, but their existence is not known at +compile time. + +In other cases, it may be desirable to allow a constructor to pick and +choose its parameters from a set of parameters. + +This is, in some ways, a simple sort of reflection (See _reflection_ and +_simple_reflection_ for actual reflection) for the parameters of constructors. + +The _adaptable_factory_ class provides this functionality. + +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/tutorial6.qbk b/libs/extension/doc/tutorial6.qbk new file mode 100644 index 00000000..ba89c376 --- /dev/null +++ b/libs/extension/doc/tutorial6.qbk @@ -0,0 +1,12 @@ +[/ Boost.Extension - sixth tutorial ] +[/ Copyright 2008 Mariano G. Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:tutorial06 Tutorial 6 - Basic Reflection] + + + +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/tutorials.qbk b/libs/extension/doc/tutorials.qbk new file mode 100644 index 00000000..a1937f3b --- /dev/null +++ b/libs/extension/doc/tutorials.qbk @@ -0,0 +1,14 @@ +[/ Boost.Extension - tutorials ] +[/ Copyright 2008 Mariano G. Consoni and Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section Tutorials] +[include tutorial1.qbk] +[include tutorial2.qbk] +[include tutorial3.qbk] +[include tutorial4.qbk] + +[endsect] \ No newline at end of file diff --git a/libs/extension/doc/type_safety.qbk b/libs/extension/doc/type_safety.qbk new file mode 100644 index 00000000..e168ae14 --- /dev/null +++ b/libs/extension/doc/type_safety.qbk @@ -0,0 +1,38 @@ +[/ Boost.Extension - Type Safety ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + + +[section:type_safety Type Safety of Boost.Extension] + +Type-safe classes in Boost.Extension: + +* _factory_ +* _adaptable_factory_ +* _type_map_ +* _parameter_map_ + +Other classes: + +* _reflection_, _instance_: An _instance_ must be used only with the _reflection_ from +which it was created. Other than this, these two classes are type safe. +* _shared_library_: The get functions for this class do not guarantee type safety. This +is caused by the fact that the underlying operating system-specific functions return void +pointers that must be converted to function pointers. + +To help alleviate type safety issues when using _shared_library_, it is recommended +that a common function signature be used in each shared library. The library provides +shortcuts for this technique, which are used in +[link boost_extension.tutorials.tutorial02]. + +To help overcome the type safety problems with the _instance_ class, it would be +possible to have each _instance_ contain a reference to the _reflection_ that +created it, and then only allow that functions from that _reflection_ to be +called. The plan is to add this as an option, which can be disabled with +a preprocessor macro, but it may be added permanently, depending on input +from the Boost community. + +[endsect] \ No newline at end of file diff --git a/libs/extension/examples/Jamfile.v2 b/libs/extension/examples/Jamfile.v2 new file mode 100644 index 00000000..6684128a --- /dev/null +++ b/libs/extension/examples/Jamfile.v2 @@ -0,0 +1,76 @@ +# Boost.Extension - examples Jamfile +# +# Copyright 2007 Jeremy Pack +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# +# See http://www.boost.org/ for latest version. +# + +import type : change-generated-target-suffix ; +import type : change-generated-target-prefix ; +type.change-generated-target-suffix SHARED_LIB : : extension ; +type.change-generated-target-prefix SHARED_LIB : : lib ; +import os ; + +project + : requirements + ../../../ + $(BOOST_ROOT) + gcc:dl + gcc:"-Wl,-rpath,'$ORIGIN'" + darwin:DYLD_LIBRARY_PATH=./ + : + ; + +exe HelloWorldVersions : versioning/main_versions.cpp ; +lib HelloWorldLibv2 : versioning/hello_world_versions.cpp : shared ; +lib SaluteLib : versioning/salute.cpp : shared ; + +exe Parameters : parameters/main_lp.cpp ; +lib ParametersLib : parameters/lots_of_parameters.cpp : shared ; + +exe MultilanguageWord : info/multilanguage_main.cpp ; +lib MultilanguageHelloWorld : info/multilanguage_hello_world.cpp : shared ; + +exe IM : info/im/im_main.cpp ; +lib IMPlugins : info/im/plugins.cpp : shared ; + +lib Vehicle : multiple_inheritance/vehicle.cpp : shared ; +lib Car : multiple_inheritance/car.cpp Vehicle : shared ; +lib Plane : multiple_inheritance/plane.cpp Vehicle : shared ; +lib Boat : multiple_inheritance/boat.cpp Vehicle : shared ; +lib Computer : multiple_inheritance/computer.cpp : shared ; +lib FlyingCar : multiple_inheritance/flying_car.cpp Plane Car Vehicle : shared ; +lib CarOfTheFuture : + multiple_inheritance/car_of_the_future.cpp + Plane Car Vehicle + FlyingCar Computer Boat + : + shared +; +exe MultipleInheritance : multiple_inheritance/main_mi.cpp Computer Vehicle ; + +lib tutorial_1 : tutorial_1/hello_world.cpp : shared ; +exe tutorial_1_bin : tutorial_1/main.cpp ; + +lib tutorial_2 : tutorial_2/factories_lib.cpp : shared ; +exe tutorial_2_bin : tutorial_2/factories_main.cpp ; +exe tutorial_5_bin : tutorial_5/adaptable.cpp ; + +install ../test/ : + tutorial_1 tutorial_1_bin + tutorial_2 tutorial_2_bin + Parameters ParametersLib + MultilanguageWord MultilanguageHelloWorld + IM IMPlugins + Vehicle Boat FlyingCar Car Plane + CarOfTheFuture MultipleInheritance + Computer + : + on + EXE + SHARED_LIB + LIB + ; diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/HelloWorldVersions b/libs/extension/examples/bin/gcc-4.4.3/debug/HelloWorldVersions new file mode 100755 index 00000000..16721a78 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/HelloWorldVersions differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/IM b/libs/extension/examples/bin/gcc-4.4.3/debug/IM new file mode 100755 index 00000000..6c4d587c Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/IM differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/MultilanguageWord b/libs/extension/examples/bin/gcc-4.4.3/debug/MultilanguageWord new file mode 100755 index 00000000..f5cc57b2 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/MultilanguageWord differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/MultipleInheritance b/libs/extension/examples/bin/gcc-4.4.3/debug/MultipleInheritance new file mode 100755 index 00000000..424cb981 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/MultipleInheritance differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/Parameters b/libs/extension/examples/bin/gcc-4.4.3/debug/Parameters new file mode 100755 index 00000000..87361977 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/Parameters differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/info/im/im_main.o b/libs/extension/examples/bin/gcc-4.4.3/debug/info/im/im_main.o new file mode 100644 index 00000000..61d44e2f Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/info/im/im_main.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/info/im/plugins.o b/libs/extension/examples/bin/gcc-4.4.3/debug/info/im/plugins.o new file mode 100644 index 00000000..7671cf86 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/info/im/plugins.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/info/multilanguage_hello_world.o b/libs/extension/examples/bin/gcc-4.4.3/debug/info/multilanguage_hello_world.o new file mode 100644 index 00000000..fa336560 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/info/multilanguage_hello_world.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/info/multilanguage_main.o b/libs/extension/examples/bin/gcc-4.4.3/debug/info/multilanguage_main.o new file mode 100644 index 00000000..92e83570 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/info/multilanguage_main.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libBoat.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libBoat.extension new file mode 100755 index 00000000..53b72aa7 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libBoat.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libCar.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libCar.extension new file mode 100755 index 00000000..73f3e6bd Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libCar.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libCarOfTheFuture.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libCarOfTheFuture.extension new file mode 100755 index 00000000..3deaa636 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libCarOfTheFuture.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libComputer.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libComputer.extension new file mode 100755 index 00000000..418e7082 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libComputer.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libFlyingCar.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libFlyingCar.extension new file mode 100755 index 00000000..ee535f51 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libFlyingCar.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libHelloWorldLibv2.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libHelloWorldLibv2.extension new file mode 100755 index 00000000..225562f6 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libHelloWorldLibv2.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libIMPlugins.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libIMPlugins.extension new file mode 100755 index 00000000..b2965d8b Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libIMPlugins.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libMultilanguageHelloWorld.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libMultilanguageHelloWorld.extension new file mode 100755 index 00000000..565d9d05 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libMultilanguageHelloWorld.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libParametersLib.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libParametersLib.extension new file mode 100755 index 00000000..1893ec41 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libParametersLib.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libPlane.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libPlane.extension new file mode 100755 index 00000000..8f199a26 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libPlane.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libSaluteLib.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libSaluteLib.extension new file mode 100755 index 00000000..df9f16d2 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libSaluteLib.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libVehicle.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libVehicle.extension new file mode 100755 index 00000000..3d6b4646 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libVehicle.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libtutorial_1.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libtutorial_1.extension new file mode 100755 index 00000000..0517c2e7 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libtutorial_1.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/libtutorial_2.extension b/libs/extension/examples/bin/gcc-4.4.3/debug/libtutorial_2.extension new file mode 100755 index 00000000..bfd4e099 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/libtutorial_2.extension differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/boat.o b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/boat.o new file mode 100644 index 00000000..8f3ec810 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/boat.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/car.o b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/car.o new file mode 100644 index 00000000..248d5920 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/car.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/car_of_the_future.o b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/car_of_the_future.o new file mode 100644 index 00000000..106b6d8b Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/car_of_the_future.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/computer.o b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/computer.o new file mode 100644 index 00000000..6a2cb628 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/computer.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/flying_car.o b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/flying_car.o new file mode 100644 index 00000000..e90e7f8d Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/flying_car.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/main_mi.o b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/main_mi.o new file mode 100644 index 00000000..0635eccd Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/main_mi.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/plane.o b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/plane.o new file mode 100644 index 00000000..a98b0de9 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/plane.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/vehicle.o b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/vehicle.o new file mode 100644 index 00000000..284eff08 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/multiple_inheritance/vehicle.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/parameters/lots_of_parameters.o b/libs/extension/examples/bin/gcc-4.4.3/debug/parameters/lots_of_parameters.o new file mode 100644 index 00000000..595b3ad0 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/parameters/lots_of_parameters.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/parameters/main_lp.o b/libs/extension/examples/bin/gcc-4.4.3/debug/parameters/main_lp.o new file mode 100644 index 00000000..02d806e9 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/parameters/main_lp.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_1/hello_world.o b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_1/hello_world.o new file mode 100644 index 00000000..6a828de3 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_1/hello_world.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_1/main.o b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_1/main.o new file mode 100644 index 00000000..ad97d07f Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_1/main.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_1_bin b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_1_bin new file mode 100755 index 00000000..1be42b33 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_1_bin differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_2/factories_lib.o b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_2/factories_lib.o new file mode 100644 index 00000000..521ade69 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_2/factories_lib.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_2/factories_main.o b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_2/factories_main.o new file mode 100644 index 00000000..72fada57 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_2/factories_main.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_2_bin b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_2_bin new file mode 100755 index 00000000..c556ddbc Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_2_bin differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_5/adaptable.o b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_5/adaptable.o new file mode 100644 index 00000000..4801b7a4 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_5/adaptable.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_5_bin b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_5_bin new file mode 100755 index 00000000..b401c35e Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/tutorial_5_bin differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/versioning/hello_world_versions.o b/libs/extension/examples/bin/gcc-4.4.3/debug/versioning/hello_world_versions.o new file mode 100644 index 00000000..a0206922 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/versioning/hello_world_versions.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/versioning/main_versions.o b/libs/extension/examples/bin/gcc-4.4.3/debug/versioning/main_versions.o new file mode 100644 index 00000000..a70da982 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/versioning/main_versions.o differ diff --git a/libs/extension/examples/bin/gcc-4.4.3/debug/versioning/salute.o b/libs/extension/examples/bin/gcc-4.4.3/debug/versioning/salute.o new file mode 100644 index 00000000..3cd458a6 Binary files /dev/null and b/libs/extension/examples/bin/gcc-4.4.3/debug/versioning/salute.o differ diff --git a/libs/extension/examples/hello_world.cpp b/libs/extension/examples/hello_world.cpp new file mode 100644 index 00000000..a0e5701a --- /dev/null +++ b/libs/extension/examples/hello_world.cpp @@ -0,0 +1,32 @@ +/* + * Boost.Extension / hello world implementations + * + * (C) Copyright Jeremy Pack 2007-2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include "word.hpp" +#include +#include + +class world : public word +{ +public: + virtual const char * get_val(){return "world!";} +}; +class hello : public word +{ +public: + virtual const char * get_val(){return "hello";} +}; +extern "C" +void BOOST_EXTENSION_EXPORT_DECL +extension_export_word(boost::extensions::factory_map & fm) +{ + fm.get()[1].set(); + fm.get()[2].set(); +} diff --git a/libs/extension/examples/info/im/im_main.cpp b/libs/extension/examples/info/im/im_main.cpp new file mode 100644 index 00000000..999177fe --- /dev/null +++ b/libs/extension/examples/info/im/im_main.cpp @@ -0,0 +1,95 @@ +/* + * Boost.Extension / instant messaging main + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include +#include +#include + +#include + +#include "protocol.hpp" +#include "network_parameters.hpp" + + +int main(void) +{ + using namespace boost::extensions; + + // create the factory_map object - it will hold all of the available + // constructors. Multiple factory_maps can be constructed. + factory_map fm; + + // load the shared library with + load_single_library(fm, "libIMPlugins.extension", + "extension_export_plugins"); + + // get a reference to the list of constructors for protocols + std::map, factory > & + factory_list = fm.get >(); + + if (factory_list.size() < 2) { + std::cout << "Error - the classes were not found."; + return 1; + } + + std::map, factory > + ::iterator current_plugin = factory_list.begin(); + + // MSN plugin + std::auto_ptr MSN_ptr(current_plugin->second.create()); + boost::shared_ptr msn_parameters = + current_plugin->first; + current_plugin++; + + // Jabber plugin + std::auto_ptr Jabber_ptr(current_plugin->second.create()); + boost::shared_ptr jabber_parameters = + current_plugin->first; + + // server + std::cout << "MSN hostname: " << msn_parameters->hostname() << std::endl; + std::cout << "Jabber hostname: " << jabber_parameters->hostname() + << std::endl; + std::cout << std::endl; + + // http_mode: note that one of the implementations doesn't support it, + // having a base class + // and different specific concrete network parameters allow us to handle this + std::cout << "MSN: "; + msn_parameters->set_http_mode(); + std::cout << "Jabber: "; + jabber_parameters->set_http_mode(); + std::cout << std::endl; + + // login + MSN_ptr->login("testuser", "testpass"); + Jabber_ptr->login("testuser", "testpass"); + std::cout << std::endl; + + // send message + MSN_ptr->send("hi"); + Jabber_ptr->send("hi"); + std::cout << std::endl; + + // change status + MSN_ptr->change_status("away"); + Jabber_ptr->change_status("away"); + std::cout << std::endl; + + // wait for message + std::cout << MSN_ptr->receive() << std::endl; + std::cout << Jabber_ptr->receive() << std::endl; + std::cout << std::endl; + + return 0; +} diff --git a/libs/extension/examples/info/im/network_parameters.hpp b/libs/extension/examples/info/im/network_parameters.hpp new file mode 100644 index 00000000..c0683b79 --- /dev/null +++ b/libs/extension/examples/info/im/network_parameters.hpp @@ -0,0 +1,66 @@ +/* + * Boost.Extension / network parameters (info class) + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include +#include + +// interface for the parameters of each plugin +class network_parameters +{ + public: + virtual const char * hostname(void) const = 0; + virtual const char * port(void) const = 0; + virtual void set_http_mode(void) = 0; + + virtual ~network_parameters(void) {}; +}; + + +// MSN implementation +class MSN_network_parameters : public network_parameters +{ + public: + virtual const char * hostname(void) const { return "msn.messenger.com"; } + virtual const char * port(void) const { return "1863"; } + + virtual void set_http_mode(void) { + std::cout << "http mode set" << std::endl; + } + + virtual ~MSN_network_parameters() {} +}; + + + +// Jabber implementation +class Jabber_network_parameters : public network_parameters +{ + public: + virtual const char * hostname(void) const { return "jabber.org"; } + virtual const char * port(void) const { return "7063"; } + + virtual void set_http_mode(void) { + std::cout << "http mode not supported" << std::endl; + } + + virtual ~Jabber_network_parameters() {} +}; + +inline bool operator<(const boost::shared_ptr & first, + const boost::shared_ptr & second) { + int comp = strcmp(first->hostname(), second->hostname()); + if (!comp) { + return strcmp(first->port(), second->port()) < 0; + } + else return comp < 0; +} diff --git a/libs/extension/examples/info/im/plugins.cpp b/libs/extension/examples/info/im/plugins.cpp new file mode 100644 index 00000000..77bf7512 --- /dev/null +++ b/libs/extension/examples/info/im/plugins.cpp @@ -0,0 +1,72 @@ +/* + * Boost.Extension / IM plugins + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include + +#include "protocol.hpp" +#include "network_parameters.hpp" + +#include + +// MSN protocol implementation +class MSN : public protocol +{ +public: + virtual void login(const std::string &user, const std::string &pass) { + std::cout << "MSN: Logged In" << std::endl; + } + virtual void send(const std::string &msg) { + std::cout << "MSN: message [" << msg << "] sent" << std::endl; + } + virtual std::string receive(void) { + return std::string("MSN: hello! msg received"); + } + virtual void change_status(const std::string &new_status) { + std::cout << "MSN: Status changed to [" << new_status << "]" + << std::endl; + } + + virtual ~MSN(void) {} +}; + +// Jabber protocol implementation +class Jabber : public protocol +{ +public: + virtual void login(const std::string &user, const std::string &pass) { + std::cout << "Jabber: Logged In" << std::endl; + } + virtual void send(const std::string &msg) { + std::cout << "Jabber: message [" << msg << "] sent" << std::endl; + } + virtual std::string receive(void) { + return std::string("Jabber: hello! msg received"); + } + virtual void change_status(const std::string &new_status) { + std::cout << "Jabber: Status changed to [" << new_status << "]" + << std::endl; + } + + virtual ~Jabber(void) {} +}; + + +extern "C" void BOOST_EXTENSION_EXPORT_DECL +extension_export_plugins(boost::extensions::factory_map & fm) +{ + fm.get >() + [boost::shared_ptr(new MSN_network_parameters)].set(); + fm.get >() + [boost::shared_ptr(new Jabber_network_parameters)] + .set(); +} diff --git a/libs/extension/examples/info/im/protocol.hpp b/libs/extension/examples/info/im/protocol.hpp new file mode 100644 index 00000000..af14325c --- /dev/null +++ b/libs/extension/examples/info/im/protocol.hpp @@ -0,0 +1,25 @@ +/* + * Boost.Extension / protocol interface + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +// protocol for instant message communication +class protocol +{ + public: + + virtual void login(const std::string &user, const std::string &pass) {} + virtual void send(const std::string &msg) {} + virtual std::string receive(void) { return std::string(""); } + virtual void change_status(const std::string &new_status) {} + + virtual ~protocol(void) {} +}; + diff --git a/libs/extension/examples/info/multilanguage_hello_world.cpp b/libs/extension/examples/info/multilanguage_hello_world.cpp new file mode 100644 index 00000000..f75672aa --- /dev/null +++ b/libs/extension/examples/info/multilanguage_hello_world.cpp @@ -0,0 +1,86 @@ +/* + * Boost.Extension / multilanguage hello world implementations + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ +#include +#include "word_description.hpp" +#include + + +class world : public word +{ +public: + virtual const char * get_val(){return "world!";} +}; + +class mundo : public word +{ +public: + virtual const char * get_val(){return "mundo!";} +}; + +class monde : public word +{ +public: + virtual const char * get_val(){return "monde!";} +}; + +class mondo : public word +{ +public: + virtual const char * get_val(){return "mondo!";} +}; + + +class hello : public word +{ +public: + virtual const char * get_val(){return "hello";} +}; + +class hola : public word +{ +public: + virtual const char * get_val(){return "hola";} +}; + +class bonjour : public word +{ +public: + virtual const char * get_val(){return "bonjour";} +}; + +class buonasera : public word +{ +public: + virtual const char * get_val(){return "buonasera";} +}; + + +extern "C" void BOOST_EXTENSION_EXPORT_DECL +extension_export_multilanguage_word(boost::extensions::factory_map & fm) +{ + fm.get()[word_description("spanish", "hello")] + .set(); + fm.get()[word_description("spanish", "world!")] + .set(); + + fm.get()[word_description("french", "hello")] + .set(); + fm.get()[word_description("french", "world!")] + .set(); + fm.get()[word_description("italian", "hello")] + .set(); + fm.get()[word_description("italian", "world!")] + .set(); + fm.get()[word_description("english", "hello")] + .set(); + fm.get()[word_description("english", "world!")] + .set(); +} diff --git a/libs/extension/examples/info/multilanguage_main.cpp b/libs/extension/examples/info/multilanguage_main.cpp new file mode 100644 index 00000000..3f962302 --- /dev/null +++ b/libs/extension/examples/info/multilanguage_main.cpp @@ -0,0 +1,59 @@ +/* + * Boost.Extension / multilanguage hello world main + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include + +#include +#include + +#include "word_description.hpp" + +int main() +{ + using namespace boost::extensions; + // Create the factory_map object - it will hold all of the available + // constructors. Multiple factory_maps can be constructed. + factory_map fm; + + // load the shared library with + load_single_library(fm, "libMultilanguageHelloWorld.extension", + "extension_export_multilanguage_word"); + // Get a reference to the list of constructors for words. + std::map > & factory_list = + fm.get(); + + if (factory_list.size() < 4+4) { + std::cout << "Error - the classes were not found (" + << factory_list.size() << " classes)" << std::endl; + return 1; + } + + for (std::map >::iterator current_word = + factory_list.begin(); current_word != factory_list.end(); + ++current_word) { + // Using auto_ptr to avoid needing delete. Using smart_ptrs + // is recommended. + // Note that this has a zero argument constructor - currently + // constructors with up to six arguments can be used by + // default - define BOOST_EXTENSION_MAX_FUNCTOR_PARAMS to a + // a larger value if needed. + std::auto_ptr word_ptr(current_word->second.create()); + std::cout << word_ptr->get_val() << " is " + << current_word->first.english_translation + << " in " << current_word->first.language + << std::endl; + } + std::cout << std::endl; + + return 0; +} diff --git a/libs/extension/examples/info/word_description.hpp b/libs/extension/examples/info/word_description.hpp new file mode 100644 index 00000000..3e652c00 --- /dev/null +++ b/libs/extension/examples/info/word_description.hpp @@ -0,0 +1,29 @@ +/* + * Boost.Extension / info class for interface + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include "../word.hpp" +#include + +// info class for word interface +struct word_description +{ + std::string language; + std::string english_translation; + + word_description(std::string language, std::string english_translation) + : language(language), english_translation(english_translation) {} +}; +inline bool operator<(const word_description & first, + const word_description & second) { + return first.language < second.language || + (first.language == second.language && + first.english_translation < second.english_translation); +} diff --git a/libs/extension/examples/main.cpp b/libs/extension/examples/main.cpp new file mode 100644 index 00000000..1edcc9ed --- /dev/null +++ b/libs/extension/examples/main.cpp @@ -0,0 +1,46 @@ +/* + * Boost.Extension / hello world example main + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include +#include +#include "word.hpp" + +#include + +int main() +{ + using namespace boost::extensions; + // Create the factory_map object - it will hold all of the available + // constructors. Multiple factory_maps can be constructed. + factory_map fm; + // load the shared library with + load_single_library(fm, "libHelloWorldLib.extension", + "extension_export_word"); + // Get a reference to the list of constructors for words. + std::map > & factory_list = fm.get(); + if (factory_list.size() < 2) + std::cout << "Error - the classes were not found."; + for (std::map >::iterator current_word = + factory_list.begin(); current_word != factory_list.end(); + ++current_word) + { + // Using auto_ptr to avoid needing delete. Using smart_ptrs is + // recommended. + // Note that this has a zero argument constructor - currently constructors + // with up to six arguments can be used. + std::auto_ptr word_ptr(current_word->second.create()); + std::cout << word_ptr->get_val() << " "; + } + std::cout << "\n"; + return 0; +} diff --git a/libs/extension/examples/multiple_inheritance/boat.cpp b/libs/extension/examples/multiple_inheritance/boat.cpp new file mode 100644 index 00000000..6ed34eee --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/boat.cpp @@ -0,0 +1,41 @@ +/* + * Boost.Extension / multiple inheritance example (boat) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +/* The following lines are only necessary because when +are linking to this dll at compile time with another +dll on Windows. As such, standard __declspec stuff +is required. + +This example is something of a special case - normally +these types of macros are not necessary for classes +- see the FAQ. + */ +#include +#define BOOST_EXTENSION_BOAT_DECL BOOST_EXTENSION_EXPORT_DECL +#include "boat.hpp" +#include +#include + + +std::string boat::list_capabilities() +{ + return "\nIt floats on water."; +} + +using boost::extensions::factory; + +BOOST_EXTENSION_TYPE_MAP_FUNCTION { + types.get > >() + ["A boat exported as a vehicle"].set(); + types.get > >() + ["A boat exported as a boat"].set(); +} diff --git a/libs/extension/examples/multiple_inheritance/boat.hpp b/libs/extension/examples/multiple_inheritance/boat.hpp new file mode 100644 index 00000000..72b65882 --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/boat.hpp @@ -0,0 +1,29 @@ +/* + * Boost.Extension / multiple inheritance example (boat) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_BOAT_HPP +#define BOOST_EXTENSION_BOAT_HPP +// See the FAQ for info about why the following is necessary +// here, but usually isn't. +#define BOOST_EXTENSION_VEHICLE_DECL BOOST_EXTENSION_IMPORT_DECL +#include "vehicle.hpp" +#include +#include + +class BOOST_EXTENSION_BOAT_DECL boat : virtual public vehicle +{ +public: + boat(void){std::cout << "\nCreated a Boat";} + virtual ~boat(void){std::cout << "\nDestroyed a Boat";} + virtual std::string list_capabilities(void); +}; + +#endif diff --git a/libs/extension/examples/multiple_inheritance/car.cpp b/libs/extension/examples/multiple_inheritance/car.cpp new file mode 100644 index 00000000..5fb69d42 --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/car.cpp @@ -0,0 +1,42 @@ +/* + * Boost.Extension / multiple inheritance example (car) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +/* The following lines are only necessary because when +are linking to this dll at compile time with another +dll on Windows. As such, standard __declspec stuff +is required. + +This example is something of a special case - normally +these types of macros are not necessary for classes +- see the FAQ. + */ + +#include +#define BOOST_EXTENSION_CAR_DECL BOOST_EXTENSION_EXPORT_DECL + + +#include "car.hpp" +#include +#include +std::string car::list_capabilities() +{ + return "\nIt travels on roads."; +} + +using boost::extensions::factory; + +BOOST_EXTENSION_TYPE_MAP_FUNCTION { + types.get > >() + ["A car exported as a vehicle"].set(); + types.get > >() + ["A car exported as a car"].set(); +} diff --git a/libs/extension/examples/multiple_inheritance/car.hpp b/libs/extension/examples/multiple_inheritance/car.hpp new file mode 100644 index 00000000..4214a084 --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/car.hpp @@ -0,0 +1,28 @@ +/* + * Boost.Extension / multiple inheritance example (car) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_CAR_HPP +#define BOOST_EXTENSION_CAR_HPP +// See the FAQ for info about why the following is necessary +// here, but usually isn't. +#define BOOST_EXTENSION_VEHICLE_DECL BOOST_EXTENSION_IMPORT_DECL +#include "vehicle.hpp" +#include +#include +class BOOST_EXTENSION_CAR_DECL car : virtual public vehicle +{ +public: + car(void){std::cout << "\nCreated a Car";} + virtual ~car(void){std::cout << "\nDestroyed a Car";} + virtual std::string list_capabilities(void); +}; + +#endif diff --git a/libs/extension/examples/multiple_inheritance/car_of_the_future.cpp b/libs/extension/examples/multiple_inheritance/car_of_the_future.cpp new file mode 100644 index 00000000..8b058e6e --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/car_of_the_future.cpp @@ -0,0 +1,52 @@ +/* + * Boost.Extension / multiple inheritance example (car of the future) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +/* The following lines are only necessary because when +are linking to this dll at compile time with another +dll on Windows. As such, standard __declspec stuff +is required. + +This example is something of a special case - normally +these types of macros are not necessary for classes +- see the FAQ. + */ +#include +#define BOOST_EXTENSION_CAR_OF_THE_FUTURE_DECL BOOST_EXTENSION_EXPORT_DECL + +#include "car_of_the_future.hpp" +#include +#include +#include + +std::string car_of_the_future::list_capabilities() { + return boat::list_capabilities() + flying_car::list_capabilities() + + computer::list_capabilities() + "\nCosts an arm and a leg"; +} + +using boost::extensions::factory; + +BOOST_EXTENSION_TYPE_MAP_FUNCTION { + types.get > >() + ["A car of the future exported as a vehicle"].set(); + types.get > >() + ["A car of the future exported as a car"].set(); + types.get > >() + ["A car of the future exported as a plane"].set(); + types.get > >() + ["A car of the future exported as a flying car"].set(); + types.get > >() + ["A car of the future exported as a boat"].set(); + types.get > >() + ["A car of the future exported as a computer"].set(); + types.get > >() + ["A car of the future exported as a car of the future"].set(); +} diff --git a/libs/extension/examples/multiple_inheritance/car_of_the_future.hpp b/libs/extension/examples/multiple_inheritance/car_of_the_future.hpp new file mode 100644 index 00000000..fab402ab --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/car_of_the_future.hpp @@ -0,0 +1,33 @@ +/* + * Boost.Extension / multiple inheritance example (car of the future) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_CAR_OF_THE_FUTURE_HPP +#define BOOST_EXTENSION_CAR_OF_THE_FUTURE_HPP +// See the FAQ for info about why the following is necessary +// here, but usually isn't. +#define BOOST_EXTENSION_FLYING_CAR_DECL BOOST_EXTENSION_IMPORT_DECL +#define BOOST_EXTENSION_BOAT_DECL BOOST_EXTENSION_IMPORT_DECL +#define BOOST_EXTENSION_COMPUTER_DECL BOOST_EXTENSION_IMPORT_DECL +#include "flying_car.hpp" +#include "boat.hpp" +#include "computer.hpp" +#include +#include +class BOOST_EXTENSION_CAR_OF_THE_FUTURE_DECL + car_of_the_future : public flying_car, public boat, public computer +{ +public: + car_of_the_future(void){std::cout << "\nCreated a Car of the Future";} + ~car_of_the_future(void){std::cout << "\nDestroyed a Car of the Future";} + virtual std::string list_capabilities(void); +}; + +#endif diff --git a/libs/extension/examples/multiple_inheritance/computer.cpp b/libs/extension/examples/multiple_inheritance/computer.cpp new file mode 100644 index 00000000..af79271d --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/computer.cpp @@ -0,0 +1,39 @@ +/* + * Boost.Extension / multiple inheritance example (computer) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +/* The following lines are only necessary because when +are linking to this dll at compile time with another +dll on Windows. As such, standard __declspec stuff +is required. + +This example is something of a special case - normally +these types of macros are not necessary for classes +- see the FAQ. + */ +#include +#define BOOST_EXTENSION_COMPUTER_DECL BOOST_EXTENSION_EXPORT_DECL + + +#include "computer.hpp" +#include +#include + +std::string computer::list_capabilities() +{ + return "\nIt computes."; +} + +using boost::extensions::factory; + +BOOST_EXTENSION_TYPE_MAP_FUNCTION { + types.get > >() + ["\nA computer exported as a computer"].set(); +} diff --git a/libs/extension/examples/multiple_inheritance/computer.hpp b/libs/extension/examples/multiple_inheritance/computer.hpp new file mode 100644 index 00000000..cefff9cd --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/computer.hpp @@ -0,0 +1,26 @@ +/* + * Boost.Extension / multiple inheritance example (computer) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_COMPUTER_HPP +#define BOOST_EXTENSION_COMPUTER_HPP +#include +#include +#include +#include +class BOOST_EXTENSION_COMPUTER_DECL computer +{ +public: + computer(void){std::cout << "\nCreated a Computer";} + virtual ~computer(void){std::cout << "\nDestroyed a Computer";} + virtual std::string list_capabilities(void); +}; + +#endif diff --git a/libs/extension/examples/multiple_inheritance/flying_car.cpp b/libs/extension/examples/multiple_inheritance/flying_car.cpp new file mode 100644 index 00000000..d97598f2 --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/flying_car.cpp @@ -0,0 +1,48 @@ +/* + * Boost.Extension / multiple inheritance example (flying car) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +/* The following lines are only necessary because when +are linking to this dll at compile time with another +dll on Windows. As such, standard __declspec stuff +is required. + +This example is something of a special case - normally +these types of macros are not necessary for classes +- see the FAQ. + */ +#include +#include +#define BOOST_EXTENSION_FLYING_CAR_DECL BOOST_EXTENSION_EXPORT_DECL + + +#include "flying_car.hpp" +#include +#include + +std::string flying_car::list_capabilities() +{ + return car::list_capabilities() + plane::list_capabilities() + + "\nIt takes off from your driveway"; +} + +using boost::extensions::factory; + +BOOST_EXTENSION_TYPE_MAP_FUNCTION { + types.get > >() + ["A flying car exported as a vehicle"].set(); + types.get > >() + ["A flying car exported as a plane"].set(); + types.get > >() + ["A flying car exported as a car"].set(); + types.get > >() + ["A flying car exported as a flying car"].set(); +} diff --git a/libs/extension/examples/multiple_inheritance/flying_car.hpp b/libs/extension/examples/multiple_inheritance/flying_car.hpp new file mode 100644 index 00000000..ca5c6624 --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/flying_car.hpp @@ -0,0 +1,31 @@ +/* + * Boost.Extension / multiple inheritance example (flying car) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#ifndef BOOST_EXTENSION_FLYING_CAR_HPP +#define BOOST_EXTENSION_FLYING_CAR_HPP +// See the FAQ for info about why the following is necessary +// here, but usually isn't. +#define BOOST_EXTENSION_PLANE_DECL BOOST_EXTENSION_IMPORT_DECL +#define BOOST_EXTENSION_CAR_DECL BOOST_EXTENSION_IMPORT_DECL +#include "car.hpp" +#include "plane.hpp" +#include +#include +class BOOST_EXTENSION_FLYING_CAR_DECL flying_car : public car, public plane +{ +public: + flying_car(void):vehicle(){std::cout << "\nCreated a Flying Car";} + ~flying_car(void){std::cout << "\nDestroyed a Flying Car";} + virtual std::string list_capabilities(void); +}; + +#endif diff --git a/libs/extension/examples/multiple_inheritance/main_mi.cpp b/libs/extension/examples/multiple_inheritance/main_mi.cpp new file mode 100644 index 00000000..7df8133f --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/main_mi.cpp @@ -0,0 +1,85 @@ +/* + * Boost.Extension / multiple inheritance example (main) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +// See the FAQ for info about why the following is necessary +// here, but usually isn't. +#define BOOST_EXTENSION_VEHICLE_DECL BOOST_EXTENSION_IMPORT_DECL +#define BOOST_EXTENSION_COMPUTER_DECL BOOST_EXTENSION_IMPORT_DECL +#include "vehicle.hpp" +#include "computer.hpp" +#include +#include +#include + +int main() { + using namespace boost::extensions; + // Create the type_map object - it will hold all of the available + // constructors. + type_map types; + // Load the constructors and information into the factory_map. + load_single_library(types, "libVehicle.extension"); + load_single_library(types, "libCar.extension"); + load_single_library(types, "libComputer.extension"); + load_single_library(types, "libBoat.extension"); + load_single_library(types, "libFlyingCar.extension"); + load_single_library(types, "libCarOfTheFuture.extension"); + load_single_library(types, "libPlane.extension"); + + // Get a reference to the list of constructors. + // Note that the factories can be copied just fine - meaning that the + // map of factories can be copied from the type_map object into a + // different data structure, and the type_map can be destroyed. + // Here, we just use the efficient std::map::swap function. + std::cout << "\n>>>>>>>>>>>>\nComputers:\n>>>>>>>>>>>>>>>>>>>"; + std::map > computers; + computers.swap(types.get()); + if (computers.empty()) { + std::cout << "Error - no computers were found."; + return 1; + } + for (std::map >::iterator comp = + computers.begin(); comp != computers.end(); ++comp) { + // Using scoped_ptr to avoid needing delete. Using smart_ptrs is + // recommended. + // Note that this has a zero argument constructor - currently constructors + // with up to six arguments can be used. + boost::scoped_ptr computer_ptr(comp->second.create()); + std::cout << "\n--------\nLoaded the class described as: "; + std::cout << comp->first; + std::cout << "\n\nIt claims the following capabilities: "; + std::cout << computer_ptr->list_capabilities() << std::endl; + } + std::cout << "\n\n"; + + std::cout << "\n>>>>>>>>>>>>\nVehicles:\n>>>>>>>>>>>>>>>>>>>" << std::endl; + std::map > vehicles; + vehicles.swap(types.get()); + if (vehicles.empty()) { + std::cout << "Error - no vehicles were found."; + return 1; + } + for (std::map >::iterator v = + vehicles.begin(); v != vehicles.end(); ++v) { + // Using auto_ptr to avoid needing delete. Using smart_ptrs is + // recommended. + // Note that this has a zero argument constructor - currently constructors + // with up to six arguments can be used. + std::auto_ptr vehicle_ptr(v->second.create()); + std::cout << "\n--------\nLoaded the class described as: "; + std::cout << v->first; + std::cout << "\n\nIt claims the following capabilities: "; + std::cout << vehicle_ptr->list_capabilities() << std::endl; + } + std::cout << "\n\n"; + return 0; +} diff --git a/libs/extension/examples/multiple_inheritance/plane.cpp b/libs/extension/examples/multiple_inheritance/plane.cpp new file mode 100644 index 00000000..5e1cee0d --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/plane.cpp @@ -0,0 +1,40 @@ +/* + * Boost.Extension / multiple inheritance example (plane) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +/* The following lines are only necessary because when +are linking to this dll at compile time with another +dll on Windows. As such, standard __declspec stuff +is required. + +This example is something of a special case - normally +these types of macros are not necessary for classes +- see the FAQ. + */ +#include +#define BOOST_EXTENSION_PLANE_DECL BOOST_EXTENSION_EXPORT_DECL + + +#include "plane.hpp" +#include +#include + +std::string plane::list_capabilities() { + return "\nIt flies in the air."; +} + +using boost::extensions::factory; + +BOOST_EXTENSION_TYPE_MAP_FUNCTION { + types.get > >() + ["A plane exported as a vehicle"].set(); + types.get > >() + ["A plane exported as a plane"].set(); +} diff --git a/libs/extension/examples/multiple_inheritance/plane.hpp b/libs/extension/examples/multiple_inheritance/plane.hpp new file mode 100644 index 00000000..19d1f506 --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/plane.hpp @@ -0,0 +1,29 @@ +/* + * Boost.Extension / multiple inheritance example (plane) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#ifndef BOOST_EXTENSION_PLANE_HPP +#define BOOST_EXTENSION_PLANE_HPP +// See the FAQ for info about why the following is necessary +// here, but usually isn't. +#define BOOST_EXTENSION_VEHICLE_DECL BOOST_EXTENSION_IMPORT_DECL +#include "vehicle.hpp" +#include +#include +class BOOST_EXTENSION_PLANE_DECL plane : virtual public vehicle +{ +public: + plane(void){std::cout << "\nCreated a Plane";} + ~plane(void){std::cout << "\nDestroyed a Plane";} + virtual std::string list_capabilities(void); +}; + +#endif diff --git a/libs/extension/examples/multiple_inheritance/vehicle.cpp b/libs/extension/examples/multiple_inheritance/vehicle.cpp new file mode 100644 index 00000000..62fc8fa0 --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/vehicle.cpp @@ -0,0 +1,38 @@ +/* + * Boost.Extension / multiple inheritance example (vehicle) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +/* The following lines are only necessary because when +are linking to this dll at compile time with another +dll on Windows. As such, standard __declspec stuff +is required. + +This example is something of a special case - normally +these types of macros are not necessary for classes +- see the FAQ. + */ +#include +#define BOOST_EXTENSION_VEHICLE_DECL BOOST_EXTENSION_EXPORT_DECL + +#include "vehicle.hpp" +#include +#include + +std::string vehicle::list_capabilities() +{ + return "\nIt is some sort of vehicle."; +} + +using boost::extensions::factory; + +BOOST_EXTENSION_TYPE_MAP_FUNCTION { + types.get > >() + ["A vehicle exported as a vehicle"].set(); +} diff --git a/libs/extension/examples/multiple_inheritance/vehicle.hpp b/libs/extension/examples/multiple_inheritance/vehicle.hpp new file mode 100644 index 00000000..8bd0fcda --- /dev/null +++ b/libs/extension/examples/multiple_inheritance/vehicle.hpp @@ -0,0 +1,27 @@ +/* + * Boost.Extension / multiple inheritance example (vehicle) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_VEHICLE_HPP +#define BOOST_EXTENSION_VEHICLE_HPP +#include +#include +#include +#include + +class BOOST_EXTENSION_VEHICLE_DECL vehicle +{ +public: + vehicle(void){std::cout << "\nCreated a Vehicle";} + virtual ~vehicle(void){std::cout << "\nDestroyed a Vehicle";} + virtual std::string list_capabilities(void); +}; + +#endif diff --git a/libs/extension/examples/parameters/lots_of_parameters.cpp b/libs/extension/examples/parameters/lots_of_parameters.cpp new file mode 100644 index 00000000..06d702dd --- /dev/null +++ b/libs/extension/examples/parameters/lots_of_parameters.cpp @@ -0,0 +1,46 @@ +/* + * Boost.Extension / lots of parameters implementation + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include "lots_of_parameters_iface.hpp" +#include +#include + +#include + +class six_parameters : public lots_of_parameters_interface +{ +public: + six_parameters(bool b, unsigned int i, char c, std::string s, A a, + boost::shared_ptr ptr_a) + : lots_of_parameters_interface(b, i, c, s, a, ptr_a) + { + std::cout << "Constructor called." << std::endl << std::endl; + std::cout << "Parameters:" << std::endl; + std::cout << "\tbool: " << b << std::endl; + std::cout << "\tunsigned int: " << i << std::endl; + std::cout << "\tchar: " << c << std::endl; + std::cout << "\tstring: " << s << std::endl; + std::cout << "\tA.i: " << a.i << std::endl; + std::cout << "\tptr_a->i: " << ptr_a->i << std::endl; + std::cout << std::endl; + } + + virtual ~six_parameters(void) {} +}; + + +extern "C" void BOOST_EXTENSION_EXPORT_DECL +extension_export(boost::extensions::factory_map & fm) +{ + fm.get< lots_of_parameters_interface, int, bool, + unsigned int, char, std::string, A, boost::shared_ptr >()[6] + .set(); +} diff --git a/libs/extension/examples/parameters/lots_of_parameters_iface.hpp b/libs/extension/examples/parameters/lots_of_parameters_iface.hpp new file mode 100644 index 00000000..f3796891 --- /dev/null +++ b/libs/extension/examples/parameters/lots_of_parameters_iface.hpp @@ -0,0 +1,31 @@ +/* + * Boost.Extension / lots of parameters interface + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include + +#include + +class A +{ +public: + A(unsigned int i) : i(i) {} + int i; +}; + + +class lots_of_parameters_interface +{ +public: + lots_of_parameters_interface(bool b, unsigned int i, char c, std::string s, + A a, boost::shared_ptr ptr_a) {} + virtual ~lots_of_parameters_interface(void) {} +}; + diff --git a/libs/extension/examples/parameters/main_lp.cpp b/libs/extension/examples/parameters/main_lp.cpp new file mode 100644 index 00000000..62cc3baf --- /dev/null +++ b/libs/extension/examples/parameters/main_lp.cpp @@ -0,0 +1,47 @@ +/* + * Boost.Extension / lots of parameters main + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include +#include + +#include "lots_of_parameters_iface.hpp" + +int main(void) +{ + using namespace boost::extensions; + + // Create the factory_map object - it will hold all of the available + // constructors. Multiple factory_maps can be constructed. + factory_map fm; + + // load the shared library with + load_single_library(fm, "libParametersLib.extension", "extension_export"); + + std::map > > & factory_list = + fm.get >(); + if (factory_list.size() != 1) { + std::cout << "Error - the class was not found."; + return 1; + } + + std::map > >::iterator par = + factory_list.begin(); + std::auto_ptr< lots_of_parameters_interface > + par_ptr(par->second.create(true, 4, 'c', "test", A(2), + boost::shared_ptr(new A(15)))); + + return 0; +} diff --git a/libs/extension/examples/registry/registry_example.cpp b/libs/extension/examples/registry/registry_example.cpp new file mode 100644 index 00000000..1a4bfc5f --- /dev/null +++ b/libs/extension/examples/registry/registry_example.cpp @@ -0,0 +1,82 @@ +/* + * Boost.Extension / registry class use example + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include "../word.hpp" +#include +#include +using namespace boost::extensions; + +int main(int argc, char * argv[]) +{ + registry reg; + reg.open("libRegistryLibrary.extension"); + { + std::list > & factory_list = + reg.get(); + if (factory_list.size() != 1) { + std::cout << "Expected to see one class available - found: " + << factory_list.size() << std::endl; + return -1; + } + { + std::auto_ptr w(factory_list.begin()->create()); + const char * value; + if (strcmp(value = w->get_val(), "First Time") != 0) + { + std::cout << "The string's value should have been \"First Time\" " + << "but was: " + << value << std::endl; + return -1; + } + if (strcmp(value = w->get_val(), "Second Time") != 0) + { + std::cout << "The string's value should have been \"Second Time\" " + << "but was: " + << value << std::endl; + return -1; + } + } + } + + // This closes libregistry_library.extension, since none of + // its classes are instantiated any more. + reg.clear(); + // Now we can repeat the above - with the same results. + { + reg.open("libRegistryLibrary.extension"); + std::list > & factory_list = + reg.get(); + if (factory_list.size() != 1) + { + std::cout << "Expected to see one class available - found: " + << factory_list.size() << std::endl; + return -1; + } + { + std::auto_ptr w(factory_list.begin()->create()); + const char * value; + if (strcmp(value = w->get_val(), "First Time") != 0) + { + std::cout << "The string's value should have been \"First Time\" " + << "but was: " + << value << std::endl; + return -1; + } + if (strcmp(value = w->get_val(), "Second Time") != 0) + { + std::cout << "The string's value should have been \"Second Time\" " + << "but was: " + << value << std::endl; + return -1; + } + } + } +} diff --git a/libs/extension/examples/registry/registry_library.cpp b/libs/extension/examples/registry/registry_library.cpp new file mode 100644 index 00000000..36ef842c --- /dev/null +++ b/libs/extension/examples/registry/registry_library.cpp @@ -0,0 +1,49 @@ +/* + * Boost.Extension / registry class use example (library) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +// Notice that we don't need to include the registry file - just the class +// it's derived from. This keeps us from pulling the shared_library headers +// into this shared library. +#include "../word.hpp" +using namespace boost::extensions; +class counting_word : public word +{ +public: + virtual ~counting_word(){} + virtual const char * get_val() + { + static int times_called = 0; + ++times_called; + switch (times_called) + { + case 1: + return "First Time"; + break; + case 2: + return "Second Time"; + break; + default: + return "Many times"; + break; + } + } +}; +// Using the default function name for loading registries: +extern "C" void BOOST_EXTENSION_EXPORT_DECL +boost_extension_registry_function(counted_factory_map & fm) +{ + // 5 is just an identifier - not used in this example. + // Arbitrary information (not just an int) can be stored + // with the factory. + fm.get[5].set(); +} diff --git a/libs/extension/examples/runtime_compilation/Jamfile.v2 b/libs/extension/examples/runtime_compilation/Jamfile.v2 new file mode 100644 index 00000000..22bd182e --- /dev/null +++ b/libs/extension/examples/runtime_compilation/Jamfile.v2 @@ -0,0 +1,28 @@ +# Boost.Extension - examples Jamfile +# +# Copyright 2007 Jeremy Pack +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# +# See http://www.boost.org/ for latest version. +# + +import type : change-generated-target-suffix ; +import type : change-generated-target-prefix ; +type.change-generated-target-suffix SHARED_LIB : : extension ; +type.change-generated-target-prefix SHARED_LIB : : lib ; +import os ; + +local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; +project + : requirements + + $(BOOST_ROOT)/libs/filesystem/build//boost_filesystem/static + $(BOOST_ROOT)/libs/system/build//boost_system/static + ../../../ + $(BOOST_ROOT) + : + ; + +exe bjam_compilation_bin : bjam_compilation.cpp ; diff --git a/libs/extension/examples/runtime_compilation/bjam_compilation.cpp b/libs/extension/examples/runtime_compilation/bjam_compilation.cpp new file mode 100644 index 00000000..d20d09a9 --- /dev/null +++ b/libs/extension/examples/runtime_compilation/bjam_compilation.cpp @@ -0,0 +1,154 @@ +/* + * Boost.Extension / bjam_compilation example source: + * main file for bjam compilation + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include "compilation.hpp" + +#include + +#include +#include +#include +#include + +namespace boost { +namespace extensions { +namespace { +const char* boost_build_contents = +"local rule if-has-file ( file + : dir * ) \n" +"{ \n" +" local result ;\n" +" if $(dir) { \n" +" result = [ GLOB $(dir) : $(file) ] ;\n" +" } return $(result[1]:P) ; }\n" +" local boost-src = [ if-has-file configure : [ MATCH --boost=(.*)\n" +" : $(ARGV) ] $(BOOST) $(.boost-build-file:D)/../boost ] ;\n" +" local boost-build-src = [ if-has-file bootstrap.jam :\n" +" [ MATCH --boost-build=(.*) : $(ARGV) ] $(BOOST_BUILD_PATH) $(BOOST_BUILD)\n" +" $(boost-src)/tools/build/v2 ] ; BOOST ?= $(boost-src) ;\n" +" BOOST_ROOT ?= $(boost-src) ; boost-build $(boost-build-src) ;\n"; + +const char* jamfile_preamble = +"import type : change-generated-target-suffix ; \n" +"import type : change-generated-target-prefix ; \n" +"type.change-generated-target-suffix SHARED_LIB : : extension ; \n" +"type.change-generated-target-prefix SHARED_LIB : : lib ; \n" +"import os ; \n" +"local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; \n" +"local BOOST_SANDBOX_ROOT = [ os.environ BOOST_SANDBOX_ROOT ] ; \n" +"project : requirements $(BOOST_SANDBOX_ROOT) $(BOOST_ROOT) \n" +"gcc:dl \n" +"gcc:\"-Wl,-rpath,'$ORIGIN'\" \n" +"darwin:DYLD_LIBRARY_PATH=./ : ; \n"; + +void PrintHeader(std::ostream& out, const std::string& header_location) { + out << "#include " << header_location << std::endl; +} + +} // namespace + +class bjam_compilation : public compilation { +public: + virtual bool run(const std::string& library_name, + const std::string& external_function_contents, + type_map& types, + const std::string& earlier_file_contents = "") const { + using namespace boost::filesystem; + + // Find the current path so that it can be reset after + // the shared library is loaded. + path initial_path(current_path()); + + // If there is a compilation_directory set, switch to it. + if (!compilation_directory_.empty()) { + std::system(("cd " + compilation_directory_).c_str()); + } + + // Create boost-build.jam. + path compilation_path(current_path()); + ofstream o(compilation_path / "boost-build.jam"); + o << "# Start\n\n" + << std::string(boost_build_contents) << std::endl; + + // Create Jamfile.v2 + ofstream p(compilation_path / "Jamfile.v2"); + p << "# Start\n\n" + << std::string(jamfile_preamble) + << " lib " << library_name << " : " << library_name + << ".cpp ;\ninstall . : " << library_name << " ;" + << std::endl; + p.close(); + + // Create Jamroot. + ofstream r(compilation_path / "Jamroot"); + r << "# empty" << std::endl; + r.close(); + + // Create the source file that will be compiled. + ofstream cpp_file(compilation_path / (library_name + ".cpp")); + cpp_file << "// Boost.Extension generated file" << std::endl; + std::for_each(headers_.begin(), headers_.end(), + boost::bind(PrintHeader, boost::ref(cpp_file), _1)); + cpp_file << + "\n\nextern \"C\" " + "void BOOST_EXTENSION_EXPORT_DECL\n" + "boost_extension_compiled_function(" + "boost::extensions::type_map& types) {\n" + << external_function_contents << "\n}" << std::endl; + cpp_file.close(); + + // Compile it! + int result = 0; + if ((result = std::system("bjam")) != 0) { + std::cerr << "Compilation failed." << std::endl; + return false; + } + std::cout << "Compilation result: " << result << std::endl; + + // Open the shared library, and call the function. + shared_library library("lib" + library_name + ".extension"); + if (!library.open()) { + std::cerr << "Library not found." << std::endl; + return false; + } + typedef void (*compiled_func)(type_map&); + compiled_func func = + library.get("boost_extension_compiled_function"); + if (!func) { + std::cerr << "Function not found." << std::endl; + return false; + } + func(types); + + // Return to the original directory. + std::system(("cd " + initial_path.string()).c_str()); + return true; + } +}; +} // namespace extensions +} // namespace boost + + +int main(int argc, char* argv[]) { + using namespace boost::extensions; + bjam_compilation c; + c.add_header(""); + type_map types; + if (c.run("test_library", + "std::cout << \"Inside the shared library: Yay!\" << std::endl;", + types)) { + std::cout << "Success!" << std::endl; + } else { + std::cout << "Failure!" << std::endl; + } + return 0; +} diff --git a/libs/extension/examples/runtime_compilation/compilation.hpp b/libs/extension/examples/runtime_compilation/compilation.hpp new file mode 100644 index 00000000..fbbde387 --- /dev/null +++ b/libs/extension/examples/runtime_compilation/compilation.hpp @@ -0,0 +1,58 @@ +/* + * Boost.Extension / compilation example header: + * main header for compilations + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_COMPILATION_HPP +#define BOOST_EXTENSION_COMPILATION_HPP + +#include +#include + +#include + +namespace boost { +namespace extensions { +class compilation { +public: + virtual bool run(const std::string& library_name, + const std::string& external_function_contents, + type_map& types, + const std::string& earlier_file_contents = "") const = 0; + void add_header(const std::string& location) { + headers_.push_back(location); + } + void vector_compilation_directory(const std::string& directory) { + compilation_directory_ = directory; + } + void add_include_path(const std::string& location) { + headers_.push_back(location); + } + void add_source(const std::string& location) { + headers_.push_back(location); + } + void add_static_library(const std::string& name) { + headers_.push_back(name); + } + compilation() { + headers_.push_back(""); + headers_.push_back(""); + } + virtual ~compilation() {} +protected: + std::vector headers_; + std::vector include_path_; + std::vector sources_; + std::vector libraries_; + std::string compilation_directory_; +}; +} // namespace extensions +} // namespace boost +#endif // BOOST_EXTENSION_COMPILATION_HPP diff --git a/libs/extension/examples/tutorial_1/hello_world.cpp b/libs/extension/examples/tutorial_1/hello_world.cpp new file mode 100644 index 00000000..0d5bd161 --- /dev/null +++ b/libs/extension/examples/tutorial_1/hello_world.cpp @@ -0,0 +1,28 @@ +/* + * Boost.Extension / hello world implementations + * + * (C) Copyright Jeremy Pack 2007-2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include + +#include + +// Any exported function or variable must be declared +// extern "C" to avoid C++ name mangling. +extern "C" +// Depending on the compiler and settings, +// it may be necessary to add a specific export +// declaration. The BOOST_EXTENSION_EXPORT_DECL +// adds this if necessary. +void BOOST_EXTENSION_EXPORT_DECL +boost_extension_hello_world(int repetitions) { + for (int i = 0; i < repetitions; ++i) { + std::cout << "Hello World" << std::endl; + } +} diff --git a/libs/extension/examples/tutorial_1/main.cpp b/libs/extension/examples/tutorial_1/main.cpp new file mode 100644 index 00000000..436b57c3 --- /dev/null +++ b/libs/extension/examples/tutorial_1/main.cpp @@ -0,0 +1,54 @@ +/* + * Boost.Extension / hello world example main + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include + +#include +#include + +int main() { + using namespace boost::extensions; + + // In the Jamfile, shared libraries are set to have the same + // prefix and extension, even on different operating systems. + // This is for convenience in writing cross-platform code, but + // is not required. All shared libraries are set to start with + // "lib" and end with "extension". + std::string library_path = "libtutorial_1.extension"; + + // Create shared_library object with the relative or absolute + // path to the shared library. + shared_library lib(library_path); + + // Attempt to open the shared library. + if (!lib.open()) { + std::cerr << "Library failed to open: " << library_path << std::endl; + return 1; + } + + // Retrieve a function from the library, and store it in a Boost.Function + // object. It is also possible to use function pointers, but the syntax + // for Boost.Function is easier to understand. This retrieves a function + // called "boost_extension_hello_world" with a void return type and a single + // parameter of type int. + boost::function + f(lib.get("boost_extension_hello_world")); + + // Check that the function was found. + if (!f) { + std::cerr << "Function not found!" << std::endl; + return 1; + } + + // Call the function from the shared library with + // an integer parameter. + f(4); +} diff --git a/libs/extension/examples/tutorial_2/animal.hpp b/libs/extension/examples/tutorial_2/animal.hpp new file mode 100644 index 00000000..358fb29e --- /dev/null +++ b/libs/extension/examples/tutorial_2/animal.hpp @@ -0,0 +1,32 @@ +/* + * Boost.Extension / factory example (car) + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_ANIMAL_HPP +#define BOOST_EXTENSION_ANIMAL_HPP + +#include +class animal { +public: + animal(int age) : age_(age) { + } + virtual ~animal(void) { + } + virtual std::string get_name(void) { + return "A generic animal"; + } + int get_age(void) { + return age_; + } +protected: + int age_; +}; + +#endif diff --git a/libs/extension/examples/tutorial_2/factories_lib.cpp b/libs/extension/examples/tutorial_2/factories_lib.cpp new file mode 100644 index 00000000..c452805f --- /dev/null +++ b/libs/extension/examples/tutorial_2/factories_lib.cpp @@ -0,0 +1,62 @@ +/* + * Boost.Extension / hello world implementations + * + * (C) Copyright Jeremy Pack 2007-2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include + +#include +#include +#include + +#include "animal.hpp" + +class puma : public animal { + public: + puma(int age) : animal(age) {} + virtual std::string get_name() { + return "puma"; + } +}; + +class leopard : public animal { + public: + leopard(int age) : animal(age) {} + virtual std::string get_name() { + return "leopard"; + } +}; + +class wildcat : public animal { + public: + wildcat(int age) : animal(age) {} + virtual std::string get_name() { + return "wildcat"; + } +}; + +class cougar : public animal { + public: + cougar(int age) : animal(age) {} + virtual std::string get_name() { + return "cougar"; + } +}; + + +BOOST_EXTENSION_TYPE_MAP_FUNCTION { + using namespace boost::extensions; + std::map >& + animal_factories(types.get()); + animal_factories["Puma factory"].set(); + animal_factories["Leopard factory"].set(); + animal_factories["Wildcat factory"].set(); + animal_factories["Cougar factory"].set(); +} diff --git a/libs/extension/examples/tutorial_2/factories_main.cpp b/libs/extension/examples/tutorial_2/factories_main.cpp new file mode 100644 index 00000000..7b161995 --- /dev/null +++ b/libs/extension/examples/tutorial_2/factories_main.cpp @@ -0,0 +1,71 @@ +/* + * Boost.Extension / hello world example main + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "animal.hpp" + +int main() { + using namespace boost::extensions; + + // In the Jamfile, shared libraries are set to have the same + // prefix and extension, even on different operating systems. + // This is for convenience in writing cross-platform code, but + // is not required. All shared libraries are set to start with + // "lib" and end with "extension". + std::string library_path = "libtutorial_2.extension"; + + // Create shared_library object with the relative or absolute + // path to the shared library. + shared_library lib(library_path); + + // Attempt to open the shared library. + if (!lib.open()) { + std::cerr << "Library failed to open: " << library_path << std::endl; + return 1; + } + + // Use the shared_library::call to automatically call an + // Extension-specific function in the shared library, + // which takes a type_map. + type_map types; + if (!lib.call(types)) { + std::cerr << "Function not found!" << std::endl; + return 1; + } + + // Retrieve a map of all animal factories taking an int and indexed + // by a string from the type_map. + std::map >& factories(types.get()); + if (factories.empty()) { + std::cerr << "Animals not found!" << std::endl; + return 1; + } + + // Create each animal. + int age = 1; + for (std::map >::iterator it + = factories.begin(); + it != factories.end(); ++it) { + ++age; + std::cout << "Creating an animal using factory: " << it->first << std::endl; + boost::scoped_ptr current_animal(it->second.create(age)); + std::cout << "Created an animal: " << current_animal->get_name() + << " Age: " << current_animal->get_age() << std::endl; + } +} diff --git a/libs/extension/examples/tutorial_5/adaptable.cpp b/libs/extension/examples/tutorial_5/adaptable.cpp new file mode 100644 index 00000000..392ecc00 --- /dev/null +++ b/libs/extension/examples/tutorial_5/adaptable.cpp @@ -0,0 +1,85 @@ +/* Boost.Extension / adaptable_factory example + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include + +#include +#include +#include + +using namespace boost::extensions; +using namespace boost::reflections; + +// This function would need to use some of the facilities +// from Boost.TypeTraits in order to handle references, etc. +// well. +template +void InputParameter(parameter_map& params, const std::string& name) { + T value; + std::cin >> value; + std::cout << "Input value: " << value << " of type: " + << typeid(T).name() << std::endl; + params.insert(std::make_pair(name, new parameter(value))); +} + +class Operation { + public: + Operation(double result) : result(result) { + } + + double result; +}; + +class Multiplier : public Operation { + public: + Multiplier(double first_value, double second_value) + : Operation(first_value * second_value) { + } +}; + +int main() { + std::map input_functions; + input_functions[typeid(float)] = &InputParameter; + input_functions[typeid(double)] = &InputParameter; + input_functions[typeid(std::string)] = &InputParameter; + + adaptable_factory op_factory; + op_factory.set("First Double", "Second Double"); + + parameter_map params; + + std::vector > missing = + op_factory.get_missing_params(params); + + for (std::vector >::iterator + it = missing.begin(); + it != missing.end(); ++it) { + std::cout << "Finding type to input..." << std::endl; + std::map::iterator func = + input_functions.find(it->first); + if (func == input_functions.end()) { + std::cout << "Could not find all needed parameters." << std::endl; + return 1; + } + + std::cout << "Please input a value for the variable <" << it->second + << "> of type <" << it->first.type().name() << ">:" << std::endl; + (*func->second)(params, it->second); + std::cout << "Input complete for parameter." << std::endl; + } + boost::scoped_ptr op(op_factory.create(params)); + if (op.get()) { + std::cout << "The result is: " << op->result << std::endl; + } else { + std::cout << "Creation failed!" << std::endl; + } +} diff --git a/libs/extension/examples/versioning/hello_world_versions.cpp b/libs/extension/examples/versioning/hello_world_versions.cpp new file mode 100644 index 00000000..cf6ce57d --- /dev/null +++ b/libs/extension/examples/versioning/hello_world_versions.cpp @@ -0,0 +1,32 @@ +/* + * Boost.Extension / hello world versions implementations + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include "../word.hpp" +#include +#include + +class world : public word +{ +public: + virtual const char * get_val(){return "world! v2";} +}; +class hello : public word +{ +public: + virtual const char * get_val(){return "| v2 hello";} +}; +extern "C" void BOOST_EXTENSION_EXPORT_DECL +extension_export_word(boost::extensions::factory_map & fm) +{ + fm.get()[21].set(); // int could be used as version (v2 word 1) + fm.get()[22].set(); // int could be used as version (v2 word 2) +} diff --git a/libs/extension/examples/versioning/main_versions.cpp b/libs/extension/examples/versioning/main_versions.cpp new file mode 100644 index 00000000..beda2b00 --- /dev/null +++ b/libs/extension/examples/versioning/main_versions.cpp @@ -0,0 +1,97 @@ +/* + * Boost.Extension / hello world versions main + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include +#include + +#include +#include + + +#include "../word.hpp" +#include "salute.hpp" + +int main(void) +{ + using namespace boost::extensions; + + // Create the factory_map object - it will hold all of the available + // constructors. Multiple factory_maps can be constructed. + factory_map fm; + + // load hello world first version + load_single_library(fm, "libHelloWorldLib.extension", + "extension_export_word"); + + // load hello world second version + load_single_library(fm, "libHelloWorldLibv2.extension", + "extension_export_word"); + + // load hello world second version again + load_single_library(fm, "libHelloWorldLibv2.extension", + "extension_export_word"); + + // load salute library (with hello included) + load_single_library(fm, "libSaluteLib.extension", "extension_export_salute"); + + // Get a reference to the list of constructors for words. + std::map > & factory_list = fm.get(); + + if (factory_list.size() < 6) { + std::cout << "Error - the classes were not found (" + << factory_list.size() << " classes)" << std::endl; + return 1; + } + + std::cout << "words: " << std::endl; + for (std::map >::iterator current_word = + factory_list.begin(); + current_word != factory_list.end(); ++current_word) + { + // Using auto_ptr to avoid needing delete. Using smart_ptrs is + // recommended. + // Note that this has a zero argument constructor - currently + // constructors + // with up to six arguments can be used. + std::auto_ptr word_ptr(current_word->second.create()); + std::cout << word_ptr->get_val() << " "; + } + std::cout << std::endl << std::endl; + + // Get a reference to the list of constructors for salutes. + std::map > & salute_factory_list = + fm.get(); + + if (salute_factory_list.size() < 2) { + std::cout << "Error - the classes were not found (" + << salute_factory_list.size() << " classes)" << std::endl; + return 1; + } + + std::cout << "salutes: " << std::endl; + for (std::map >::iterator current_salute = + salute_factory_list.begin(); + current_salute != salute_factory_list.end(); ++current_salute) + { + // Using auto_ptr to avoid needing delete. Using smart_ptrs is + // recommended. + // Note that this has a zero argument constructor - currently + // constructors + // with up to six arguments can be used. + std::auto_ptr salute_ptr(current_salute->second.create()); + std::cout << salute_ptr->say() << " "; + } + std::cout << std::endl; + + return 0; +} diff --git a/libs/extension/examples/versioning/salute.cpp b/libs/extension/examples/versioning/salute.cpp new file mode 100644 index 00000000..25dcd7e9 --- /dev/null +++ b/libs/extension/examples/versioning/salute.cpp @@ -0,0 +1,34 @@ +/* + * Boost.Extension / salute implementations + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include "salute.hpp" +#include +#include + +class hello : public salute +{ +public: + virtual const char *say(void) {return "hello";} +}; + +class bye : public salute +{ +public: + virtual const char *say(void) {return "bye!";} +}; + + +extern "C" void BOOST_EXTENSION_EXPORT_DECL +extension_export_salute(boost::extensions::factory_map & fm) +{ + fm.get()[1].set(); + fm.get()[2].set(); +} diff --git a/libs/extension/examples/versioning/salute.hpp b/libs/extension/examples/versioning/salute.hpp new file mode 100644 index 00000000..e15108b3 --- /dev/null +++ b/libs/extension/examples/versioning/salute.hpp @@ -0,0 +1,17 @@ +/* + * Boost.Extension / salute interface + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +class salute +{ +public: + virtual ~salute(){} + virtual const char * say(){return "";} +}; diff --git a/libs/extension/examples/word.hpp b/libs/extension/examples/word.hpp new file mode 100644 index 00000000..0d882763 --- /dev/null +++ b/libs/extension/examples/word.hpp @@ -0,0 +1,17 @@ +/* + * Boost.Extension / word interface + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +class word +{ +public: + virtual ~word(){} + virtual const char * get_val(){return "";} +}; diff --git a/libs/extension/fake.cpp b/libs/extension/fake.cpp new file mode 100644 index 00000000..758fff83 --- /dev/null +++ b/libs/extension/fake.cpp @@ -0,0 +1,13 @@ +/* This file is used to create empty library + * for MSVC Extension.Test project dependency. + * It is useful to test header files. + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +//#include diff --git a/libs/extension/project-root.jam b/libs/extension/project-root.jam new file mode 100644 index 00000000..a37c8cf5 --- /dev/null +++ b/libs/extension/project-root.jam @@ -0,0 +1,7 @@ +# Copyright Rene Rivera 2007. +# +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For instructions see Jamfile.v2, or "bjam --help". diff --git a/libs/extension/scripts/GenerateHeaders.py b/libs/extension/scripts/GenerateHeaders.py new file mode 100755 index 00000000..b551ef42 --- /dev/null +++ b/libs/extension/scripts/GenerateHeaders.py @@ -0,0 +1,500 @@ +# Boost.Extension - GenerateHeaders script +# Use for generation of factory.h and shared_library.h +# +# Copyright 2007 Jeremy Pack +# +# See http://www.boost.org/ for latest version. +# + +import sys + +class header_generator: + def __init__(self, max_params): + self.max_params = max_params + + def generate(self): + self.generate_factory_hpp() + self.generate_counted_factory_hpp() + self.generate_shared_library_hpp() + self.generate_factory_map_hpp("factory", "", "", "", """ + factory_container() {} + // factory_container(basic_factory_map & z) + // :std::list >(z.get()){} + virtual ~factory_container(){} + """) + self.generate_factory_map_hpp("counted_factory", + "\n f.set_library(current_library_.c_str());\n f.set_counter(current_counter_);", + "\n std::string current_library_;\n int default_counter_;\n int * current_counter_;", + "\n virtual bool remove_library(const char * library_name) = 0;", """ + int * counter_; + factory_container(int * counter) : counter_(counter) {++(*counter_);} + virtual ~factory_container() {--(*counter_);} + virtual bool remove_library(const char * library_name) + { + for (typename std::list >::iterator it = this->begin(); + it != this->end();) + { + if (strcmp(it->library(), library_name) == 0) + this->erase(it++); + else + ++it; + } + return this->size() == 0; + }""", + "\n basic_counted_factory_map() : default_counter_(0), current_counter_(&default_counter_){}", + "current_counter_") + + def template_header(self, start_string, start, count, add_void): + if add_void: + v = " = void" + else: + v = "" + center = ", ".join(start + ["class Param" + str(i) + v + for i in range(1, count + 1)]) + out_str = "".join([start_string, center, ">\n"]) + return out_str + + def nameless_param_list(self, count, start): + return ", ".join(start + ["Param" + str(i) for i in range(1, count+1)]) + + def param_names(self, count, start): + return ", ".join(start + ["p" + str(i) for i in range(1, count+1)]) + + def named_param_list(self, count, start): + return ", ".join(start + ["".join(["Param", str(i), " p", str(i)]) + for i in range(1, count+1)]) + + def generate_factory_hpp(self): + out = open('../../../boost/extension/factory.hpp', mode='w') + out.write("""/* (C) Copyright Jeremy Pack 2007 +* Distributed under the Boost Software License, Version 1.0. (See +* accompanying file LICENSE_1_0.txt or copy at +* http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef BOOST_EXTENSION_FACTORY_HPP +#define BOOST_EXTENSION_FACTORY_HPP +#include +namespace boost{namespace extensions{ + """) + for i in range(self.max_params, -1, -1): + out.write(self.template_header('template <', + ['class Interface', 'class Info'], + i, i == self.max_params)) + if i != self.max_params: + factory_template = ("".join(["<", + self.nameless_param_list(i, ["Interface", "Info"]), + ">\n"])) + else: + factory_template = "\n" + out.write("".join(["class factory", + factory_template, + """{ +protected: + class generic_factory_function + { + public: + virtual ~generic_factory_function(){} + virtual Interface * operator()(""", + self.nameless_param_list(i, []), + """) = 0; + virtual generic_factory_function * copy() const = 0; + }; + template + class factory_function : public generic_factory_function + { + public: + virtual ~factory_function(){} + virtual Interface * operator() + (""", + self.named_param_list(i, []), + """) + {return new T(""", + self.param_names(i, []), + """);} + virtual generic_factory_function * copy() const {return new factory_function;} + }; + std::auto_ptr factory_func_ptr_; + Info info_; +public: + template + void set_type_special(Actual *){factory_func_ptr_.reset(new factory_function());} + template + void set_type(){factory_func_ptr_ = new factory_function();} + factory(Info info) + :factory_func_ptr_(0), + info_(info) + {} + factory(const factory & first) + :factory_func_ptr_(first.factory_func_ptr_->copy()), + info_(first.info_) + {} + Interface * operator()(""", + self.named_param_list(i, []), + """) + {return create(""", + self.param_names(i, []), + """);} + Interface * create(""", + self.named_param_list(i, []), + """){return (*factory_func_ptr_) + (""", + self.param_names(i, []), + """);} + Info & get_info(){return info_;} +}; +"""])) + # For loop over! + out.write("""}} +#endif +""") + + def generate_counted_factory_hpp(self): + out = open('../../../boost/extension/counted_factory.hpp', mode='w') + out.write("""/* (C) Copyright Jeremy Pack 2007 +* Distributed under the Boost Software License, Version 1.0. (See +* accompanying file LICENSE_1_0.txt or copy at +* http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef BOOST_EXTENSION_COUNTED_FACTORY_HPP +#define BOOST_EXTENSION_COUNTED_FACTORY_HPP +#include +namespace boost{namespace extensions{ + """) + for i in range(self.max_params, -1, -1): + out.write(self.template_header('template <', + ['class Interface', 'class Info'], + i, i == self.max_params)) + if i != self.max_params: + factory_template = ("".join(["<", + self.nameless_param_list(i, ["Interface", "Info"]), + ">\n"])) + else: + factory_template = "\n" + out.write("".join(["class counted_factory", + factory_template, + """{ +protected: + int * counter_; + std::string library_; + class generic_factory_function + { + public: + virtual ~generic_factory_function(){} + virtual Interface * operator()(""", + self.nameless_param_list(i, ["int * counter"]), + """) = 0; + virtual generic_factory_function * copy() const = 0; + }; + template + class factory_function : public generic_factory_function + { + public: + class counted_object : public T + { + private: + int * counter_; + public: + counted_object(""", + self.named_param_list(i, ["int * counter"]), + """) : T(""", + self.param_names(i, []),"""), + counter_(counter) + { + ++(*counter_); + } + ~counted_object() + { + --(*counter_); + } + }; + virtual ~factory_function(){} + virtual Interface * operator() + (""", + self.named_param_list(i, ["int * counter"]), + """) + { // A compilation error here usually indicates that the + // class you are adding is not derived from the base class + // that you indicated. + return static_cast(new counted_object(""", + self.param_names(i, ["counter"]), + """));} + virtual generic_factory_function * copy() const {return new factory_function;} + }; + std::auto_ptr factory_func_ptr_; + Info info_; +public: + void set_library(const char * library_name) + { + library_ = library_name; + } + void set_counter(int * counter) + { + counter_ = counter; + } + const char * library() + { + return library_.c_str(); + } + template + void set_type_special(Actual *){factory_func_ptr_.reset(new factory_function());} + template + void set_type(){factory_func_ptr_ = new factory_function();} + counted_factory(Info info) + :factory_func_ptr_(0), + info_(info) + {} + counted_factory(const counted_factory & first) + :counter_(first.counter_), + library_(first.library_), + factory_func_ptr_(first.factory_func_ptr_->copy()), + info_(first.info_) + {} + Interface * operator()(""", + self.named_param_list(i, ["int * counter"]), + """) + {return create(""", + self.param_names(i, ["counter"]), + """);} + Interface * create(""", + self.named_param_list(i, []), + """){return (*factory_func_ptr_) + (""", + self.param_names(i, ["counter_"]), + """);} + Info & get_info(){return info_;} +}; +"""])) + # For loop over! + out.write("""}} +#endif +""") + + + def generate_shared_library_hpp(self): + out = open('../../../boost/extension/shared_library.hpp', mode='w') + out.write("""/* (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_EXTENSION_LINKED_LIBRARY_HPP +#define BOOST_EXTENSION_LINKED_LIBRARY_HPP +#include + + +namespace boost{namespace extensions{ +""") + for i in range(self.max_params, -1, -1): + out.write(self.template_header('template <', + ['class ReturnValue'], + i, i == self.max_params)) + if i != self.max_params: + functor_template = ("".join(["<", + self.nameless_param_list(i, ["ReturnValue"]), + ">\n"])) + else: + functor_template = "\n" + out.write("".join(["class functor", + functor_template, + """{ +protected: + typedef ReturnValue (*FunctionType)(""", + self.nameless_param_list(i, []), + """); + FunctionType func_; +public: + bool is_valid(){return func_ != 0;} + functor(FunctionType func) + :func_(func) + {} + functor(generic_function_ptr func) + :func_(FunctionType(func)) + {} + ReturnValue operator()(""", + self.named_param_list(i, []), + """) + { + return func_(""", + self.param_names(i, []), + """); + } +}; + +"""])) + # end for loop + out.write("""class shared_library +{ +protected: + std::string location_; + library_handle handle_; + bool auto_close_; +public: + bool is_open(){return handle_ != 0;} + static bool is_linkable_library(const char * file_name){return is_library(file_name);} + bool open(){return (handle_ = load_shared_library(location_.c_str())) != 0;} + bool close(){return close_shared_library(handle_);}""") + for i in range(0, self.max_params + 1): + out.write(self.template_header(' template <', + ['class ReturnValue'], + i, False)) + functor_template = ("".join(["functor<", + self.nameless_param_list(i, ["ReturnValue"]), + ">\n"])) + out.write("".join([" ", + functor_template, + " get_functor(", + "const char * function_name", + """) + { + return """, + functor_template, + """ (get_function(handle_, function_name)); + } +"""])) + # end for loop + out.write("""shared_library(const char * location, bool auto_close = false) + :location_(location), handle_(0), auto_close_(auto_close){} +}; +}} + + +#endif +""") + + def generate_factory_map_hpp(self, factory_type, factory_type_add, new_members, + generic_factory_container_additions = "", + factory_container_contents = "", new_public_members = "", + factory_container_params = ""): + out = open(''.join(['../../../boost/extension/',factory_type,'_map.hpp']), mode='w') + out.write(''.join(["""/* (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +#ifndef BOOST_EXTENSION_""",factory_type.upper(),"""_MAP_HPP +#define BOOST_EXTENSION_""",factory_type.upper(),"""_MAP_HPP +#include +#include +#include +#include +#include +#include + +namespace boost{namespace extensions{ + + +template +class basic_""",factory_type,"""_map +{ +protected: + class generic_factory_container + { + public:""", generic_factory_container_additions, """ + virtual ~generic_factory_container(){} + }; + template + class factory_container : public std::list<""",factory_type,""" >, public generic_factory_container + { + public:""", factory_container_contents, """ + // factory_container() {} + // factory_container(basic_""", factory_type, """_map & z) + // :std::list<""",factory_type,""" >(z.get()){} + //virtual ~factory_container(){} + }; + typedef std::map FactoryMap; + FactoryMap factories_;""", new_members,""" +public:""", new_public_members,""" + ~basic_""", factory_type, """_map(){ + for(typename FactoryMap::iterator it = factories_.begin(); it != factories_.end(); ++it) + delete it->second; + //TODO - test for memory leaks. + } +"""])) + for i in range(0, self.max_params + 1): + out.write("".join([self.template_header(' template <', + ['class Interface', 'class Info'], + i, False), + "operator std::list<",factory_type,"<", + self.nameless_param_list(i, ["Interface", "Info"]), + """> > & () + {return this->get<""", + self.nameless_param_list(i, ["Interface", "Info"]), + """>();} +""", + self.template_header(' template <', + ['class Interface', 'class Info'], + i, False), + "std::list<", factory_type, "<", + self.nameless_param_list(i, ["Interface", "Info"]), + """> > & get() + { + TypeInfo current_type = + type_info_handler >::get_class_type(); + typename FactoryMap::iterator it = + factories_.find(current_type); + + if (it == factories_.end()) + { + factory_container<""", + self.nameless_param_list(i, ["Interface", "Info"]), + """> * ret = + new factory_container<""", + self.nameless_param_list(i, ["Interface", "Info"]), + """>(""",factory_container_params,"""); + factories_[current_type] = ret; + return *ret; + } + else + { + // Change to dynamic if this fails + return static_cast &>(*(it->second)); + } + } +""", + self.template_header('template <', + ['class Actual', 'class Interface', 'class Info'], + i, False), + """void add(Info info) + { + typedef std::list<""",factory_type,"""<""", + self.nameless_param_list(i, ["Interface", "Info"]), + """> > ListType; + ListType & s = this->get<""", + self.nameless_param_list(i, ["Interface", "Info"]), + """>(); + """,factory_type,"""<""", + self.nameless_param_list(i, ["Interface", "Info"]), + """> f(info);""",factory_type_add,""" + //f.set_type(); + f.set_type_special((Actual*)0); + s.push_back(f); + //it->set_type(); + } +"""])) + # end for loop + out.write(''.join(["""}; + +typedef basic_""",factory_type,"""_map """,factory_type,"""_map; +}} + +#endif +"""])) + + +def main(argv): + if len(argv) > 1: + max_params = int(argv[1]) + else: + max_params = 6 + gen = header_generator(max_params) + gen.generate() + +if __name__=='__main__': + main(sys.argv) diff --git a/libs/extension/test/IM b/libs/extension/test/IM new file mode 100755 index 00000000..6c4d587c Binary files /dev/null and b/libs/extension/test/IM differ diff --git a/libs/extension/test/Jamfile.v2 b/libs/extension/test/Jamfile.v2 new file mode 100644 index 00000000..f8409f78 --- /dev/null +++ b/libs/extension/test/Jamfile.v2 @@ -0,0 +1,42 @@ +# Boost.Extension - tests Jamfile +# +# Copyright 2008 Jeremy Pack +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# +# See http://www.boost.org/ for latest version. +# + +using testing ; +import os ; +local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; +project + : requirements + $(BOOST_ROOT)/libs/test/build//boost_unit_test_framework/shared + ../../../ + $(BOOST_ROOT) + BOOST_TEST_NO_AUTO_LINK=1 + gcc:BOOST_TEST_DYN_LINK + gcc:dl + darwin:DYLD_LIBRARY_PATH=../bin/ + : + ; + +test-suite extension_tests_all +: + [ run type_map_test.cpp ] + [ run adaptable_factory_test.cpp ] + [ run factory_test.cpp ] + [ run zone_test.cpp ] + [ run construction.cpp ] + [ run single_param_test.cpp ] + [ run double_param_test.cpp ] + [ run mixed_param_test.cpp ] + [ run shared_library_test.cpp ] + [ run parameters_test.cpp ] + [ run executables_test.cpp ] + # [ run multiple_inheritance_test.cpp ] # redundant + [ run extension_test.cpp ] +: +; diff --git a/libs/extension/test/MultilanguageWord b/libs/extension/test/MultilanguageWord new file mode 100755 index 00000000..f5cc57b2 Binary files /dev/null and b/libs/extension/test/MultilanguageWord differ diff --git a/libs/extension/test/MultipleInheritance b/libs/extension/test/MultipleInheritance new file mode 100755 index 00000000..d463d5b3 Binary files /dev/null and b/libs/extension/test/MultipleInheritance differ diff --git a/libs/extension/test/Parameters b/libs/extension/test/Parameters new file mode 100755 index 00000000..87361977 Binary files /dev/null and b/libs/extension/test/Parameters differ diff --git a/libs/extension/test/adaptable_factory_test.cpp b/libs/extension/test/adaptable_factory_test.cpp new file mode 100644 index 00000000..e70b9e2f --- /dev/null +++ b/libs/extension/test/adaptable_factory_test.cpp @@ -0,0 +1,131 @@ +/* + * Boost.Extension / testing of adapter class + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include +#include +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include + + +using namespace boost::extensions; +using namespace boost::reflections; +using boost::scoped_ptr; +using boost::function; + +class adaptable_class_base { +public: + virtual ~adaptable_class_base() {} + virtual int get() { + return 5; + } +}; + +class adaptable_class : public adaptable_class_base { +public: + virtual int get() { + return my_value; + } + adaptable_class() + : my_value(1) { + } + adaptable_class(int new_val) + : my_value(new_val) { + } + adaptable_class(int first_new_val, int second_new_val) + : my_value(first_new_val * second_new_val) { + } +private: + int my_value; +}; + +BOOST_AUTO_TEST_CASE(noArgTest) { + parameter_map params; + adaptable_factory current_factory; + current_factory.set(); + scoped_ptr a(current_factory.create(params)); + BOOST_CHECK_EQUAL(a->get(), 1); +} + +BOOST_AUTO_TEST_CASE(oneArgTest) { + parameter_map params; + parameter* p1 = new parameter(5); + params.insert(std::make_pair("first value", p1)); + + generic_parameter<>* g = params.get_first("first value"); + BOOST_CHECK(g->can_cast()); + BOOST_CHECK_EQUAL(5, g->cast()); + + adaptable_factory current_factory; + current_factory.set("first value"); + BOOST_CHECK(current_factory.get_missing_params(params).empty()); + scoped_ptr a(current_factory.create(params)); + BOOST_CHECK_EQUAL(a->get(), 5); +} + +BOOST_AUTO_TEST_CASE(twoArgTest) { + parameter_map params; + parameter* p1 = new parameter(5); + parameter* p2 = new parameter(2); + params.insert(std::make_pair("first value", p1)); + params.insert(std::make_pair("second value", p2)); + + adaptable_factory current_factory; + current_factory.set("first value", "second value"); + BOOST_CHECK(current_factory.get_missing_params(params).empty()); + scoped_ptr a(current_factory.create(params)); + BOOST_CHECK_EQUAL(a->get(), 10); +} + +BOOST_AUTO_TEST_CASE(missingParameters) { + parameter_map params; + parameter* p1 = new parameter(5); + parameter* p2 = new parameter(2); + + adaptable_factory current_factory; + current_factory.set("first value", "second value"); + + BOOST_CHECK_EQUAL(current_factory.get_missing_params(params).size(), size_t(2)); + + params.insert(std::make_pair("first value", p1)); + params.insert(std::make_pair("second value", p2)); + + BOOST_CHECK_EQUAL(current_factory.get_missing_params(params).size(), size_t(0)); +} + +BOOST_AUTO_TEST_CASE(getFunction) { + parameter_map params; + + adaptable_factory current_factory; + current_factory.set("first value"); + + boost::function f( + current_factory.get_function(params)); + + // It doesn't have the needed parameter. + BOOST_CHECK(f.empty()); + + params.insert(std::make_pair("second_value", new parameter(6))); + f = current_factory.get_function(params); + + BOOST_CHECK(f.empty()); + + params.insert(std::make_pair("first value", new parameter(8))); + f = current_factory.get_function(params); + + BOOST_CHECK(!f.empty()); + + scoped_ptr a(f()); + BOOST_CHECK(a.get()); + BOOST_CHECK_EQUAL(a->get(), 8); +} diff --git a/libs/extension/test/bin/construction.test/gcc-4.4.3/debug/construction.o b/libs/extension/test/bin/construction.test/gcc-4.4.3/debug/construction.o new file mode 100644 index 00000000..3bd9b267 Binary files /dev/null and b/libs/extension/test/bin/construction.test/gcc-4.4.3/debug/construction.o differ diff --git a/libs/extension/test/bin/double_param_test.test/gcc-4.4.3/debug/double_param_test.o b/libs/extension/test/bin/double_param_test.test/gcc-4.4.3/debug/double_param_test.o new file mode 100644 index 00000000..17ab87be Binary files /dev/null and b/libs/extension/test/bin/double_param_test.test/gcc-4.4.3/debug/double_param_test.o differ diff --git a/libs/extension/test/bin/executables_test.test/gcc-4.4.3/debug/executables_test.o b/libs/extension/test/bin/executables_test.test/gcc-4.4.3/debug/executables_test.o new file mode 100644 index 00000000..9f610bd9 Binary files /dev/null and b/libs/extension/test/bin/executables_test.test/gcc-4.4.3/debug/executables_test.o differ diff --git a/libs/extension/test/bin/extension_test.test/gcc-4.4.3/debug/extension_test.o b/libs/extension/test/bin/extension_test.test/gcc-4.4.3/debug/extension_test.o new file mode 100644 index 00000000..70e62ad3 Binary files /dev/null and b/libs/extension/test/bin/extension_test.test/gcc-4.4.3/debug/extension_test.o differ diff --git a/libs/extension/test/bin/factory_test.test/gcc-4.4.3/debug/factory_test.o b/libs/extension/test/bin/factory_test.test/gcc-4.4.3/debug/factory_test.o new file mode 100644 index 00000000..38600280 Binary files /dev/null and b/libs/extension/test/bin/factory_test.test/gcc-4.4.3/debug/factory_test.o differ diff --git a/libs/extension/test/bin/mixed_param_test.test/gcc-4.4.3/debug/mixed_param_test.o b/libs/extension/test/bin/mixed_param_test.test/gcc-4.4.3/debug/mixed_param_test.o new file mode 100644 index 00000000..0832a73b Binary files /dev/null and b/libs/extension/test/bin/mixed_param_test.test/gcc-4.4.3/debug/mixed_param_test.o differ diff --git a/libs/extension/test/bin/parameters_test.test/gcc-4.4.3/debug/parameters_test.o b/libs/extension/test/bin/parameters_test.test/gcc-4.4.3/debug/parameters_test.o new file mode 100644 index 00000000..8f901ae2 Binary files /dev/null and b/libs/extension/test/bin/parameters_test.test/gcc-4.4.3/debug/parameters_test.o differ diff --git a/libs/extension/test/bin/shared_library_test.test/gcc-4.4.3/debug/shared_library_test.o b/libs/extension/test/bin/shared_library_test.test/gcc-4.4.3/debug/shared_library_test.o new file mode 100644 index 00000000..3eb4b058 Binary files /dev/null and b/libs/extension/test/bin/shared_library_test.test/gcc-4.4.3/debug/shared_library_test.o differ diff --git a/libs/extension/test/bin/single_param_test.test/gcc-4.4.3/debug/single_param_test.o b/libs/extension/test/bin/single_param_test.test/gcc-4.4.3/debug/single_param_test.o new file mode 100644 index 00000000..03c420ff Binary files /dev/null and b/libs/extension/test/bin/single_param_test.test/gcc-4.4.3/debug/single_param_test.o differ diff --git a/libs/extension/test/bin/type_map_test.test/gcc-4.4.3/debug/type_map_test.o b/libs/extension/test/bin/type_map_test.test/gcc-4.4.3/debug/type_map_test.o new file mode 100644 index 00000000..2955f8dd Binary files /dev/null and b/libs/extension/test/bin/type_map_test.test/gcc-4.4.3/debug/type_map_test.o differ diff --git a/libs/extension/test/bin/zone_test.test/gcc-4.4.3/debug/zone_test.o b/libs/extension/test/bin/zone_test.test/gcc-4.4.3/debug/zone_test.o new file mode 100644 index 00000000..cd200bab Binary files /dev/null and b/libs/extension/test/bin/zone_test.test/gcc-4.4.3/debug/zone_test.o differ diff --git a/libs/extension/test/construction.cpp b/libs/extension/test/construction.cpp new file mode 100644 index 00000000..40df6af9 --- /dev/null +++ b/libs/extension/test/construction.cpp @@ -0,0 +1,140 @@ +/* + * Boost.Extension / construction test case + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include +#define BOOST_TEST_MAIN +#include +#include +#include + +using namespace boost::extensions; + + +class Automobile { +public: + virtual ~Automobile() {} + Automobile() {} + virtual int getSpeed() {return 45;} +}; + +class Van : virtual public Automobile { +public: + virtual ~Van() {} + Van() {} + virtual int getSpeed() {return 35;} +}; + +class PickupTruck : virtual public Automobile { +public: + virtual ~PickupTruck() {} + PickupTruck() {} + virtual int getSpeed() {return 50;} +}; + +class Racecar : virtual public Automobile { +public: + virtual ~Racecar() {} + Racecar(int speed) : speed_(speed) {} + virtual int getSpeed() {return speed_;} +private: + int speed_; +}; + +class RacingVan : public Racecar, public Van +{ +public: + virtual ~RacingVan() {} + RacingVan() : Racecar(120) {} + virtual int getSpeed() {return Racecar::getSpeed();} +}; + + + + +BOOST_AUTO_TEST_CASE(factory_argless) { + factory f; + f.set(); + BOOST_CHECK(f.is_valid()); + std::auto_ptr pickup(f.create()); + BOOST_CHECK_EQUAL(pickup->getSpeed(), 50); +} + +BOOST_AUTO_TEST_CASE(map_argless) +{ + std::map > m; + m["A van"].set(); + m["A basic automobile"].set(); + m["A pickup truck"].set(); + std::auto_ptr van(m["A van"].create()); + BOOST_CHECK_EQUAL(van->getSpeed(), 35); + BOOST_CHECK_EQUAL + (m["An unloaded car!"].create(), + (Automobile*)0); +} +/* +namespace boost { namespace extensions { +template <> +Automobile * create_function::create() +{ + return new Racecar(101); +} +}} // namespace boost::extensions + +BOOST_AUTO_TEST_CASE(factory_template) +{ + factory f; + f.set(); + BOOST_CHECK(f.is_valid()); + std::auto_ptr racecar(f.create()); + BOOST_CHECK_EQUAL(racecar->getSpeed(), 101); +}*/ + +BOOST_AUTO_TEST_CASE(factory_map_argless) +{ + factory_map m; + m.get()["A pickup truck"].set(); + m.get()["A racing van!"].set(); + std::auto_ptr pickup + (m.get()["A pickup truck"].create()); + BOOST_CHECK_EQUAL(pickup->getSpeed(), 50); + std::auto_ptr racingVan + (m.get()["A racing van!"].create()); + BOOST_CHECK_EQUAL(racingVan->getSpeed(), 120); + std::auto_ptr car_ptr + (m.get()["A nonexistent car!"].create()); + BOOST_CHECK_EQUAL(car_ptr.get(), (Automobile*)0); +} +/* +BOOST_AUTO_TEST_CASE(registry_argless2) +{ + registry r; + r.add("A pickup truck"); + r.add("A racing van!"); + factory_map & m1(r.get()); + factory_map & m2(r.get()); + BOOST_CHECK_EQUAL(m1.size(), 2); +} + +BOOST_AUTO_TEST_CASE(counted_registry_argless) +{ + count_registry r; + r.add("A pickup truck"); + r.add("A racing van!"); + { + BOOST_CHECK_EQUAL(r.instances(), 0); + std::auto_ptr pickup = + r.create("A pickup truck"); + BOOST_CHECK_EQUAL(r.instances(), 1); + } + BOOST_CHECK_EQUAL(r.instances(), 0); +}*/ diff --git a/libs/extension/test/counted_factory_test.cpp b/libs/extension/test/counted_factory_test.cpp new file mode 100644 index 00000000..285aef70 --- /dev/null +++ b/libs/extension/test/counted_factory_test.cpp @@ -0,0 +1,22 @@ +/* + * Boost.Extension / counted factory test case + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include + +#define BOOST_TEST_MAIN +#include + +using namespace boost::extensions; + +BOOST_AUTO_TEST_CASE(counted_factory_map_construction) +{ + counted_factory_map fm; +} diff --git a/libs/extension/test/double_param_test.cpp b/libs/extension/test/double_param_test.cpp new file mode 100644 index 00000000..e3aa7711 --- /dev/null +++ b/libs/extension/test/double_param_test.cpp @@ -0,0 +1,123 @@ +/* + * Boost.Extension / construction test case + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include +#define BOOST_TEST_MAIN +#include +#include +#include + +using namespace boost::extensions; + + +class Automobile { +public: + virtual ~Automobile() {} + Automobile(int speed1, float speed2) : speed_(speed1 * speed2) {} + virtual float getSpeed() {return speed_;} +protected: + float speed_; +}; + +class Van : virtual public Automobile { +public: + virtual ~Van() {} + Van(int speed1, float speed2) : Automobile(speed1, speed2) {} + virtual float getSpeed() {return speed_ / 2;} +}; + +class PickupTruck : virtual public Automobile { +public: + virtual ~PickupTruck() {} + PickupTruck(int speed1, float speed2) : Automobile(speed1, speed2) {} + virtual float getSpeed() {return speed_ / 3;} +}; + +class Racecar : virtual public Automobile { +public: + virtual ~Racecar() {} + Racecar(int speed1, float speed2) : Automobile(speed1, speed2) {} + virtual float getSpeed() {return 3 * speed_;} +}; + +class RacingVan : public Racecar, public Van +{ +public: + virtual ~RacingVan() {} + RacingVan(int speed1, float speed2) : Automobile(speed1, speed2), + Racecar(speed1, speed2), Van(speed1, speed2) {} + virtual float getSpeed() {return 2 * speed_;} +}; + + + + +BOOST_AUTO_TEST_CASE(factory_argless) { + factory f; + f.set(); + BOOST_CHECK(f.is_valid()); + std::auto_ptr pickup(f.create(45, 2.0f)); + BOOST_CHECK_EQUAL(pickup->getSpeed(), 30.0f); +} + +BOOST_AUTO_TEST_CASE(map_argless) +{ + std::map > m; + m["A van"].set(); + m["A basic automobile"].set(); + m["A pickup truck"].set(); + std::auto_ptr van(m["A van"].create(10, 3.0f)); + BOOST_CHECK_EQUAL(van->getSpeed(), 15.0f); + BOOST_CHECK_EQUAL + (m["An unloaded car!"].create(30, 1.5f), + (Automobile*)0); +} +/* +namespace boost { namespace extensions { namespace impl { +template <> +Automobile * create_function::create() +{ + return new Racecar(25, 2.0f); +} +}}} // namespace boost::extensions::impl +*/ + +BOOST_AUTO_TEST_CASE(factory_template) +{ + factory f; + f.set(); + BOOST_CHECK(f.is_valid()); + std::auto_ptr racecar(f.create(20, 3.0f)); + BOOST_CHECK_EQUAL(racecar->getSpeed(), 180.0f); +} + +BOOST_AUTO_TEST_CASE(factory_map_single_param) +{ + factory_map m; + m.get()["A pickup truck"] + .set(); + m.get()["A racing van!"] + .set(); + std::auto_ptr pickup + (m.get()["A pickup truck"] + .create(30, 2.0f)); + BOOST_CHECK_EQUAL(pickup->getSpeed(), 20); + std::auto_ptr racingVan + (m.get()["A racing van!"] + .create(20, 3.0f)); + BOOST_CHECK_EQUAL(racingVan->getSpeed(), 120); + std::auto_ptr car_ptr + (m.get()["A nonexistent car!"] + .create(30, 1.2f)); + BOOST_CHECK_EQUAL(car_ptr.get(), (Automobile*)0); +} diff --git a/libs/extension/test/executables_test.cpp b/libs/extension/test/executables_test.cpp new file mode 100644 index 00000000..44100652 --- /dev/null +++ b/libs/extension/test/executables_test.cpp @@ -0,0 +1,22 @@ +/* + * Boost.Extension / executables unit test + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include + +#include + +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include + +BOOST_AUTO_TEST_CASE(multiple_inheritance_example) { + BOOST_CHECK_EQUAL(std::system("./MultipleInheritance"), 0); +} diff --git a/libs/extension/test/extension_test.cpp b/libs/extension/test/extension_test.cpp new file mode 100644 index 00000000..fb090d9b --- /dev/null +++ b/libs/extension/test/extension_test.cpp @@ -0,0 +1,34 @@ +/* + * Boost.Extension / extensions unit test + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include + +#define BOOST_TEST_MAIN +#include + + +using namespace boost::extensions; + +BOOST_AUTO_TEST_CASE(zone_construction) +{ + factory_map z; +} +BOOST_AUTO_TEST_CASE(factory_construction) +{ + //factory f; + +} +BOOST_AUTO_TEST_CASE(extension_construction) +{ + // extension e; + +} diff --git a/libs/extension/test/factory_test.cpp b/libs/extension/test/factory_test.cpp new file mode 100644 index 00000000..6c21caec --- /dev/null +++ b/libs/extension/test/factory_test.cpp @@ -0,0 +1,72 @@ +/* + * Boost.Extension / factory unit test + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#define BOOST_TEST_MAIN +#include + +#include "fruit.hpp" +#include +#include +using namespace boost::extensions; +void setup_zone(factory_map & z) +{ + z.get()["round fruit"].set(); + z.get()["long fruit"].set(); + z.get()["small fruit"].set(); + +} +BOOST_AUTO_TEST_CASE(construct_from_zone) +{ + factory_map z; + setup_zone(z); + std::vector > > + f1(z.get().begin(), + z.get().end()); + + std::vector > > + f2(z.get().begin(), + z.get().end()); + std::map > m1(z); + //std::vector > > f3(m1); + //std::vector > > f4 = m1; + BOOST_CHECK_EQUAL(f1.size(), f2.size()); + // BOOST_CHECK_EQUAL(f1.size(), f3.size()); + // BOOST_CHECK_EQUAL(f2.size(), f4.size()); + BOOST_CHECK_EQUAL(f1.size(), size_t(3)); +} +BOOST_AUTO_TEST_CASE(factory_construction) +{ + factory_map z; + setup_zone(z); + std::vector > > + f1(z.get().begin(), + z.get().end()); + std::vector > >::iterator + it = f1.begin(); + // Since our TypeInfo is string, they will be created + // in alphabetical order by TypeInfo ("round fruit" etc.), yielding + // banana, apple, nectarine + std::auto_ptr first(it->second.create(0, 1)); + std::auto_ptr second((++it)->second.create(0, 1)); + std::auto_ptr third((++it)->second.create(0, 1)); + BOOST_CHECK_EQUAL((first->get_cost()), 7); + BOOST_CHECK_EQUAL((second->get_cost()), 21); + BOOST_CHECK_EQUAL((third->get_cost()), 18); + BOOST_CHECK_EQUAL(typeid(*first.get()).name(), + typeid(banana).name()); + BOOST_CHECK_EQUAL(typeid(*second.get()).name(), typeid(apple).name()); + BOOST_CHECK_EQUAL(typeid(*third.get()).name(), typeid(nectarine).name()); + BOOST_CHECK(typeid(*third.get()).name() != typeid(banana).name()); + BOOST_CHECK(typeid(*third.get()).name() != typeid(banana).name()); +} + diff --git a/libs/extension/test/fruit.hpp b/libs/extension/test/fruit.hpp new file mode 100644 index 00000000..d9ca773a --- /dev/null +++ b/libs/extension/test/fruit.hpp @@ -0,0 +1,84 @@ +/* + * Boost.Extension / fruit interface + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#ifndef BOOST_EXTENSION_TEST_FRUIT_HPP +#define BOOST_EXTENSION_TEST_FRUIT_HPP + +class fruit +{ +public: + fruit(int a = 0, int b = 0){} + virtual ~fruit(){} + virtual int get_cost(){return 5;} +}; +class banana : public fruit +{ +public: + banana(int a = 0, int b = 0){} + + virtual int get_cost(){return 7;} +}; +class nectarine : public fruit +{ +public: + nectarine(int a = 0, int b = 0){} + + virtual int get_cost(){return 18;} +}; +class apple : public fruit +{ +public: + apple(int a = 0, int b = 0){} + + virtual int get_cost(){return 21;} + static bool meets_requirements(int val){return val == 8;} +}; + +class vegetable +{ +private: + float f_; +public: + virtual ~vegetable(){} + vegetable(float f):f_(f){} + virtual float get_weight(){return f_ * 2.0f;} +}; + +//I know it's not really a vegetable - but it as used as one +class tomato : public fruit, public vegetable +{ +public: + virtual ~tomato(){} + tomato(float f, int a, int b):fruit(a, b),vegetable(f){} + tomato(float f):fruit(0, 0), vegetable(f){} + tomato(int a, int b):fruit(a, b), vegetable(0){} + virtual float get_weight(){return 1.5 * vegetable::get_weight();} +}; +class vegetable_info +{ +private: + std::string name_; + int num_calories_; +public: + vegetable_info(const char * name, int calories) + :name_(name), num_calories_(calories){} + int get_calories() const {return num_calories_;} + const char * get_name() const {return name_.c_str();} + friend inline bool + operator<(const vegetable_info & first, + const vegetable_info & second) { + return first.name_ < second.name_ || + (first.name_ == second.name_ && + first.num_calories_ < second.num_calories_); + } + +}; +#endif diff --git a/libs/extension/test/hello_world_test.cpp b/libs/extension/test/hello_world_test.cpp new file mode 100644 index 00000000..a14841a5 --- /dev/null +++ b/libs/extension/test/hello_world_test.cpp @@ -0,0 +1,59 @@ +/* + * Boost.Extension / hello world unit test + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ +#include +#include +#include +#include +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include + +#include "../examples/word.hpp" + + +using namespace boost::extensions; + +BOOST_AUTO_TEST_CASE(hello_world_example) +{ + // check if the library can be loaded + shared_library l((std::string("libHelloWorldLib") + ".extension") + .c_str()); + BOOST_CHECK_EQUAL( l.open(), true ); + + // check if the factory can return the functor + factory_map fm; + void (*load_func)(factory_map &) = + l.get("extension_export_word"); + BOOST_CHECK(load_func != 0); + + (*load_func)(fm); + + // check if we can get the word list + std::map > & factory_list = fm.get(); + BOOST_CHECK_EQUAL( factory_list.size(), 2U ); + + // iterate trough the classes and execute get_val method + // to obtain the correct words + std::map >::iterator current_word = factory_list.begin(); + + std::auto_ptr hello_word_ptr(current_word->second.create()); + BOOST_CHECK_EQUAL( !hello_word_ptr.get(), 0 ); + + BOOST_CHECK_EQUAL( hello_word_ptr->get_val(), "hello"); + + ++current_word; + + std::auto_ptr world_word_ptr(current_word->second.create()); + BOOST_CHECK_EQUAL( !world_word_ptr.get(), 0 ); + + BOOST_CHECK_EQUAL( world_word_ptr->get_val(), "world!"); +} + diff --git a/libs/extension/test/libBoat.extension b/libs/extension/test/libBoat.extension new file mode 100755 index 00000000..53b72aa7 Binary files /dev/null and b/libs/extension/test/libBoat.extension differ diff --git a/libs/extension/test/libCar.extension b/libs/extension/test/libCar.extension new file mode 100755 index 00000000..73f3e6bd Binary files /dev/null and b/libs/extension/test/libCar.extension differ diff --git a/libs/extension/test/libCarOfTheFuture.extension b/libs/extension/test/libCarOfTheFuture.extension new file mode 100755 index 00000000..3deaa636 Binary files /dev/null and b/libs/extension/test/libCarOfTheFuture.extension differ diff --git a/libs/extension/test/libComputer.extension b/libs/extension/test/libComputer.extension new file mode 100755 index 00000000..418e7082 Binary files /dev/null and b/libs/extension/test/libComputer.extension differ diff --git a/libs/extension/test/libFlyingCar.extension b/libs/extension/test/libFlyingCar.extension new file mode 100755 index 00000000..ee535f51 Binary files /dev/null and b/libs/extension/test/libFlyingCar.extension differ diff --git a/libs/extension/test/libIMPlugins.extension b/libs/extension/test/libIMPlugins.extension new file mode 100755 index 00000000..b2965d8b Binary files /dev/null and b/libs/extension/test/libIMPlugins.extension differ diff --git a/libs/extension/test/libMultilanguageHelloWorld.extension b/libs/extension/test/libMultilanguageHelloWorld.extension new file mode 100755 index 00000000..565d9d05 Binary files /dev/null and b/libs/extension/test/libMultilanguageHelloWorld.extension differ diff --git a/libs/extension/test/libParametersLib.extension b/libs/extension/test/libParametersLib.extension new file mode 100755 index 00000000..1893ec41 Binary files /dev/null and b/libs/extension/test/libParametersLib.extension differ diff --git a/libs/extension/test/libPlane.extension b/libs/extension/test/libPlane.extension new file mode 100755 index 00000000..8f199a26 Binary files /dev/null and b/libs/extension/test/libPlane.extension differ diff --git a/libs/extension/test/libVehicle.extension b/libs/extension/test/libVehicle.extension new file mode 100755 index 00000000..3d6b4646 Binary files /dev/null and b/libs/extension/test/libVehicle.extension differ diff --git a/libs/extension/test/lib_caching_test.cpp b/libs/extension/test/lib_caching_test.cpp new file mode 100644 index 00000000..96ceb043 --- /dev/null +++ b/libs/extension/test/lib_caching_test.cpp @@ -0,0 +1,167 @@ +/* + * Boost.Extension / hello world unit test + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 + +#include +#include + +#include +#include + +#include "../examples/word.hpp" + +using namespace boost::extensions; + +/// tests if the SO caches libraries and if we can remove .extensions files before unloading + +BOOST_AUTO_TEST_CASE(lib_caching_test) +{ + if(boost::filesystem::exists("libHelloWorldCache.extension")) { + boost::filesystem::remove("libHelloWorldCache.extension"); + } + boost::filesystem::copy_file("libHelloWorldLib.extension", + "libHelloWorldCache.extension"); + + { + // load the first version + shared_library l((std::string("libHelloWorldCache") + + ".extension").c_str()); + BOOST_CHECK_EQUAL( l.open(), true ); + { + + // check if the factory can return the functor + factory_map fm; + void (*load_func)(factory_map &) = l.get("extension_export_word"); + BOOST_CHECK(load_func != 0); + + (*load_func)(fm); + + std::map > & factory_list = fm.get(); + BOOST_CHECK_EQUAL( factory_list.size(), 2U ); + + std::map >::iterator current_word = + factory_list.begin(); + + std::auto_ptr hello_word_ptr(current_word->second.create()); + BOOST_CHECK_EQUAL( !hello_word_ptr.get(), 0 ); + + BOOST_CHECK_EQUAL( hello_word_ptr->get_val(), "hello"); + std::cerr << hello_word_ptr->get_val() << " " << std::endl; + + ++current_word; + + std::auto_ptr world_word_ptr(current_word->second.create()); + BOOST_CHECK_EQUAL( !world_word_ptr.get(), 0 ); + + BOOST_CHECK_EQUAL( world_word_ptr->get_val(), "world!"); + std::cerr << world_word_ptr->get_val() << std::endl << std::endl; + } + l.close(); + } + + // replace the loaded library and try to reload + boost::filesystem::remove("libHelloWorldCache.extension"); + boost::filesystem::copy_file("libHelloWorldLibv2.extension", + "libHelloWorldCache.extension"); + + { + // load the second version + shared_library l((std::string("libHelloWorldCache") + + ".extension").c_str()); + BOOST_CHECK_EQUAL( l.open(), true ); + + { + // check if the factory can return the functor + factory_map fm; + void (*load_func)(factory_map &) = l.get("extension_export_word"); + BOOST_CHECK(load_func != 0); + + (*load_func)(fm); + + // check if we can get the word list + std::map > & factory_list = fm.get(); + BOOST_CHECK_EQUAL( factory_list.size(), 2U ); + + // iterate trough the classes and execute get_val method + // to obtain the correct words + std::map >::iterator current_word = + factory_list.begin(); + + std::auto_ptr hello_word_ptr(current_word->second.create()); + BOOST_CHECK_EQUAL( !hello_word_ptr.get(), 0 ); + + BOOST_CHECK_EQUAL( hello_word_ptr->get_val(), "| v2 hello"); + std::cerr << hello_word_ptr->get_val() << " " << std::endl; + + ++current_word; + + std::auto_ptr world_word_ptr(current_word->second.create()); + BOOST_CHECK_EQUAL( !world_word_ptr.get(), 0 ); + + BOOST_CHECK_EQUAL( world_word_ptr->get_val(), "world! v2"); + std::cerr << world_word_ptr->get_val() << " " << std::endl; + } + l.close(); + } + + { + // load the library again and remove it when loaded + shared_library l((std::string("libHelloWorldCache") + + ".extension").c_str()); + BOOST_CHECK_EQUAL( l.open(), true ); + + { + // check if the factory can return the functor + factory_map fm; + void (*load_func)(factory_map &) = l.get("extension_export_word"); + BOOST_CHECK(load_func != 0); + + (*load_func)(fm); + + // check if we can get the word list + std::map > & factory_list = fm.get(); + BOOST_CHECK_EQUAL( factory_list.size(), 2U ); + + // iterate trough the classes and execute get_val method + // to obtain the correct words + std::map >::iterator current_word = + factory_list.begin(); + + std::auto_ptr hello_word_ptr(current_word->second.create()); + BOOST_CHECK_EQUAL( !hello_word_ptr.get(), 0 ); + + BOOST_CHECK_EQUAL( hello_word_ptr->get_val(), "| v2 hello"); + std::cerr << hello_word_ptr->get_val() << " " << std::endl; + + ++current_word; + + boost::filesystem::remove("libHelloWorldCache.extension"); + + std::auto_ptr world_word_ptr(current_word->second.create()); + BOOST_CHECK_EQUAL( !world_word_ptr.get(), 0 ); + + BOOST_CHECK_EQUAL( world_word_ptr->get_val(), "world! v2"); + std::cerr << world_word_ptr->get_val() << " " << std::endl; + + + } + l.close(); + } +} + diff --git a/libs/extension/test/libtutorial_1.extension b/libs/extension/test/libtutorial_1.extension new file mode 100755 index 00000000..0517c2e7 Binary files /dev/null and b/libs/extension/test/libtutorial_1.extension differ diff --git a/libs/extension/test/libtutorial_2.extension b/libs/extension/test/libtutorial_2.extension new file mode 100755 index 00000000..bfd4e099 Binary files /dev/null and b/libs/extension/test/libtutorial_2.extension differ diff --git a/libs/extension/test/mixed_param_test.cpp b/libs/extension/test/mixed_param_test.cpp new file mode 100644 index 00000000..165c39ca --- /dev/null +++ b/libs/extension/test/mixed_param_test.cpp @@ -0,0 +1,66 @@ +/* + * Boost.Extension / construction test case + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include +#define BOOST_TEST_MAIN +#include +#include +#include + +using namespace boost::extensions; + + +class Base { +public: + Base(int i) : value_(i) {} + Base(float j) : value_(int(j)) {} + Base() : value_(23) {} + Base(int i, float j) : value_(int(j) * i) {} + virtual int getValue() {return value_;} + virtual ~Base() {} +private: + int value_; +}; +class Derived : public Base { +public: + virtual ~Derived() {} + Derived(int i) : Base(float(i * 3)) {} + Derived(float j) : Base(int(j)) {} + Derived() : Base(2, 23.0f) {} + Derived(int i, float j) : Base() {} + virtual int getValue() {return 2 * Base::getValue();} +}; + + + + +BOOST_AUTO_TEST_CASE(CorrectConstructor) { + factory_map m; + m.get()["Derived"].set(); + m.get()["Derived"].set(); + m.get()["Derived"].set(); + m.get()["Derived"].set(); + m.get()["Base"].set(); + m.get()["Base"].set(); + m.get()["Base"].set(); + m.get()["Base"].set(); + std::auto_ptr b1(m.get() + ["Derived"].create(2.0f)); + BOOST_CHECK_EQUAL(b1->getValue(), 4); + std::auto_ptr b2(m.get() + ["Derived"].create()); + BOOST_CHECK_EQUAL(b2->getValue(), 92); + std::auto_ptr b3(m.get() + ["Base"].create(4, 5.0f)); + BOOST_CHECK_EQUAL(b3->getValue(), 20.0f); +} \ No newline at end of file diff --git a/libs/extension/test/multiple_inheritance_test.cpp b/libs/extension/test/multiple_inheritance_test.cpp new file mode 100644 index 00000000..a576aaf8 --- /dev/null +++ b/libs/extension/test/multiple_inheritance_test.cpp @@ -0,0 +1,161 @@ +/* + * Boost.Extension / multiple inheritance unit test + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include + +// See the FAQ for info about why the following is necessary +// here, but usually isn't. +#define BOOST_EXTENSION_VEHICLE_DECL BOOST_EXTENSION_IMPORT_DECL +#define BOOST_EXTENSION_COMPUTER_DECL BOOST_EXTENSION_IMPORT_DECL + +#include "../examples/multiple_inheritance/vehicle.hpp" +#include "../examples/multiple_inheritance/computer.hpp" + +#include + +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include + + +using namespace boost::extensions; + +BOOST_AUTO_TEST_CASE(multiple_inheritance_example) +{ + // Create the factory_map object - it will hold all of the available + // constructors. Multiple zones can be constructed. + factory_map twilight; + + // check if each library loads correct. It could be done with a helper + // function but I think that it is clear this way because we are in a + // unit test, and the output for the helper function version will be + // less descriptive + + // check if the libraries can be loaded + shared_library libVehicle("libVehicle.extension"); + shared_library libCar("libCar.extension"); + shared_library libComputer("libComputer.extension"); + shared_library libBoat("libBoat.extension"); + shared_library libFlyingCar("libFlyingCar.extension"); + shared_library libCarOfTheFuture("libCarOfTheFuture.extension"); + shared_library libPlane("libPlane.extension"); + BOOST_CHECK_EQUAL( libVehicle.open(), true ); + BOOST_CHECK_EQUAL( libCar.open(), true ); + BOOST_CHECK_EQUAL( libComputer.open(), true ); + BOOST_CHECK_EQUAL( libBoat.open(), true ); + BOOST_CHECK_EQUAL( libFlyingCar.open(), true ); + BOOST_CHECK_EQUAL( libCarOfTheFuture.open(), true ); + BOOST_CHECK_EQUAL( libPlane.open(), true ); + + // check if the factory can return the functor for each library + void (*load_func_vehicle)(factory_map&) = + libVehicle.get("extension_export"); + void (*load_func_car)(factory_map&) = + libCar.get("extension_export"); + void (*load_func_computer)(factory_map&) = + libComputer.get("extension_export"); + void (*load_func_boat)(factory_map&) = + libBoat.get("extension_export"); + void (*load_func_flyingcar)(factory_map&) = + libFlyingCar.get("extension_export"); + void (*load_func_carofthefuture)(factory_map&) = + libCarOfTheFuture.get("extension_export"); + void (*load_func_plane)(factory_map&) = + libPlane.get("extension_export"); + BOOST_CHECK( load_func_vehicle != 0 ); + BOOST_CHECK( load_func_car != 0 ); + BOOST_CHECK( load_func_computer != 0 ); + BOOST_CHECK( load_func_boat != 0 ); + BOOST_CHECK( load_func_flyingcar != 0 ); + BOOST_CHECK( load_func_carofthefuture != 0 ); + BOOST_CHECK( load_func_plane != 0 ); + load_func_vehicle(twilight); + load_func_car(twilight); + load_func_computer(twilight); + load_func_boat(twilight); + load_func_flyingcar(twilight); + load_func_carofthefuture(twilight); + load_func_plane(twilight); + + + // Computer test: we test if we obtain the computer implementation + std::map > & factory_list = + twilight.get(); + BOOST_CHECK_EQUAL( factory_list.size(), 2U ); + + std::map >::iterator comp = + factory_list.begin(); + + std::auto_ptr computer_ptr(comp->second.create()); + BOOST_CHECK_EQUAL( !computer_ptr.get(), 0 ); + + BOOST_CHECK_EQUAL( comp->first, "\nA computer exported as a computer" ); + BOOST_CHECK_EQUAL( computer_ptr->list_capabilities(), "\nIt computes."); + + // Vehicles test: we test if we obtain the different vehicles implementation + std::map > & factory_list2 = + twilight.get(); + BOOST_CHECK_EQUAL( factory_list2.size(), 6U ); + + std::map >::iterator v = + factory_list2.begin(); + + // boat as a vehicle + std::auto_ptr v3_ptr(v->second.create()); + BOOST_CHECK_EQUAL( v->first, "A boat exported as a vehicle" ); + BOOST_CHECK_EQUAL( v3_ptr->list_capabilities(), "\nIt floats on water." ); + + ++v; + + // car as a vehicle + std::auto_ptr v2_ptr(v->second.create()); + BOOST_CHECK_EQUAL( v->first, "A car exported as a vehicle" ); + BOOST_CHECK_EQUAL( v2_ptr->list_capabilities(), "\nIt travels on roads." ); + + ++v; + + // a car of the future as a vehicle + std::auto_ptr v5_ptr(v->second.create()); + BOOST_CHECK_EQUAL( v->first, "A car of the future exported as " + "a vehicle" ); + BOOST_CHECK_EQUAL( v5_ptr->list_capabilities(), "\nIt floats on water.\n" + "It travels on roads.\nIt flies in the air.\n" + "It takes off from your driveway\nIt computes.\n" + "Costs an arm and a leg" ); + + ++v; + + // flying car as a vehicle + std::auto_ptr v4_ptr(v->second.create()); + BOOST_CHECK_EQUAL( v->first, "A flying car exported as a vehicle"); + BOOST_CHECK_EQUAL( v4_ptr->list_capabilities(), + "\nIt travels on roads.\nIt flies in the air.\n" + "It takes off from your driveway" ); + + ++v; + + // a plane as a vehicle + std::auto_ptr v6_ptr(v->second.create()); + BOOST_CHECK_EQUAL( v->first, "A plane exported as a vehicle" ); + BOOST_CHECK_EQUAL( v6_ptr->list_capabilities(), "\nIt flies in the air."); + + ++v; + + // vehicle as a vehicle + std::auto_ptr v1_ptr(v->second.create()); + BOOST_CHECK_EQUAL( v->first, "A vehicle exported as a vehicle" ); + BOOST_CHECK_EQUAL( v1_ptr->list_capabilities(), + "\nIt is some sort of vehicle." ); + + // all tests done +} diff --git a/libs/extension/test/parameters_test.cpp b/libs/extension/test/parameters_test.cpp new file mode 100644 index 00000000..99338135 --- /dev/null +++ b/libs/extension/test/parameters_test.cpp @@ -0,0 +1,56 @@ +/* + * Boost.Extension / lots of parameters unit test + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include +#include +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include + +#include "../examples/parameters/lots_of_parameters_iface.hpp" + + +using namespace boost::extensions; + +BOOST_AUTO_TEST_CASE(parameters_example) +{ + // check if the library can be loaded + shared_library l("libParametersLib.extension"); + BOOST_CHECK_EQUAL( l.open(), true ); + + // check if the factory can return the functor + factory_map fm; + void (*load_func)(factory_map &) = l.get("extension_export"); + BOOST_CHECK(load_func != 0); + + (*load_func)(fm); + + // check if we can get the parameter list + std::map > > & factory_list = + fm.get >(); + BOOST_CHECK_EQUAL( factory_list.size(), 1U ); + + // get the interface and construct it + std::map > >::iterator params = + factory_list.begin(); + + std::auto_ptr + params_ptr(params->second.create(true, 4, 'c', "test", A(2), + boost::shared_ptr(new A(15)))); + BOOST_CHECK_EQUAL( !params_ptr.get(), 0 ); + +} diff --git a/libs/extension/test/registry_test.cpp b/libs/extension/test/registry_test.cpp new file mode 100644 index 00000000..5c355b90 --- /dev/null +++ b/libs/extension/test/registry_test.cpp @@ -0,0 +1,60 @@ +/* + * Boost.Extension / registry unit test + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include + +#define BOOST_TEST_MAIN +#include + +#include "../examples/word.hpp" + +using namespace boost::extensions; + +BOOST_AUTO_TEST_CASE(registry_construction) +{ + registry reg; + BOOST_CHECK_EQUAL(reg.num_libraries(), size_t(0)); + BOOST_CHECK(reg.open("libRegistryLibrary.extension")); + BOOST_CHECK_EQUAL(reg.num_libraries(), size_t(1)); + BOOST_CHECK(reg.close("libRegistryLibrary.extension")); + BOOST_CHECK_EQUAL(reg.num_libraries(), size_t(0)); +} + +BOOST_AUTO_TEST_CASE(library_closing) +{ // this tests the requirements of the registry example + registry reg; + BOOST_ASSERT(reg.open("libRegistryLibrary.extension")); + { + std::list > & factory_list = + reg.get(); + BOOST_CHECK_EQUAL(factory_list.size(), size_t(1)); + { + std::auto_ptr w(factory_list.begin()->create()); + BOOST_CHECK_EQUAL(std::string(w->get_val()), std::string("First Time")); + BOOST_CHECK_EQUAL(std::string(w->get_val()), std::string("Second Time")); + } + } + BOOST_CHECK(reg.clear()); + BOOST_ASSERT(reg.open("libRegistryLibrary.extension")); + { + std::list > & factory_list = + reg.get(); + BOOST_CHECK_EQUAL(factory_list.size(), size_t(1)); + { + std::auto_ptr w(factory_list.begin()->create()); + BOOST_CHECK_EQUAL(std::string(w->get_val()), std::string("First Time")); + BOOST_CHECK_EQUAL(std::string(w->get_val()), std::string("Second Time")); + } + } + BOOST_CHECK(reg.clear()); + BOOST_CHECK_EQUAL(reg.num_libraries(), size_t(0)); +} diff --git a/libs/extension/test/shared_library_test.cpp b/libs/extension/test/shared_library_test.cpp new file mode 100644 index 00000000..99b7ba6a --- /dev/null +++ b/libs/extension/test/shared_library_test.cpp @@ -0,0 +1,23 @@ +/* + * Boost.Extension / construction test case + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#define BOOST_TEST_MAIN +#include +#include +#include + +using namespace boost::extensions; + +BOOST_AUTO_TEST_CASE(construction) { + boost::extensions::shared_library sl("sl"); +} diff --git a/libs/extension/test/single_param_test.cpp b/libs/extension/test/single_param_test.cpp new file mode 100644 index 00000000..99809b80 --- /dev/null +++ b/libs/extension/test/single_param_test.cpp @@ -0,0 +1,117 @@ +/* + * Boost.Extension / construction test case + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include +#define BOOST_TEST_MAIN +#include +#include +#include + +using namespace boost::extensions; + + +class Automobile { +public: + virtual ~Automobile() {} + Automobile(int speed) : speed_(speed) {} + virtual int getSpeed() {return speed_;} +protected: + int speed_; +}; + +class Van : virtual public Automobile { +public: + virtual ~Van() {} + Van(int speed) : Automobile(speed) {} + virtual int getSpeed() {return speed_ / 2;} +}; + +class PickupTruck : virtual public Automobile { +public: + virtual ~PickupTruck() {} + PickupTruck(int speed) : Automobile(speed) {} + virtual int getSpeed() {return speed_ / 3;} +}; + +class Racecar : virtual public Automobile { +public: + virtual ~Racecar() {} + Racecar(int speed) : Automobile(speed) {} + virtual int getSpeed() {return 3 * speed_;} +}; + +class RacingVan : public Racecar, public Van +{ +public: + virtual ~RacingVan() {} + RacingVan(int speed) : Automobile(speed), + Racecar(speed), Van(speed) {} + virtual int getSpeed() {return 2 * speed_;} +}; + + + + +BOOST_AUTO_TEST_CASE(factory_argless) { + factory f; + f.set(); + BOOST_CHECK(f.is_valid()); + std::auto_ptr pickup(f.create(90)); + BOOST_CHECK_EQUAL(pickup->getSpeed(), 30); +} + +BOOST_AUTO_TEST_CASE(map_argless) +{ + std::map > m; + m["A van"].set(); + m["A basic automobile"].set(); + m["A pickup truck"].set(); + std::auto_ptr van(m["A van"].create(30)); + BOOST_CHECK_EQUAL(van->getSpeed(), 15); + BOOST_CHECK_EQUAL + (m["An unloaded car!"].create(30), + (Automobile*)0); +} +/* +namespace boost { namespace extensions { +template <> +Automobile * create_function::create() +{ + return new Racecar(50); +} +}} // namespace boost::extensions + +BOOST_AUTO_TEST_CASE(factory_template) +{ + factory f; + f.set(); + BOOST_CHECK(f.is_valid()); + std::auto_ptr racecar(f.create(60)); + BOOST_CHECK_EQUAL(racecar->getSpeed(), 180); +}*/ + +BOOST_AUTO_TEST_CASE(factory_map_single_param) +{ + factory_map m; + m.get()["A pickup truck"].set(); + m.get()["A racing van!"].set(); + std::auto_ptr pickup + (m.get()["A pickup truck"].create(60)); + BOOST_CHECK_EQUAL(pickup->getSpeed(), 20); + std::auto_ptr racingVan + (m.get()["A racing van!"].create(60)); + BOOST_CHECK_EQUAL(racingVan->getSpeed(), 120); + std::auto_ptr car_ptr + (m.get()["A nonexistent car!"].create(30)); + BOOST_CHECK_EQUAL(car_ptr.get(), (Automobile*)0); +} diff --git a/libs/extension/test/tutorial_1_bin b/libs/extension/test/tutorial_1_bin new file mode 100755 index 00000000..1be42b33 Binary files /dev/null and b/libs/extension/test/tutorial_1_bin differ diff --git a/libs/extension/test/tutorial_2_bin b/libs/extension/test/tutorial_2_bin new file mode 100755 index 00000000..c556ddbc Binary files /dev/null and b/libs/extension/test/tutorial_2_bin differ diff --git a/libs/extension/test/type_map_test.cpp b/libs/extension/test/type_map_test.cpp new file mode 100644 index 00000000..b1c85d9f --- /dev/null +++ b/libs/extension/test/type_map_test.cpp @@ -0,0 +1,45 @@ +/* + * Boost.Extension / testing of type_map class + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include + + +using namespace boost::extensions; + +BOOST_AUTO_TEST_CASE(intTest) { + type_map m; + int& my_int(m.get()); + my_int = 5; + int& my_second_int(m.get()); + BOOST_CHECK_EQUAL(my_second_int, 5); +} + +BOOST_AUTO_TEST_CASE(intFloatTest) { + type_map m; + int& my_int(m.get()); + my_int = 5; + float& my_float(m.get()); + my_float = 10.0; + BOOST_CHECK_EQUAL(my_int, 5); + BOOST_CHECK_EQUAL(my_float, 10.0); +} + +BOOST_AUTO_TEST_CASE(constTest) { + type_map m; + int& my_int(m.get()); + my_int = 5; + const int& my_const_int(m.get()); + BOOST_CHECK_EQUAL(my_const_int, 5); +} diff --git a/libs/extension/test/versions_test.cpp b/libs/extension/test/versions_test.cpp new file mode 100644 index 00000000..e966b747 --- /dev/null +++ b/libs/extension/test/versions_test.cpp @@ -0,0 +1,106 @@ +/* + * Boost.Extension / implementations versions test + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#include +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include + +#include +#include +#include + +#include "../examples/word.hpp" +#include "../examples/versioning/salute.hpp" + + +BOOST_AUTO_TEST_CASE(versions_test) +{ + using namespace boost::extensions; + + // Create the factory_map object - it will hold all of the available + // constructors. Multiple factory_maps can be constructed. + factory_map fm; + + // load hello world first version + load_single_library(fm, "libHelloWorldLib.extension", + "extension_export_word"); + + // load hello world second version + load_single_library(fm, "libHelloWorldLibv2.extension", + "extension_export_word"); + + // load hello world second version again + load_single_library(fm, "libHelloWorldLibv2.extension", + "extension_export_word"); + + // load salute library (with hello included) + load_single_library(fm, "libSaluteLib.extension", "extension_export_salute"); + + // Get a reference to the list of constructors for words. + std::map > & factory_list = fm.get(); + + // the point here is to check if six classes were loaded + // (two for each one of the first three libraries) + BOOST_CHECK_EQUAL( factory_list.size(), 6U ); + + // these are the expected strings + std::vector words; + words.push_back("hello"); + words.push_back("world!"); + words.push_back("| v2 hello"); + words.push_back("world! v2"); + words.push_back("| v2 hello"); + words.push_back("world! v2"); + + std::vector::const_iterator expected_word = words.begin(); + for (std::map >::iterator current_word = + factory_list.begin(); current_word != factory_list.end(); + ++current_word) + { + /// check that the pointer is OK and the word is the one that + /// we're expecting + std::auto_ptr word_ptr(current_word->second.create()); + BOOST_CHECK_EQUAL( !word_ptr.get(), 0 ); + BOOST_CHECK_EQUAL( word_ptr->get_val(), *expected_word); + ++expected_word; + } + + // Get a reference to the list of constructors for salutes. + std::map > & salute_factory_list = fm.get(); + + // the point here is to check if only two classes were loaded + BOOST_CHECK_EQUAL( salute_factory_list.size(), 2U ); + + // these are the expected strings + std::vector salutes; + salutes.push_back("hello"); + salutes.push_back("bye!"); + + std::vector::const_iterator expected_salute = salutes.begin(); + + for (std::map >::iterator current_salute = + salute_factory_list.begin(); + current_salute != salute_factory_list.end(); ++current_salute) + { + /// check that the pointer is OK and the salute is the one that + /// we're expecting + std::auto_ptr salute_ptr(current_salute->second.create()); + BOOST_CHECK_EQUAL( !salute_ptr.get(), 0 ); + BOOST_CHECK_EQUAL( salute_ptr->say(), *expected_salute); + ++expected_salute; + } + + // all ok +} diff --git a/libs/extension/test/zone_test.cpp b/libs/extension/test/zone_test.cpp new file mode 100644 index 00000000..77506143 --- /dev/null +++ b/libs/extension/test/zone_test.cpp @@ -0,0 +1,75 @@ +/* + * Boost.Extension / zone test + * + * (C) Copyright Jeremy Pack 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include +#include +#define BOOST_TEST_MAIN + +#include + +#include "fruit.hpp" +using namespace boost::extensions; + +BOOST_AUTO_TEST_CASE(add_one) +{ + factory_map z; + BOOST_CHECK_EQUAL((z.get().size()), size_t(0)); + z.get()["A round fruit"].set(); + BOOST_CHECK_EQUAL((z.get().size()), size_t(1)); + BOOST_CHECK_EQUAL((z.get().size()), size_t(0)); +} +BOOST_AUTO_TEST_CASE(add_multiple) +{ + factory_map z; + BOOST_CHECK_EQUAL((z.get().size()), size_t(0)); + z.get()["A round fruit"].set(); + BOOST_CHECK_EQUAL((z.get().size()), size_t(1)); + BOOST_CHECK_EQUAL((z.get().size()), size_t(0)); + z.get()["A fruit that is not round"] + .set(); + BOOST_CHECK_EQUAL((z.get().size()), size_t(2)); + BOOST_CHECK_EQUAL((z.get().size()), size_t(0)); + z.get()["A round fruit"].set(); + BOOST_CHECK_EQUAL((z.get().size()), size_t(2)); + BOOST_CHECK_EQUAL((z.get().size()), size_t(1)); +} +BOOST_AUTO_TEST_CASE(readd) +{ + factory_map z; + BOOST_CHECK_EQUAL((z.get().size()), size_t(0)); + z.get()["A round fruit"].set(); + BOOST_CHECK_EQUAL((z.get().size()), size_t(1)); + BOOST_CHECK_EQUAL((z.get().size()), size_t(0)); + z.get()["A round fruit"].set(); + BOOST_CHECK_EQUAL((z.get().size()), size_t(1)); + BOOST_CHECK_EQUAL((z.get().size()), size_t(0)); + z.get()["A round fruit"].set(); + BOOST_CHECK_EQUAL((z.get().size()), size_t(1)); + BOOST_CHECK_EQUAL((z.get().size()), size_t(1)); + +} +BOOST_AUTO_TEST_CASE(different_base) +{ + factory_map z; + BOOST_CHECK_EQUAL((z.get().size()), + size_t(0)); + z.get()["A round fruit"].set(); + z.get()["A round fruit that isn't very sweet"].set(); + z.get() + [vegetable_info("Tomato", 112)].set(); + BOOST_CHECK_EQUAL((z.get().size()), size_t(2)); + BOOST_CHECK_EQUAL((z.get().size()), + size_t(1)); + BOOST_CHECK_EQUAL((z.get().begin()->first.get_calories()), 112); +} + diff --git a/libs/fiber/build/Jamfile.v2 b/libs/fiber/build/Jamfile.v2 new file mode 100644 index 00000000..9979befa --- /dev/null +++ b/libs/fiber/build/Jamfile.v2 @@ -0,0 +1,31 @@ +# Boost.fiber Library Build Jamfile + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import feature ; +import modules ; +import toolset ; + +project boost/fiber + : source-location ../src + : requirements + ../../context/build//context.hpp + : usage-requirements + shared:BOOST_FIBER_DYN_LINK=1 + ; + +lib boost_fiber + : asym_fiber.cpp + sym_fiber.cpp + detail/asym_fiber_base.cpp + detail/sym_fiber_base.cpp + ../../context/build//boost_context + : shared:BOOST_FIBER_DYN_LINK=1 + : + : shared:../../context/build//boost_context + ; + +boost-install boost_fiber ; diff --git a/libs/fiber/doc/Jamfile.v2 b/libs/fiber/doc/Jamfile.v2 new file mode 100644 index 00000000..6c0d2390 --- /dev/null +++ b/libs/fiber/doc/Jamfile.v2 @@ -0,0 +1,33 @@ +# (C) Copyright 2008 Anthony Williams +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +path-constant boost-images : ../../../doc/src/images ; + +xml fiber : fiber.qbk ; + +boostbook standalone + : + fiber + : + # HTML options first: + # Use graphics not text for navigation: + navig.graphics=1 + # How far down we chunk nested sections, basically all of them: + chunk.section.depth=3 + # Don't put the first section on the same page as the TOC: + chunk.first.sections=1 + # How far down sections get TOC's + toc.section.depth=10 + # Max depth in each TOC: + toc.max.depth=3 + # How far down we go with TOC's + generate.section.toc.level=10 + # Path for links to Boost: + boost.root=../../../.. + # Path for libraries index: + boost.libraries=../../../../libs/libraries.htm + # Use the main Boost stylesheet: + html.stylesheet=../../../../doc/html/boostbook.css + ; diff --git a/libs/fiber/doc/asym_fiber_ref.qbk b/libs/fiber/doc/asym_fiber_ref.qbk new file mode 100644 index 00000000..c855e15b --- /dev/null +++ b/libs/fiber/doc/asym_fiber_ref.qbk @@ -0,0 +1,421 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + +[section:asym_fiber_management Asymmetric Fiber] + +[heading Synopsis] + +Each __asym_fiber__ class represents a user-space context of execution. Objects of +type __asym_fiber__ are characterized by providing three basic operations: __create__ +(constructor), __resume__ (__fiber_run__) and __suspend__ (__fiber_yield__). + +The constructor of __asym_fiber__ accepts a function which acts as the main body +of the fiber. Creating a fiber doesn't start its execution. The fiber begins +in suspended state with its __continuation_point__ set to the first instruction in +its main body. +Invoking __fiber_run__ starts the execution at its saved __continuation_point__ +(activate the fiber) and runs until the fiber gets suspended or returns from fibers +main function. In both cases the control is transfered back to the invoker. +When a fiber returns from its main function the fiber is said to be finished and can +not be futher resumed. +If a fiber gets suspended its __continuation_point__ is saved so that the next time +the fiber is resumed, its execution will continue at the exact point where it was +suspended. + +__asym_fiber__ supports copy- and move-semantics. + + boost::asym_fiber f1; // not-a-fiber + + void fn() + { + boost::asym_fiber f2( some_fn, boost::asym_fiber::default_stacksize); + std::cout << f2.get_id() << std::endl; + f2.run(); + } + + +[note If fibers are migrated between threads the code called by a fiber must not +use thread-local-storage.] + + +[heading Creating] + +A new fiber is launched by passing an object of a callable type that can be +invoked with no parameters to the constructor. The object is then copied into +internal storage, and invoked on the newly-created fiber. If the object must +not (or cannot) be copied, then `boost::ref` can be used to pass in a reference +to the function object. In this case, the user of __boost_fiber__ must ensure +that the referred-to object outlives the newly-created fiber. + + struct callable + { void operator()(); }; + + boost::asym_fiber copies_are_safe() + { + callable x; + return boost::asym_fiber( x); + } // x is destroyed, but the newly-created fiber has a copy, so this is OK + + boost::asym_fiber oops() + { + callable x; + return boost::asym_fiber( boost::ref( x), boost::asym_fiber::default_stacksize); + } // x is destroyed, but the newly-created fiber still has a reference + // this leads to undefined behaviour + +If you wish to construct an instance of __asym_fiber__ with a function or callable +object that requires arguments to be supplied, this can be done by passing +additional arguments to the __asym_fiber__ constructor: + + void find_the_question( int the_answer); + + boost::asym_fiber deep_thought_2( + find_the_question, 42, boost::asym_fiber::default_stacksize); + +The arguments are ['copied] into the internal fiber structure: if a reference +is required, use `boost::ref`, just as for references to callable functions. + +[caution Functions passed to the __asym_fiber__ must not throw exceptions.] + +If the main function of __asym_fiber__ returns the fiber returns to the +__continuation_point__ which has invoked the fiber. If desired the application +will terminate if the last constructor argument is set to `false` and fibers +main function returns. + + +[heading Control Transfer] + +__asym_fiber__ has two operations to transfer the execution between different +fibers. The __continuation_point__ of the fiber ist started/resumed if +__fiber_run__ is invoked. +If the fiber decides to give the execution control back to its caller +__fiber_yield__ must be called. + + // create `my_fiber` which will execute `my_function()` + boost::asym_fiber my_fiber( my_function, boost::asym_fiber:default_stacksize); + + // jump to execution context of `my_fiber` + // run `my_function` + my_fiber.run(); + + // this section will be entered if `my_function()` calls + // `my_fiber.yield()` + + +[heading Fiber IDs] + +Objects of class __asym_fiber_id__ can be used to identify fibers. Each running fiber +has a unique ID obtainable from the corresponding __asym_fiber__ by calling +__asym_get_id__ member function. Objects of class __asym_fiber_id__ can be copied, and +used as keys in associative containers: the full range of comparison operators +is provided. Fiber IDs can also be written to an output stream using the stream +insertion operator, though the output format is unspecified. + +Each instance of __asym_fiber_id__ either refers to some fiber, or __not_a_fiber__. +Instances that refer to __not_a_fiber__ compare equal to each other, but not +equal to any instances that refer to an actual fiber. +The comparison operators on __asym_fiber_id__ yield a total order for every non-equal +fiber ID. + + +[heading Example] + +The example below demonstrates that the function `fn` is executed in the context +of the fiber `f` and the local state of `fn` is preserved (value of `i` in the +for loop). + + boost::asym_fiber f; + + void fn( int n) + { + for ( int i = 0; i < n; ++i) + { + std::cout << i << " iteration" << std::endl; + // jump back to main + // value of i will be preserved + f.yield(); + // if we return from f.yield() + // f.run() was called again + } + } + + int main() + { + f = boost::asym_fiber( fn, 5); + + std::cout << "start" << std::endl; + while ( ! f.finished() ) + f.run(); // jump to context of fn() + std::cout << "finish" << std::endl; + + return EXIT_SUCCESS; + } + + +[section:asym_fiber Class `asym_fiber`] + + #include + + class asym_fiber + { + public: + asym_fiber(); + + template< typename Fn > + explicit asym_fiber( Fn fn, std::size_t stacksize, bool do_return = true); + + template< typename Fn, typename A1, typename A2,... > + asym_fiber( Fn fn, A1 a1, A2 a2,..., std::size_t stacksize, bool do_return = true); + + asym_fiber( asym_fiber const& other); + asym_fiber & operator=( asym_fiber const& other); + + // move support + template< typename Fn > + explicit asym_fiber( Fn && fn, std::size_t stacksize, bool do_return = true); + + asym_fiber( asym_fiber && other); + + asym_fiber & operator=( asym_fiber && other); + + operator unspecified-bool-type() const; + + bool operator!() const; + + void swap( asym_fiber & other); + + id get_id() const; + + bool operator==( asym_fiber const&) const; + bool operator!=( asym_fiber const&) const; + + void run(); + + void yield(); + + bool finished() const; + }; + + void swap( asym_fiber & lhs, asym_fiber & rhs); + + +[section:default_constructor `asym_fiber()`] +[variablelist +[[Effects:] [Constructs a __asym_fiber__ instance that refers to __not_a_fiber__.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:callable_constructor_stack `template< typename Fn > asym_fiber( Fn fn, std::size_t stacksize, bool do_return)`] +[variablelist +[[Effects:] [`fn` is copied into storage managed internally by the fiber, that +copy is invoked on a newly-created fiber. If last argument is +`true` the execution context returns to caller after `fn` finished.]] +[[Postconditions:] [`*this` refers to the newly created fiber.]] +[[Throws:] [__bad_alloc__.]] +] +[endsect] + +[section:multiple_argument_constructor `template< typename Fn, typename A1, typename A2,... > fiber( Fn fn, A1 a1, A2 a2,..., std::size_t stacksize, bool do_return)`] +[variablelist +[[Preconditions:] [`Fn` and each `A`n must by copyable or movable.]] +[[Effects:] [As if `asym_fiber( boost::bind( fn, a1, a2,...) )`. Consequently, +`fn` and each `a`n are copied into internal storage for access by the new fiber. +If last argument is `true` the execution context returns to caller after `fn` +finished.]] +[[Postconditions:] [`*this` refers to the newly created fiber.]] +[[Throws:] [__bad_alloc__]] +[[Note:] [Currently up to nine additional arguments `a1` to `a9` can be +specified in addition to the function `fn`.]] +] +[endsect] + +[section:get_id `asym_fiber::id get_id() const`] +[variablelist +[[Returns:] [If `*this` refers to a fiber, an instance of __asym_fiber_id__ that +represents that fiber. Otherwise returns a default-constructed __asym_fiber_id__.]] +[[Throws:] [Nothing.]] +] + +[endsect] + +[section:run `void run()`] +[variablelist +[[Effects:] [Save current execution context (caller) and jump to execution +context of `*this`.]] +[[Throws:] [__fiber_moved__ if `*this` is __not_a_fiber__.]] +] +[endsect] + +[section:yield `void yield()`] +[variablelist +[[Effects:] [Save execution context of `*this` and jump back to callers +execution context.]] +[[Throws:] [__fiber_moved__ if `*this` is __not_a_fiber__.]] +] +[endsect] + +[section:finished `bool finished() const`] +[variablelist +[[Effects:] [Returns `true` if fiber has finished.]] +[[Throws:] [__fiber_moved__ if `*this` is __not_a_fiber__.]] +] +[endsect] + +[section:unspec_operator `operator unspecified-bool-type() const`] +[variablelist +[[Returns:] [If `*this` refers to a fiber, the function returns true. Otherwise +false.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:not_operator `bool operator!() const`] +[variablelist +[[Returns:] [If `*this` refers not to a fiber, the function returns true. +Otherwise false.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:equals `bool operator==( asym_fiber const& other) const`] +[variablelist +[[Returns:] [`get_id()==other.get_id()`]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:not_equals `bool operator!=( asym_fiber const& other) const`] +[variablelist +[[Returns:] [`get_id()!=other.get_id()`]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:swap `void swap( asym_fiber & other)`] +[variablelist +[[Effects:] [Exchanges the fibers associated with `*this` and `other`, so +`*this` is associated with the fiber associated with `other` prior to the call, +and vice-versa.]] +[[Postconditions:] [`this->get_id()` returns the same value as `other.get_id()` +prior to the call. `other.get_id()` returns the same value as `this->get_id()` +prior to the call.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:non_member_swap Non-member function `swap()`] + + #include + + void swap( asym_fiber & lhs, asym_fiber & rhs); + +[variablelist +[[Effects:] [[link fiber.asym_fiber_management.asym_fiber.swap +`lhs.swap( rhs)`].]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:id Class `boost::asym_fiber::id`] + + #include + + class asym_fiber::id + { + public: + id(); + + bool operator==( id const& y) const; + bool operator!=( id const& y) const; + bool operator<( id const& y) const; + bool operator>( id const& y) const; + bool operator<=( id const& y) const; + bool operator>=( id const& y) const; + + template< typename charT, typename traitsT > + friend std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream & os, id const& x); + }; + +[section:constructor `id()`] +[variablelist +[[Effects:] [Constructs a __asym_fiber_id__ instance that represents +__not_a_fiber__.]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:is_equal `bool operator==( id const& y) const`] +[variablelist +[[Returns:] [`true` if `*this` and `y` both represent the same fiber of +execution, or both represent __not_a_fiber__, `false` otherwise.]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:not_equal `bool operator!=( id const& y) const`] +[variablelist +[[Returns:] [`true` if `*this` and `y` represent different fibers of execution, +or one represents a fiber of execution, and the other represent __not_a_fiber__, +`false` otherwise.]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:less_than `bool operator<( id const& y) const`] +[variablelist +[[Returns:] [`true` if `*this!=y` is `true` and the implementation-defined total +order of __asym_fiber_id__ values places `*this` before `y`, `false` otherwise.]] +[[Throws:] [Nothing]] +[[Note:] [A __asym_fiber_id__ instance representing __not_a_fiber__ will always +compare less than an instance representing a fiber of +execution.]] +] +[endsect] + +[section:greater_than `bool operator>( id const& y) const`] +[variablelist +[[Returns:] [`y<*this`]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:less_than_or_equal `bool operator<=( id const& y) const`] +[variablelist +[[Returns:] [`!(y<*this)`]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:greater_than_or_equal `bool operator>=( id const& y) const`] +[variablelist +[[Returns:] [`!(*this + friend std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream & os, id const& x); + +[variablelist +[[Effects:] [Writes a representation of the __asym_fiber_id__ instance `x` to +the stream `os`, such that the representation of two instances of +__asym_fiber_id__ `a` and `b` is the same if `a==b`, and different if `a!=b`.]] +[[Returns:] [`os`]] +] +[endsect] + +[endsect] + +[endsect] + +[endsect] diff --git a/libs/fiber/doc/fiber.qbk b/libs/fiber/doc/fiber.qbk new file mode 100644 index 00000000..737151c4 --- /dev/null +++ b/libs/fiber/doc/fiber.qbk @@ -0,0 +1,53 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + +[article Fiber + [quickbook 1.4] + [authors [Kowalke, Oliver]] + [copyright 2009 Oliver Kowalke] + [purpose C++ Library providing an symmetric and asymmetric continuation framework] + [category text] + [license + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + [@http://www.boost.org/LICENSE_1_0.txt]) + ] +] + + +[def __boost_context__ [*Boost.Context]] +[def __boost_fiber__ [*Boost.Fiber]] +[def __boost_move__ [*Boost.Move]] + +[def __blocked__ ['blocked]] +[def __continuation_point__ ['continuation point]] +[def __fiber_id__ ['fiber-id]] +[def __not_a_fiber__ ['not-a-fiber]] +[def __create__ ['create]] +[def __resume__ ['resume]] +[def __suspend__ ['suspend]] +[def __yield__ ['yield]] + +[def __asym_get_id__ `boost::asym_fiber::get_id()`] +[def __fiber_run__ `boost::asym_fiber::run()`] +[def __fiber_switch_to__ `boost::sym_fiber::switch_to()`] +[def __fiber_yield__ `boost::asym_fiber::yield()`] +[def __sym_get_id__ `boost::sym_fiber::get_id()`] + +[def __asym_fiber__ `boost::asym_fiber`] +[def __asym_fiber_id__ `boost::asym_fiber::id`] +[def __bad_alloc__ `std::bad_alloc`] +[def __fcontext__ `fcontext_t`] +[def __fiber_moved__ `boost::fibers::fiber_moved`] +[def __sym_fiber__ `boost::sym_fiber`] +[def __sym_fiber_id__ `boost::sym_fiber::id`] +[def __ucontext__ `ucontext_t`] + + +[include overview.qbk] +[include asym_fiber_ref.qbk] +[include sym_fiber_ref.qbk] diff --git a/libs/fiber/doc/overview.qbk b/libs/fiber/doc/overview.qbk new file mode 100644 index 00000000..72d98547 --- /dev/null +++ b/libs/fiber/doc/overview.qbk @@ -0,0 +1,67 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + +[section:overview Overview] + +__boost_fiber__ provides an framework utilizing lightweight threads of execution +- also known as user-space threads, microthreads or fibers. The API contains +classes and functions to manage fibers. + +A fiber is able to store the current execution state, including all registers +and CPU flags, the instruction pointer, and the stack pointer and later restore +this state (on behalf of __boost_context__). The idea is to have multiple +execution paths running on a single thread using a sort of cooperative +scheduling (threads are preemptively scheduled) - the running fiber decides +explicitly when its yields to allow another fiber to run (fiber switching). +Fibers are less expensive than threads because the kernel doesn't know anything +about fibers - no kernel transitions are required for scheduling (done in the +user-space). +A context switch between threads costs usally thousends of CPU cycles on x86 +compared to a fiber switch with few hundreds of cycles. +A fiber can only run on a single thread at any point in time but may be migrated +between threads. Because a thread can run many different fibers during its life +cycle the name ['fiber] was choosen. + +Beside fibers a conceptualy equivalent constructs are coroutines. A coroutine +can be seen as a language-level construct while a fiber is a system-level +construct. + +It is characteristic for a fiber that local data of a fiber persist between +successive calls of the (symmetric or asymetric) control transfer operations. +The __boost_fiber__ framework provides stackfull fiber implementations allowing +to suspend and resume the execution from within nested functions. + +[caution The documentation from __boost_context__ is relevant too.] + +In order to use the classes and functions described here, you can either include +the specific headers specified by the descriptions of each class or function, or +include the master library header: + + #include + +which includes all the other headers in turn. + +Used namespaces is: + + namespace boost::fibers + + +[warning This library is ['not] an official Boost library] + +__boost_fiber__ depends uppon __boost_context__ and +__boost_move__. + + +[heading How to build and install] + +* download the sources from +[@http://www.boost-consulting.com/vault/index.php?directory=Concurrent%20Programming Boost Vault] +* extract the archive into the boost-source directory +* call ['bjam] (take the different build options of __boost_context__ into acount) + + +[endsect] diff --git a/libs/fiber/doc/sym_fiber_ref.qbk b/libs/fiber/doc/sym_fiber_ref.qbk new file mode 100644 index 00000000..781bbfb5 --- /dev/null +++ b/libs/fiber/doc/sym_fiber_ref.qbk @@ -0,0 +1,470 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + +[section:sym_fiber_management Symmetric Fiber] + +[heading Synopsis] + +Each __sym_fiber__ class represents a user-space context of execution. Objects of +type __sym_fiber__ are characterized by providing two basic operations: __create__ +(constructor) and __suspend__ (__fiber_switch_to__). + +The constructor of __sym_fiber__ accepts a function which acts as the main body +of the fiber. Creating a fiber doesn't start its execution. The fiber begins +in suspended state with its __continuation_point__ set to the first instruction in +its main body. +It is characeristic for symmetric fibers to provide a single control-transfer operation +(__fiber_switch_to__) allowing to pass the control explicitly among themself. +The __continuation_point__ of the fiber which initiates the control transfer will be +saved so that the next time the cotnrol is transfered back its execution will continue +at the exact point where it was suspended. +When a fiber returns from its main function the fiber is said to be finished and can +not be futher resumed. If a fibers main function returns and the fiber was not linked +against another one, the application terminates. + +__sym_fiber__ supports copy- and move-semantics. + + boost::sym_fiber f1; // not-a-fiber + + void fn() + { + boost::sym_fiber f2( some_fn, boost::sym_fiber::default_stacksize); + std::cout << f2.get_id() << std::endl; + f2.run(); + } + + +[note If fibers are migrated between threads the code called by a fiber must not +use thread-local-storage.] + + +[heading Creating] + +A new fiber is created by passing an object of a callable type that can be +invoked with no parameters to the constructor. The object is then copied into +internal storage, and invoked on the newly-created fiber. If the object must not +(or cannot) be copied, then `boost::ref` can be used to pass in a reference to +the function object. In this case, the user of __boost_fiber__ must ensure that +the referred-to object outlives the newly-created fiber. + + struct callable + { void operator()(); }; + + boost::sym_fiber copies_are_safe() + { + callable x; + return boost::sym_fiber( x); + } // x is destroyed, but the newly-created fiber has a copy, so this is OK + + boost::sym_fiber oops() + { + callable x; + return boost::sym_fiber( boost::ref( x), boost::sym_fiber::default_stacksize); + } // x is destroyed, but the newly-created fiber still has a reference + // this leads to undefined behaviour + +If you wish to construct an instance of __sym_fiber__ with a function or callable +object that requires arguments to be supplied, this can be done by passing +additional arguments to the __sym_fiber__ constructor: + + void find_the_question( int the_answer); + + boost::sym_fiber deep_thought_2( + find_the_question, 42, boost::sym_fiber::default_stacksize); + +The arguments are ['copied] into the internal fiber structure: if a reference +is required, use `boost::ref`, just as for references to callable functions. + +[caution Functions passed to the __sym_fiber__ must not throw exceptions.] + + +[heading Control Transfer] + +__sym_fiber__ provides only one operation to transfer the execution control +between different fibers. This allows the symmetric fibers to transfer the +execution control among themselfs (chain of fiber invocations). The +__continuation_point__ of the `other` fiber is started/resumed if +__fiber_switch_to__ is called with `other` as its argument. + + // create `current_fiber` which represent the current execution context + boost::sym_fiber current_fiber( boost::sym_fiber::from_current-context() ); + + // create `my_fiber` which will execute `my_function()` + boost::sym_fiber my_fiber( my_function, boost::sym_fiber:default_stacksize); + + // jump to execution context of `my_fiber` + // run `my_function` + current_fiber.switch_to( my_fiber); + + // this section will be entered if `my_function()` calls + // `my_fiber.switch_to( current_fiber)` + + +[heading Linking against other Fiber] + +If the main function of a symmetric fiber returns the application terminates. +If desired it is possible to link to another fiber which will be executed after +a fiber is finished. For this purpose the constructor takes a __sym_fiber__ as +last argument. + + // create `current_fiber` which represent the current execution context + boost::sym_fiber current_fiber( boost::sym_fiber::from_current-context() ); + + // create `my_fiber` which will execute `my_function()` and link + // against `current_fiber` which will be resumed after `my_function()` returns + boost::sym_fiber my_fiber( my_function, boost::sym_fiber:default_stacksize, current_fiber); + + // jump to execution context of `my_fiber` + // run `my_function` + current_fiber.switch_to( my_fiber); + + // this section will be entered if `my_function()` returns + ... + +[heading Fiber IDs] + +Objects of class __sym_fiber_id__ can be used to identify fibers. Each running fiber +has a unique ID obtainable from the corresponding __sym_fiber__ by calling +__sym_get_id__ member function. Objects of class __sym_fiber_id__ can be copied, and +used as keys in associative containers: the full range of comparison operators +is provided. Fiber IDs can also be written to an output stream using the stream +insertion operator, though the output format is unspecified. + +Each instance of __sym_fiber_id__ either refers to some fiber, or __not_a_fiber__. +Instances that refer to __not_a_fiber__ compare equal to each other, but not +equal to any instances that refer to an actual fiber. +The comparison operators on __sym_fiber_id__ yield a total order for every non-equal +fiber ID. + + +[heading Example] + +The example below demonstrates that the function `fn` is executed in the context +of the fiber `f` and the local state of `fn` is preserved (value of `i` in the +for loop). + + boost::sym_fiber f, current; + + void fn( int n) + { + for ( int i = 0; i < n; ++i) + { + std::cout << i << " iteration" << std::endl; + // jump back to main + // value of i will be preserved + f.switch_to( current); + // if we return from f.switch_to( current) + // current.switch_to( f) was called again + } + } + + int main() + { + current = boost::sym_fiber::from_current_context(); + f = boost::sym_fiber( fn, 5, current); + + std::cout << "start" << std::endl; + while ( ! f.finished() ) + current.switch_to( f); // jump to context of fn() + std::cout << "finish" << std::endl; + + return EXIT_SUCCESS; + } + + +[section:sym_fiber Class `sym_fiber`] + + #include + + class sym_fiber + { + public: + static sym_fiber from_current_cotnext(); + + sym_fiber(); + + template< typename Fn > + explicit sym_fiber( Fn fn, std::size_t stacksize); + + template< typename Fn > + explicit sym_fiber( Fn fn, std::size_t stacksize, sym_fiber & nxt); + + template< typename Fn, typename A1, typename A2,... > + sym_fiber( Fn fn, A1 a1, A2 a2,..., std::size_t stacksize); + + template< typename Fn, typename A1, typename A2,... > + sym_fiber( Fn fn, A1 a1, A2 a2,..., std::size_t stacksize, sym_fiber & nxt); + + sym_fiber( sym_fiber const& other); + sym_fiber & operator=( sym_fiber const& other); + + // move support + template< typename Fn > + explicit sym_fiber( Fn && fn, std::size_t stacksize); + + template< typename Fn > + explicit sym_fiber( Fn && fn, std::size_t stacksize, sym_fiber & nxt); + + sym_fiber( sym_fiber && other); + + sym_fiber & operator=( sym_fiber && other); + + operator unspecified-bool-type() const; + + bool operator!() const; + + void swap( sym_fiber & other); + + id get_id() const; + + bool operator==( sym_fiber const&) const; + bool operator!=( sym_fiber const&) const; + + void switch_to( sym_fiber & other); + + bool finished() const; + }; + + void swap( sym_fiber & lhs, sym_fiber & rhs); + + +[section:create `static sym_fiber from_current_context()`] +[variablelist +[[Effects:] [Creates a fiber attached to current execution context. +The fiber doesn't manage the stack.]] +[[Postconditions:] [`*this` refers to the newly created fiber.]] +[[Throws:] [__bad_alloc__.]] +] +[endsect] + +[section:default_constructor `sym_fiber()`] +[variablelist +[[Effects:] [Constructs a __sym_fiber__ instance that refers to __not_a_fiber__.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:callable_constructor_stack1 `template< typename Fn > sym_fiber( Fn fn, std::size_t stacksize)`] +[variablelist +[[Effects:] [`fn` is copied into storage managed internally by the fiber, that +copy is invoked on a newly-created fiber.]] +[[Postconditions:] [`*this` refers to the newly created fiber.]] +[[Throws:] [__bad_alloc__.]] +] +[endsect] + +[section:callable_constructor_stack2 `template< typename Fn > sym_fiber( Fn fn, std::size_t stacksize, sym_fiber & other)`] +[variablelist +[[Effects:] [`fn` is copied into storage managed internally by the fiber, that +copy is invoked on a newly-created fiber. If `fn` returns `nxt` will be invoked.]] +[[Postconditions:] [`*this` refers to the newly created fiber.]] +[[Throws:] [__bad_alloc__.]] +] +[endsect] + +[section:multiple_argument_constructor1 `template< typename Fn, typename A1, typename A2,... > fiber( Fn fn, A1 a1, A2 a2,..., std::size_t stacksize)`] +[variablelist +[[Preconditions:] [`Fn` and each `A`n must by copyable or movable.]] +[[Effects:] [As if `sym_fiber( boost::bind( fn, a1, a2,...) )`. Consequently, +`fn` and each `a`n are copied into internal storage for access by the new fiber.]] +[[Postconditions:] [`*this` refers to the newly created fiber.]] +[[Throws:] [__bad_alloc__]] +[[Note:] [Currently up to nine additional arguments `a1` to `a9` can be +specified in addition to the function `fn`.]] +] +[endsect] + +[section:multiple_argument_constructor2 `template< typename Fn, typename A1, typename A2,... > fiber( Fn fn, A1 a1, A2 a2,..., std::size_t stacksize, sym_fiber & nxt)`] +[variablelist +[[Preconditions:] [`Fn` and each `A`n must by copyable or movable.]] +[[Effects:] [As if `sym_fiber( boost::bind( fn, a1, a2,...) )`. Consequently, +`fn` and each `a`n are copied into internal storage for access by the new fiber. +If `fn` returns `nxt` will be invoked automatically.]] +[[Postconditions:] [`*this` refers to the newly created fiber.]] +[[Throws:] [__bad_alloc__]] +[[Note:] [Currently up to nine additional arguments `a1` to `a9` can be +specified in addition to the function `fn`.]] +] +[endsect] + +[section:get_id `sym_fiber::id get_id() const`] +[variablelist +[[Returns:] [If `*this` refers to a fiber, an instance of __sym_fiber_id__ that +represents that fiber. Otherwise returns a default-constructed __sym_fiber_id__.]] +[[Throws:] [Nothing.]] +] + +[endsect] + +[section:switch_to `void switch_to( sym_fiber& other)`] +[variablelist +[[Effects:] [Save current execution context and transfer execution control to +`other`.]] +[[Throws:] [__fiber_moved__ if `*this` or `other` are __not_a_fiber__.]] +] +[endsect] + +[section:finished `bool finished() const`] +[variablelist +[[Effects:] [Returns `true` if fiber has finished.]] +[[Throws:] [__fiber_moved__ if `*this` is __not_a_fiber__.]] +] +[endsect] + +[section:unspec_operator `operator unspecified-bool-type() const`] +[variablelist +[[Returns:] [If `*this` refers to a fiber, the function returns true. Otherwise +false.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:not_operator `bool operator!() const`] +[variablelist +[[Returns:] [If `*this` refers not to a fiber, the function returns true. +Otherwise false.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:equals `bool operator==( sym_fiber const& other) const`] +[variablelist +[[Returns:] [`get_id()==other.get_id()`]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:not_equals `bool operator!=( sym_fiber const& other) const`] +[variablelist +[[Returns:] [`get_id()!=other.get_id()`]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:swap `void swap( sym_fiber & other)`] +[variablelist +[[Effects:] [Exchanges the fibers associated with `*this` and `other`, so +`*this` is associated with the fiber associated with `other` prior to the call, +and vice-versa.]] +[[Postconditions:] [`this->get_id()` returns the same value as `other.get_id()` +prior to the call. `other.get_id()` returns the same value as `this->get_id()` +prior to the call.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:non_member_swap Non-member function `swap()`] + + #include + + void swap( sym_fiber & lhs, sym_fiber & rhs); + +[variablelist +[[Effects:] [[link fiber.sym_fiber_management.sym_fiber.swap +`lhs.swap( rhs)`].]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:id Class `boost::sym_fiber::id`] + + #include + + class sym_fiber::id + { + public: + id(); + + bool operator==( id const& y) const; + bool operator!=( id const& y) const; + bool operator<( id const& y) const; + bool operator>( id const& y) const; + bool operator<=( id const& y) const; + bool operator>=( id const& y) const; + + template< typename charT, typename traitsT > + friend std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream & os, id const& x); + }; + +[section:constructor `id()`] +[variablelist +[[Effects:] [Constructs a __sym_fiber_id__ instance that represents +__not_a_fiber__.]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:is_equal `bool operator==( id const& y) const`] +[variablelist +[[Returns:] [`true` if `*this` and `y` both represent the same fiber of +execution, or both represent __not_a_fiber__, `false` otherwise.]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:not_equal `bool operator!=( id const& y) const`] +[variablelist +[[Returns:] [`true` if `*this` and `y` represent different fibers of execution, +or one represents a fiber of execution, and the other represent __not_a_fiber__, +`false` otherwise.]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:less_than `bool operator<( id const& y) const`] +[variablelist +[[Returns:] [`true` if `*this!=y` is `true` and the implementation-defined total +order of __sym_fiber_id__ values places `*this` before `y`, `false` otherwise.]] +[[Throws:] [Nothing]] +[[Note:] [A __sym_fiber_id__ instance representing __not_a_fiber__ will alws +compare less than an instance representing a fiber of +execution.]] +] +[endsect] + +[section:greater_than `bool operator>( id const& y) const`] +[variablelist +[[Returns:] [`y<*this`]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:less_than_or_equal `bool operator<=( id const& y) const`] +[variablelist +[[Returns:] [`!(y<*this)`]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:greater_than_or_equal `bool operator>=( id const& y) const`] +[variablelist +[[Returns:] [`!(*this + friend std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream & os, id const& x); + +[variablelist +[[Effects:] [Writes a representation of the __sym_fiber_id__ instance `x` to +the stream `os`, such that the representation of two instances of +__sym_fiber_id__ `a` and `b` is the same if `a==b`, and different if `a!=b`.]] +[[Returns:] [`os`]] +] +[endsect] + +[endsect] + +[endsect] + +[endsect] diff --git a/libs/fiber/examples/Jamfile.v2 b/libs/fiber/examples/Jamfile.v2 new file mode 100644 index 00000000..bd5eca6c --- /dev/null +++ b/libs/fiber/examples/Jamfile.v2 @@ -0,0 +1,23 @@ +# Boost.Fiber Library Examples Jamfile + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For more information, see http://www.boost.org/ + +project boost/fiber/example + : requirements + ../../context/build//context.hpp + ../build//boost_fiber + /boost/context//boost_context + /boost/thread//boost_thread + static + multi + ; + +exe asym_fiber : asym_fiber.cpp ; +exe asym_fiber_mt : asym_fiber_mt.cpp ; +exe sym_fiber : sym_fiber.cpp ; +exe sym_fiber_mt : sym_fiber_mt.cpp ; diff --git a/libs/fiber/examples/asym_fiber.cpp b/libs/fiber/examples/asym_fiber.cpp new file mode 100644 index 00000000..301262d3 --- /dev/null +++ b/libs/fiber/examples/asym_fiber.cpp @@ -0,0 +1,47 @@ +#include +#include +#include + +#include + +#include + +int value = 0; +boost::asym_fiber gf; + +inline +void fn( std::string const& str, int n) +{ + for ( int i = 0; i < n; ++i) + { + std::cout << "asymmetric fiber " << gf.get_id() << ": increment value from " << value << " to "; + ++value; + std::cout << value << std::endl; + gf.yield(); + } +} + +int main() +{ + try + { + int n = 5; + gf = boost::asym_fiber( fn, "abc", n, boost::asym_fiber::default_stacksize); + std::cout << "start" << std::endl; + + while ( ! gf.finished() ) + { + gf.run(); + std::cout << value << " iteration" << std::endl; + } + + std::cout << "finish" << std::endl; + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/fiber/examples/asym_fiber_mt.cpp b/libs/fiber/examples/asym_fiber_mt.cpp new file mode 100644 index 00000000..4cc560da --- /dev/null +++ b/libs/fiber/examples/asym_fiber_mt.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include + +#include +#include + +#include + +int value = 0; +boost::asym_fiber gf; + +void increment_value_fn() +{ + while ( true) + { + std::stringstream ss; + ss << boost::this_thread::get_id(); + std::cout << "thread " << ss.str() << ": increment value from " << value << " to "; + ++value; + std::cout << value << std::endl; + gf.yield(); + } +} + +void increment( int k, int n) +{ + for ( int i = k; i < n; ++i) + gf.run(); +} + +void fn_first( int k, int n, boost::barrier & b) +{ + std::stringstream ss; + ss << boost::this_thread::get_id(); + std::cout << "thread " << ss.str() << " executes fiber " << gf.get_id() << std::endl; + increment( k, n); + b.wait(); +} + +void fn_last( int k, int n, boost::barrier & b) +{ + b.wait(); + std::stringstream ss; + ss << boost::this_thread::get_id(); + std::cout << "thread " << ss.str() << " executes fiber " << gf.get_id() << std::endl; + increment( k, n); +} + +int main() +{ + try + { + std::cout << "start" << std::endl; + + value = 0; + + gf = boost::asym_fiber( increment_value_fn, boost::asym_fiber::default_stacksize); + + boost::barrier b( 2); + boost::thread t1( fn_first, 0, 5, boost::ref( b) ); + boost::thread t2( fn_last, 5, 8, boost::ref( b) ); + t1.join(); + t2.join(); + + std::cout << "finish" << std::endl; + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/fiber/examples/sym_fiber.cpp b/libs/fiber/examples/sym_fiber.cpp new file mode 100644 index 00000000..65730a65 --- /dev/null +++ b/libs/fiber/examples/sym_fiber.cpp @@ -0,0 +1,49 @@ +#include +#include +#include + +#include + +#include + +int value = 0; +boost::sym_fiber gf1; +boost::sym_fiber gf2; + +inline +void fn( std::string const& str, int n) +{ + for ( int i = 0; i < n; ++i) + { + std::cout << "symmetric fiber " << gf2.get_id() << ": increment value from " << value << " to "; + ++value; + std::cout << value << std::endl; + gf2.switch_to( gf1); + } +} + +int main() +{ + try + { + int n = 5; + gf1 = boost::sym_fiber::from_current_context(); + gf2 = boost::sym_fiber( fn, "abc", n, boost::sym_fiber::default_stacksize, gf1); + std::cout << "start" << std::endl; + + while ( ! gf2.finished() ) + { + gf1.switch_to( gf2); + std::cout << value << " iteration" << std::endl; + } + + std::cout << "finish" << std::endl; + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/fiber/examples/sym_fiber_mt.cpp b/libs/fiber/examples/sym_fiber_mt.cpp new file mode 100644 index 00000000..0a49fffc --- /dev/null +++ b/libs/fiber/examples/sym_fiber_mt.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include + +#include +#include + +#include + +int value = 0; +boost::sym_fiber gf1; +boost::sym_fiber gf2; + +void increment_value_fn() +{ + while ( true) + { + std::stringstream ss; + ss << boost::this_thread::get_id(); + std::cout << "thread " << ss.str() << ": increment value from " << value << " to "; + ++value; + std::cout << value << std::endl; + gf2.switch_to( gf1); + } +} + +void increment( int k, int n) +{ + gf1 = boost::sym_fiber::from_current_context(); + for ( int i = k; i < n; ++i) + gf1.switch_to( gf2); +} + +void fn_first( int k, int n, boost::barrier & b) +{ + std::stringstream ss; + ss << boost::this_thread::get_id(); + std::cout << "thread " << ss.str() << " executes fiber " << gf2.get_id() << std::endl; + increment( k, n); + b.wait(); +} + +void fn_last( int k, int n, boost::barrier & b) +{ + b.wait(); + std::stringstream ss; + ss << boost::this_thread::get_id(); + std::cout << "thread " << ss.str() << " executes fiber " << gf2.get_id() << std::endl; + increment( k, n); +} + +int main() +{ + try + { + std::cout << "start" << std::endl; + + value = 0; + + gf2 = boost::sym_fiber( increment_value_fn, boost::sym_fiber::default_stacksize); + + boost::barrier b( 2); + boost::thread t1( fn_first, 0, 5, boost::ref( b) ); + boost::thread t2( fn_last, 5, 8, boost::ref( b) ); + t1.join(); + t2.join(); + + std::cout << "finish" << std::endl; + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/fiber/src/asym_fiber.cpp b/libs/fiber/src/asym_fiber.cpp new file mode 100644 index 00000000..f51ad61d --- /dev/null +++ b/libs/fiber/src/asym_fiber.cpp @@ -0,0 +1,108 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_FIBER_SOURCE + +#include + +#include + +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +#if defined(BOOST_WINDOWS) +std::size_t asym_fiber::default_stacksize = 65536; +#else +std::size_t asym_fiber::default_stacksize = SIGSTKSZ; +#endif + +asym_fiber::asym_fiber() : + impl_() +{} + +asym_fiber::asym_fiber( asym_fiber const& other) : + impl_( other.impl_) +{} + +asym_fiber & +asym_fiber::operator=( BOOST_COPY_ASSIGN_REF( asym_fiber) other) +{ + asym_fiber tmp( other); + swap( tmp); + return * this; +} + +asym_fiber::asym_fiber( BOOST_RV_REF( asym_fiber) other) : + impl_() +{ swap( other); } + +asym_fiber & +asym_fiber::operator=( BOOST_RV_REF( asym_fiber) other) +{ + asym_fiber tmp( boost::move( other) ); + swap( tmp); + return * this; +} + +asym_fiber::operator unspecified_bool_type() const +{ return impl_; } + +bool +asym_fiber::operator!() const +{ return ! impl_; } + +bool +asym_fiber::operator==( asym_fiber const& other) const +{ return get_id() == other.get_id(); } + +bool +asym_fiber::operator!=( asym_fiber const& other) const +{ return !( get_id() == other.get_id() ); } + +void +asym_fiber::swap( asym_fiber & other) +{ impl_.swap( other.impl_); } + +asym_fiber::id +asym_fiber::get_id() const +{ return asym_fiber::id( impl_); } + +void +asym_fiber::run() +{ + if ( ! impl_) throw fiber_moved(); + BOOST_ASSERT( ! impl_->get_finished() && "fiber already finished"); + impl_->run(); +} + +void +asym_fiber::yield() +{ + if ( ! impl_) throw fiber_moved(); + BOOST_ASSERT( ! impl_->get_finished() && "fiber already finished"); + impl_->yield(); +} + +bool +asym_fiber::finished() const +{ + if ( ! impl_) throw fiber_moved(); + return impl_->get_finished(); +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/libs/fiber/src/detail/asym_fiber_base.cpp b/libs/fiber/src/detail/asym_fiber_base.cpp new file mode 100644 index 00000000..2076f434 --- /dev/null +++ b/libs/fiber/src/detail/asym_fiber_base.cpp @@ -0,0 +1,89 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_FIBER_SOURCE + +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4355) +# endif + +namespace { + +boost::protected_stack create_stack( std::size_t stacksize) +{ return boost::protected_stack( stacksize); } + +} + +namespace boost { +namespace fibers { +namespace detail { + +BOOST_FIBER_DECL void trampoline_asym( void * vp) +{ + BOOST_ASSERT( vp); + detail::asym_fiber_base * self( + static_cast< detail::asym_fiber_base * >( vp) ); + try + { + self->exec(); + self->set_finished(); + } + catch (...) + { BOOST_ASSERT( false && "exeception from fiber-function"); } +} + +asym_fiber_base::do_not_return_t asym_fiber_base::do_not_return; +asym_fiber_base::do_return_t asym_fiber_base::do_return; + +asym_fiber_base::asym_fiber_base( std::size_t stacksize, do_not_return_t) : + use_count_( 0), + finished_( false), + caller_(), + callee_( trampoline_asym, this, create_stack( stacksize) ) +{} + +asym_fiber_base::asym_fiber_base( std::size_t stacksize, do_return_t) : + use_count_( 0), + finished_( false), + caller_(), + callee_( trampoline_asym, caller_, this, create_stack( stacksize) ) +{} + +void +asym_fiber_base::run() +{ caller_.jump_to( callee_); } + +void +asym_fiber_base::yield() +{ callee_.jump_to( caller_); } + +void +asym_fiber_base::set_finished() +{ finished_ = true; } + +bool +asym_fiber_base::get_finished() const +{ return finished_; } + +}}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/libs/fiber/src/detail/sym_fiber_base.cpp b/libs/fiber/src/detail/sym_fiber_base.cpp new file mode 100644 index 00000000..e58ad9f7 --- /dev/null +++ b/libs/fiber/src/detail/sym_fiber_base.cpp @@ -0,0 +1,86 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_FIBER_SOURCE + +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4355) +# endif + +namespace { + +boost::protected_stack create_stack( std::size_t stacksize) +{ return boost::protected_stack( stacksize); } + +} + +namespace boost { +namespace fibers { +namespace detail { + +BOOST_FIBER_DECL void trampoline_sym( void * vp) +{ + BOOST_ASSERT( vp); + detail::sym_fiber_base * self( + static_cast< detail::sym_fiber_base * >( vp) ); + try + { + self->exec(); + self->set_finished(); + } + catch (...) + { BOOST_ASSERT( false && "exeception from fiber-function"); } +} + +sym_fiber_base::sym_fiber_base() : + use_count_( 0), + finished_( false), + ctx_() +{} + +sym_fiber_base::sym_fiber_base( std::size_t stacksize) : + use_count_( 0), + finished_( false), + ctx_( trampoline_sym, this, create_stack( stacksize) ) +{} + +sym_fiber_base::sym_fiber_base( std::size_t stacksize, sym_fiber_base & nxt) : + use_count_( 0), + finished_( false), + ctx_( trampoline_sym, nxt.ctx_, this, create_stack( stacksize) ) +{} + +void +sym_fiber_base::switch_to( sym_fiber_base & other) +{ ctx_.jump_to( other.ctx_); } + +void +sym_fiber_base::set_finished() +{ finished_ = true; } + +bool +sym_fiber_base::get_finished() const +{ return finished_; } + +}}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/libs/fiber/src/sym_fiber.cpp b/libs/fiber/src/sym_fiber.cpp new file mode 100644 index 00000000..6a2269d2 --- /dev/null +++ b/libs/fiber/src/sym_fiber.cpp @@ -0,0 +1,109 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_FIBER_SOURCE + +#include + +#include + +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +#if defined(BOOST_WINDOWS) +std::size_t sym_fiber::default_stacksize = 65536; +#else +std::size_t sym_fiber::default_stacksize = SIGSTKSZ; +#endif + +sym_fiber +sym_fiber::from_current_context() +{ return sym_fiber( new detail::sym_fiber_dummy() ); } + +sym_fiber::sym_fiber() : + impl_() +{} + +sym_fiber::sym_fiber( detail::sym_fiber_base::ptr const& impl) : + impl_( impl) +{} + +sym_fiber::sym_fiber( sym_fiber const& other) : + impl_( other.impl_) +{} + +sym_fiber & +sym_fiber::operator=( BOOST_COPY_ASSIGN_REF( sym_fiber) other) +{ + sym_fiber tmp( other); + swap( tmp); + return * this; +} + +sym_fiber::sym_fiber( BOOST_RV_REF( sym_fiber) other) : + impl_() +{ swap( other); } + +sym_fiber & +sym_fiber::operator=( BOOST_RV_REF( sym_fiber) other) +{ + sym_fiber tmp( boost::move( other) ); + swap( tmp); + return * this; +} + +sym_fiber::operator unspecified_bool_type() const +{ return impl_; } + +bool +sym_fiber::operator!() const +{ return ! impl_; } + +bool +sym_fiber::operator==( sym_fiber const& other) const +{ return get_id() == other.get_id(); } + +bool +sym_fiber::operator!=( sym_fiber const& other) const +{ return !( get_id() == other.get_id() ); } + +void +sym_fiber::swap( sym_fiber & other) +{ impl_.swap( other.impl_); } + +sym_fiber::id +sym_fiber::get_id() const +{ return sym_fiber::id( impl_); } + +void +sym_fiber::switch_to( sym_fiber & other) +{ + if ( ! impl_ || ! other) throw fiber_moved(); + BOOST_ASSERT( ! impl_->get_finished() && "fiber already finished"); + BOOST_ASSERT( ! other.finished() && "fiber already finished"); + impl_->switch_to( * other.impl_); +} + +bool +sym_fiber::finished() const +{ + if ( ! impl_) throw fiber_moved(); + return impl_->get_finished(); +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/libs/fiber/test/Jamfile.v2 b/libs/fiber/test/Jamfile.v2 new file mode 100644 index 00000000..88de8e5c --- /dev/null +++ b/libs/fiber/test/Jamfile.v2 @@ -0,0 +1,31 @@ +# Boost.fiber Library Tests Jamfile + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import testing ; + +project boost/fiber/test + : requirements + ../../context/build//context.hpp + ../../test/build//boost_unit_test_framework + /boost/context//boost_context + /boost/fiber//boost_fiber + /boost/thread//boost_thread + static + multi + ; + +rule test ( source ) +{ + return + [ run $(source).cpp ] + ; +} + +test-suite fiber : + [ test test_asym_fiber ] + [ test test_sym_fiber ] + ; diff --git a/libs/fiber/test/test_asym_fiber.cpp b/libs/fiber/test/test_asym_fiber.cpp new file mode 100644 index 00000000..41af6523 --- /dev/null +++ b/libs/fiber/test/test_asym_fiber.cpp @@ -0,0 +1,228 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include +#include +#include +#include + +#include + +int value; + +boost::asym_fiber gf; + +void zero_args_fn() +{} + +void one_args_fn( int) +{} + +void two_args_fn( int, std::string const&) +{} + +void set_value_fn( int i) +{ value = i; } + +void increment_value_fn() +{ + while ( true) + { + ++value; + gf.yield(); + } +} + +struct X +{ + void operator()() + {} +}; + +struct Y +{ + void operator()( int i) + {} +}; + +void increment( int k, int n) +{ + for ( int i = k; i < n; ++i) + { + gf.run(); + BOOST_CHECK_EQUAL( i + 1, value); + } +} + +void fn_first( int k, int n, boost::barrier & b) +{ + increment( k, n); + b.wait(); +} + +void fn_last( int k, int n, boost::barrier & b) +{ + b.wait(); + increment( k, n); +} + +void test_case_1() +{ + X x; + boost::asym_fiber f1( & X::operator(), x, boost::asym_fiber::default_stacksize); + BOOST_CHECK( f1); + + Y y; + boost::asym_fiber f2( & Y::operator(), y, 7, boost::asym_fiber::default_stacksize); + BOOST_CHECK( f2); +} + +void test_case_2() +{ + boost::asym_fiber f1( one_args_fn, 10, boost::asym_fiber::default_stacksize); + boost::asym_fiber f2; + BOOST_CHECK( f1); + BOOST_CHECK( ! f2); +} + +void test_case_3() +{ + boost::asym_fiber f1( zero_args_fn, boost::asym_fiber::default_stacksize); + boost::asym_fiber f2( one_args_fn, 1, boost::asym_fiber::default_stacksize); + boost::asym_fiber f3( two_args_fn, 1, "abc", boost::asym_fiber::default_stacksize); +} + +void test_case_4() +{ + boost::asym_fiber f1( zero_args_fn, boost::asym_fiber::default_stacksize); + f1 = boost::asym_fiber( zero_args_fn, boost::asym_fiber::default_stacksize); + boost::asym_fiber f2( one_args_fn, 1, boost::asym_fiber::default_stacksize); + f2 = boost::asym_fiber( one_args_fn, 1, boost::asym_fiber::default_stacksize); +} + +void test_case_5() +{ + boost::asym_fiber f1( one_args_fn, 10, boost::asym_fiber::default_stacksize); + BOOST_CHECK( f1); + boost::asym_fiber f2( boost::move( f1) ); + BOOST_CHECK( ! f1); + BOOST_CHECK( f2); + boost::asym_fiber f3; + BOOST_CHECK( ! f3); + f3 = f2; + BOOST_CHECK( f3); + BOOST_CHECK_EQUAL( f2, f3); +} + +void test_case_6() +{ + boost::asym_fiber f1( zero_args_fn, boost::asym_fiber::default_stacksize); + boost::asym_fiber f2( zero_args_fn, boost::asym_fiber::default_stacksize); + boost::asym_fiber f3; + BOOST_CHECK( f1); + BOOST_CHECK( f2); + BOOST_CHECK( ! f3); + + BOOST_CHECK( f1 != f2); + BOOST_CHECK( f1 != f3); + BOOST_CHECK( f2 != f3); + + std::ostringstream os1; + os1 << f1.get_id(); + std::ostringstream os2; + os2 << f2.get_id(); + std::ostringstream os3; + os3 << f3.get_id(); + + std::string not_a_fiber("{not-a-fiber}"); + BOOST_CHECK( os1.str() != os2.str() ); + BOOST_CHECK( os1.str() != os3.str() ); + BOOST_CHECK( os2.str() != os3.str() ); + BOOST_CHECK( os1.str() != not_a_fiber); + BOOST_CHECK( os2.str() != not_a_fiber); + BOOST_CHECK( os3.str() == not_a_fiber); +} + +void test_case_7() +{ + boost::asym_fiber f1( zero_args_fn, boost::asym_fiber::default_stacksize); + boost::asym_fiber f2( zero_args_fn, boost::asym_fiber::default_stacksize); + + boost::asym_fiber::id id1 = f1.get_id(); + boost::asym_fiber::id id2 = f2.get_id(); + + f1.swap( f2); + + BOOST_CHECK_EQUAL( f1.get_id(), id2); + BOOST_CHECK_EQUAL( f2.get_id(), id1); +} + +void test_case_8() +{ + value = 0; + + boost::asym_fiber f( set_value_fn, 7, boost::asym_fiber::default_stacksize); + + BOOST_CHECK_EQUAL( 0, value); + + f.run(); + + BOOST_CHECK_EQUAL( 7, value); +} + +void test_case_9() +{ + value = 0; + + gf = boost::asym_fiber( increment_value_fn, boost::asym_fiber::default_stacksize); + + BOOST_CHECK_EQUAL( 0, value); + + for ( int i = 0; i < 7; ++i) + { + gf.run(); + BOOST_CHECK_EQUAL( i + 1, value); + } +} + +void test_case_10() +{ + value = 0; + + gf = boost::asym_fiber( increment_value_fn, boost::asym_fiber::default_stacksize); + + BOOST_CHECK_EQUAL( 0, value); + + boost::barrier b( 2); + boost::thread t1( fn_first, 0, 5, boost::ref( b) ); + boost::thread t2( fn_last, 5, 7, boost::ref( b) ); + t1.join(); + t2.join(); + + BOOST_CHECK_EQUAL( 7, value); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: fiber test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + test->add( BOOST_TEST_CASE( & test_case_5) ); + test->add( BOOST_TEST_CASE( & test_case_6) ); + test->add( BOOST_TEST_CASE( & test_case_7) ); + test->add( BOOST_TEST_CASE( & test_case_8) ); + test->add( BOOST_TEST_CASE( & test_case_9) ); + test->add( BOOST_TEST_CASE( & test_case_10) ); + + return test; +} diff --git a/libs/fiber/test/test_sym_fiber.cpp b/libs/fiber/test/test_sym_fiber.cpp new file mode 100644 index 00000000..89a3a810 --- /dev/null +++ b/libs/fiber/test/test_sym_fiber.cpp @@ -0,0 +1,235 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include +#include +#include +#include + +#include + +int value; + +boost::sym_fiber gf1; +boost::sym_fiber gf2; + +void zero_args_fn() +{} + +void one_args_fn( int) +{} + +void two_args_fn( int, std::string const&) +{} + +void set_value_fn( int i) +{ + value = i; + gf2.switch_to( gf1); +} + +void increment_value_fn() +{ + while ( true) + { + ++value; + gf2.switch_to( gf1); + } +} + +struct X +{ + void operator()() + {} +}; + +struct Y +{ + void operator()( int i) + {} +}; + +void increment( int k, int n) +{ + gf1 = boost::sym_fiber::from_current_context(); + for ( int i = k; i < n; ++i) + { + gf1.switch_to( gf2); + BOOST_CHECK_EQUAL( i + 1, value); + } +} + +void fn_first( int k, int n, boost::barrier & b) +{ + increment( k, n); + b.wait(); +} + +void fn_last( int k, int n, boost::barrier & b) +{ + b.wait(); + increment( k, n); +} + +void test_case_1() +{ + X x; + boost::sym_fiber f1( & X::operator(), x, boost::sym_fiber::default_stacksize); + BOOST_CHECK( f1); + + Y y; + boost::sym_fiber f2( & Y::operator(), y, 7, boost::sym_fiber::default_stacksize); + BOOST_CHECK( f2); +} + +void test_case_2() +{ + boost::sym_fiber f1( one_args_fn, 10, boost::sym_fiber::default_stacksize); + boost::sym_fiber f2; + BOOST_CHECK( f1); + BOOST_CHECK( ! f2); +} + +void test_case_3() +{ + boost::sym_fiber f1( zero_args_fn, boost::sym_fiber::default_stacksize); + boost::sym_fiber f2( one_args_fn, 1, boost::sym_fiber::default_stacksize); + boost::sym_fiber f3( two_args_fn, 1, "abc", boost::sym_fiber::default_stacksize); +} + +void test_case_4() +{ + boost::sym_fiber f1( zero_args_fn, boost::sym_fiber::default_stacksize); + f1 = boost::sym_fiber( zero_args_fn, boost::sym_fiber::default_stacksize); + boost::sym_fiber f2( one_args_fn, 1, boost::sym_fiber::default_stacksize); + f2 = boost::sym_fiber( one_args_fn, 1, boost::sym_fiber::default_stacksize); +} + +void test_case_5() +{ + boost::sym_fiber f1( one_args_fn, 10, boost::sym_fiber::default_stacksize); + BOOST_CHECK( f1); + boost::sym_fiber f2( boost::move( f1) ); + BOOST_CHECK( ! f1); + BOOST_CHECK( f2); + boost::sym_fiber f3; + BOOST_CHECK( ! f3); + f3 = f2; + BOOST_CHECK( f3); + BOOST_CHECK_EQUAL( f2, f3); +} + +void test_case_6() +{ + boost::sym_fiber f1( zero_args_fn, boost::sym_fiber::default_stacksize); + boost::sym_fiber f2( zero_args_fn, boost::sym_fiber::default_stacksize); + boost::sym_fiber f3; + BOOST_CHECK( f1); + BOOST_CHECK( f2); + BOOST_CHECK( ! f3); + + BOOST_CHECK( f1 != f2); + BOOST_CHECK( f1 != f3); + BOOST_CHECK( f2 != f3); + + std::ostringstream os1; + os1 << f1.get_id(); + std::ostringstream os2; + os2 << f2.get_id(); + std::ostringstream os3; + os3 << f3.get_id(); + + std::string not_a_fiber("{not-a-fiber}"); + BOOST_CHECK( os1.str() != os2.str() ); + BOOST_CHECK( os1.str() != os3.str() ); + BOOST_CHECK( os2.str() != os3.str() ); + BOOST_CHECK( os1.str() != not_a_fiber); + BOOST_CHECK( os2.str() != not_a_fiber); + BOOST_CHECK( os3.str() == not_a_fiber); +} + +void test_case_7() +{ + boost::sym_fiber f1( zero_args_fn, boost::sym_fiber::default_stacksize); + boost::sym_fiber f2( zero_args_fn, boost::sym_fiber::default_stacksize); + + boost::sym_fiber::id id1 = f1.get_id(); + boost::sym_fiber::id id2 = f2.get_id(); + + f1.swap( f2); + + BOOST_CHECK_EQUAL( f1.get_id(), id2); + BOOST_CHECK_EQUAL( f2.get_id(), id1); +} + +void test_case_8() +{ + value = 0; + + gf1 = boost::sym_fiber::from_current_context(); + gf2 = boost::sym_fiber( set_value_fn, 7, boost::sym_fiber::default_stacksize); + + BOOST_CHECK_EQUAL( 0, value); + + gf1.switch_to( gf2); + + BOOST_CHECK_EQUAL( 7, value); +} + +void test_case_9() +{ + value = 0; + + gf1 = boost::sym_fiber::from_current_context(); + gf2 = boost::sym_fiber( increment_value_fn, boost::sym_fiber::default_stacksize); + + BOOST_CHECK_EQUAL( 0, value); + + for ( int i = 0; i < 7; ++i) + { + gf1.switch_to( gf2); + BOOST_CHECK_EQUAL( i + 1, value); + } +} + +void test_case_10() +{ + value = 0; + + gf2 = boost::sym_fiber( increment_value_fn, boost::sym_fiber::default_stacksize); + + BOOST_CHECK_EQUAL( 0, value); + + boost::barrier b( 2); + boost::thread t1( fn_first, 0, 5, boost::ref( b) ); + boost::thread t2( fn_last, 5, 7, boost::ref( b) ); + t1.join(); + t2.join(); + + BOOST_CHECK_EQUAL( 7, value); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: fiber test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + test->add( BOOST_TEST_CASE( & test_case_5) ); + test->add( BOOST_TEST_CASE( & test_case_6) ); + test->add( BOOST_TEST_CASE( & test_case_7) ); + test->add( BOOST_TEST_CASE( & test_case_8) ); + test->add( BOOST_TEST_CASE( & test_case_9) ); + test->add( BOOST_TEST_CASE( & test_case_10) ); + + return test; +} diff --git a/libs/reflection/boost-build.jam b/libs/reflection/boost-build.jam new file mode 100644 index 00000000..d8176f36 --- /dev/null +++ b/libs/reflection/boost-build.jam @@ -0,0 +1,43 @@ +# Copyright Rene Rivera 2007. +# +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For instructions see Jamfile.v2, or "bjam --help". + +local rule if-has-file ( file + : dir * ) +{ + local result ; + if $(dir) + { + result = [ GLOB $(dir) : $(file) ] ; + } + return $(result[1]:P) ; +} + +#~ Attempts to find the Boost source tree... + +local boost-src = [ if-has-file configure : + [ MATCH --boost=(.*) : $(ARGV) ] + $(BOOST) + $(.boost-build-file:D)/../boost + ] ; + +#~ Attempts to find the Boost.Build files... + +local boost-build-src = [ if-has-file bootstrap.jam : + [ MATCH --boost-build=(.*) : $(ARGV) ] + $(BOOST_BUILD_PATH) + $(BOOST_BUILD) + $(boost-src)/tools/build/v2 + ] ; + +#~ Set some common vars to refer to the Boost sources... + +BOOST ?= $(boost-src) ; +BOOST_ROOT ?= $(boost-src) ; + +#~ And load up Boost.Build... + +boost-build $(boost-build-src) ; diff --git a/libs/reflection/boost.png b/libs/reflection/boost.png new file mode 100644 index 00000000..b4d51fcd Binary files /dev/null and b/libs/reflection/boost.png differ diff --git a/libs/reflection/boostbook.css b/libs/reflection/boostbook.css new file mode 100644 index 00000000..d84d5384 --- /dev/null +++ b/libs/reflection/boostbook.css @@ -0,0 +1,511 @@ +/*============================================================================= + Copyright (c) 2004 Joel de Guzman + http://spirit.sourceforge.net/ + + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +/*============================================================================= + Body defaults +=============================================================================*/ + + body + { + margin: 1em; + font-family: sans-serif; + } + +/*============================================================================= + Paragraphs +=============================================================================*/ + + p + { + text-align: left; + font-size: 10pt; + line-height: 1.15; + } + +/*============================================================================= + Program listings +=============================================================================*/ + + /* Code on paragraphs */ + p tt.computeroutput + { + font-size: 9pt; + } + + pre.synopsis + { + font-size: 90%; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + .programlisting, + .screen + { + font-size: 9pt; + display: block; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + /* Program listings in tables don't get borders */ + td .programlisting, + td .screen + { + margin: 0pc 0pc 0pc 0pc; + padding: 0pc 0pc 0pc 0pc; + } + +/*============================================================================= + Headings +=============================================================================*/ + + h1, h2, h3, h4, h5, h6 + { + text-align: left; + margin: 1em 0em 0.5em 0em; + font-weight: bold; + } + + h1 { font: 140% } + h2 { font: bold 140% } + h3 { font: bold 130% } + h4 { font: bold 120% } + h5 { font: italic 110% } + h6 { font: italic 100% } + + /* Top page titles */ + title, + h1.title, + h2.title + h3.title, + h4.title, + h5.title, + h6.title, + .refentrytitle + { + font-weight: bold; + margin-bottom: 1pc; + } + + h1.title { font-size: 140% } + h2.title { font-size: 140% } + h3.title { font-size: 130% } + h4.title { font-size: 120% } + h5.title { font-size: 110% } + h6.title { font-size: 100% } + + .section h1 + { + margin: 0em 0em 0.5em 0em; + font-size: 140%; + } + + .section h2 { font-size: 140% } + .section h3 { font-size: 130% } + .section h4 { font-size: 120% } + .section h5 { font-size: 110% } + .section h6 { font-size: 100% } + + /* Code on titles */ + h1 tt.computeroutput { font-size: 140% } + h2 tt.computeroutput { font-size: 140% } + h3 tt.computeroutput { font-size: 130% } + h4 tt.computeroutput { font-size: 120% } + h5 tt.computeroutput { font-size: 110% } + h6 tt.computeroutput { font-size: 100% } + +/*============================================================================= + Author +=============================================================================*/ + + h3.author + { + font-size: 100% + } + +/*============================================================================= + Lists +=============================================================================*/ + + li + { + font-size: 10pt; + line-height: 1.3; + } + + /* Unordered lists */ + ul + { + text-align: left; + } + + /* Ordered lists */ + ol + { + text-align: left; + } + +/*============================================================================= + Links +=============================================================================*/ + + a + { + text-decoration: none; /* no underline */ + } + + a:hover + { + text-decoration: underline; + } + +/*============================================================================= + Spirit style navigation +=============================================================================*/ + + .spirit-nav + { + text-align: right; + } + + .spirit-nav a + { + color: white; + padding-left: 0.5em; + } + + .spirit-nav img + { + border-width: 0px; + } + +/*============================================================================= + Table of contents +=============================================================================*/ + + .toc + { + margin: 1pc 4% 0pc 4%; + padding: 0.1pc 1pc 0.1pc 1pc; + font-size: 80%; + line-height: 1.15; + } + + .boost-toc + { + float: right; + padding: 0.5pc; + } + +/*============================================================================= + Tables +=============================================================================*/ + + .table-title, + div.table p.title + { + margin-left: 4%; + padding-right: 0.5em; + padding-left: 0.5em; + } + + .informaltable table, + .table table + { + width: 92%; + margin-left: 4%; + margin-right: 4%; + } + + div.informaltable table, + div.table table + { + padding: 4px; + } + + /* Table Cells */ + div.informaltable table tr td, + div.table table tr td + { + padding: 0.5em; + text-align: left; + font-size: 9pt; + } + + div.informaltable table tr th, + div.table table tr th + { + padding: 0.5em 0.5em 0.5em 0.5em; + border: 1pt solid white; + font-size: 80%; + } + +/*============================================================================= + Blurbs +=============================================================================*/ + + div.note, + div.tip, + div.important, + div.caution, + div.warning, + p.blurb + { + font-size: 9pt; /* A little bit smaller than the main text */ + line-height: 1.2; + display: block; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + p.blurb img + { + padding: 1pt; + } + +/*============================================================================= + Variable Lists +=============================================================================*/ + + /* Make the terms in definition lists bold */ + div.variablelist dl dt, + span.term + { + font-weight: bold; + font-size: 10pt; + } + + div.variablelist table tbody tr td + { + text-align: left; + vertical-align: top; + padding: 0em 2em 0em 0em; + font-size: 10pt; + margin: 0em 0em 0.5em 0em; + line-height: 1; + } + + div.variablelist dl dt + { + margin-bottom: 0.2em; + } + + div.variablelist dl dd + { + margin: 0em 0em 0.5em 2em; + font-size: 10pt; + } + + div.variablelist table tbody tr td p, + div.variablelist dl dd p + { + margin: 0em 0em 0.5em 0em; + line-height: 1; + } + +/*============================================================================= + Misc +=============================================================================*/ + + /* Title of books and articles in bibliographies */ + span.title + { + font-style: italic; + } + + span.underline + { + text-decoration: underline; + } + + span.strikethrough + { + text-decoration: line-through; + } + + /* Copyright, Legal Notice */ + div div.legalnotice p + { + text-align: left + } + +/*============================================================================= + Colors +=============================================================================*/ + + @media screen + { + /* Links */ + a + { + color: #005a9c; + } + + a:visited + { + color: #9c5a9c; + } + + h1 a, h2 a, h3 a, h4 a, h5 a, h6 a, + h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, h5 a:hover, h6 a:hover, + h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited + { + text-decoration: none; /* no underline */ + color: #000000; + } + + /* Syntax Highlighting */ + .keyword { color: #0000AA; } + .identifier { color: #000000; } + .special { color: #707070; } + .preprocessor { color: #402080; } + .char { color: teal; } + .comment { color: #800000; } + .string { color: teal; } + .number { color: teal; } + .white_bkd { background-color: #FFFFFF; } + .dk_grey_bkd { background-color: #999999; } + + /* Copyright, Legal Notice */ + .copyright + { + color: #666666; + font-size: small; + } + + div div.legalnotice p + { + color: #666666; + } + + /* Program listing */ + pre.synopsis + { + border: 1px solid #DCDCDC; + } + + .programlisting, + .screen + { + border: 1px solid #DCDCDC; + } + + td .programlisting, + td .screen + { + border: 0px solid #DCDCDC; + } + + /* Blurbs */ + div.note, + div.tip, + div.important, + div.caution, + div.warning, + p.blurb + { + border: 1px solid #DCDCDC; + } + + /* Table of contents */ + .toc + { + border: 1px solid #DCDCDC; + } + + /* Tables */ + div.informaltable table tr td, + div.table table tr td + { + border: 1px solid #DCDCDC; + } + + div.informaltable table tr th, + div.table table tr th + { + background-color: #F0F0F0; + border: 1px solid #DCDCDC; + } + + /* Misc */ + span.highlight + { + color: #00A000; + } + } + + @media print + { + /* Links */ + a + { + color: black; + } + + a:visited + { + color: black; + } + + .spirit-nav + { + display: none; + } + + /* Program listing */ + pre.synopsis + { + border: 1px solid gray; + } + + .programlisting, + .screen + { + border: 1px solid gray; + } + + td .programlisting, + td .screen + { + border: 0px solid #DCDCDC; + } + + /* Table of contents */ + .toc + { + border: 1px solid gray; + } + + .informaltable table, + .table table + { + border: 1px solid gray; + border-collapse: collapse; + } + + /* Tables */ + div.informaltable table tr td, + div.table table tr td + { + border: 1px solid gray; + } + + div.informaltable table tr th, + div.table table tr th + { + border: 1px solid gray; + } + + /* Misc */ + span.highlight + { + font-weight: bold; + } + } diff --git a/libs/reflection/doc/Jamfile.v2 b/libs/reflection/doc/Jamfile.v2 new file mode 100644 index 00000000..a1373d0a --- /dev/null +++ b/libs/reflection/doc/Jamfile.v2 @@ -0,0 +1,51 @@ +# Boost.Extension - documentation Jamfile +# +# Copyright 2008 Jeremy Pack +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# +# See http://www.boost.org/ for latest version. +# + +import doxygen ; +import quickbook ; + +import os ; +local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; + +doxygen autodoc + : + [ glob ../../../boost/reflection/common.hpp ] + : + EXTRACT_ALL=NO + "PREDEFINED=\"BOOST_REFLECTION_DOXYGEN_INVOKED\" \\ + \"U_EXPORT2=\"" + HIDE_UNDOC_MEMBERS=NO + EXTRACT_PRIVATE=NO + ENABLE_PREPROCESSING=YES + MACRO_EXPANSION=YES + EXPAND_ONLY_PREDEF=YES + SEARCH_INCLUDES=YES + INCLUDE_PATH=$(BOOST_ROOT) + ; + +xml reflection : reflection.qbk ; + +boostbook standalone + : + reflection + : + toc.max.depth=300 + toc.section.depth=2 + chunk.section.depth=1 + autodoc + ; + +install css : [ glob $(BOOST_ROOT)/doc/src/*.css ] + : html ; +install images : [ glob $(BOOST_ROOT)/doc/src/images/*.png ] + : html/images ; + +install main_image : [ glob $(BOOST_ROOT)/boost.png ] + : ../ ; \ No newline at end of file diff --git a/libs/reflection/doc/appendices.qbk b/libs/reflection/doc/appendices.qbk new file mode 100644 index 00000000..4ae23953 --- /dev/null +++ b/libs/reflection/doc/appendices.qbk @@ -0,0 +1,12 @@ +[/ Boost.Reflection - appendices ] +[/ Copyright 2007 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:appendices Appendices] +[include appendix_a.qbk] +[include appendix_b.qbk] +[include appendix_c.qbk] +[endsect] \ No newline at end of file diff --git a/libs/reflection/doc/appendix_a.qbk b/libs/reflection/doc/appendix_a.qbk new file mode 100644 index 00000000..e74f8ae3 --- /dev/null +++ b/libs/reflection/doc/appendix_a.qbk @@ -0,0 +1,9 @@ +[/ Boost.Reflection - windows export and import decs ] +[/ Copyright 2007 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:appendix_a Appendix A - Placeholder] +[endsect] \ No newline at end of file diff --git a/libs/reflection/doc/appendix_b.qbk b/libs/reflection/doc/appendix_b.qbk new file mode 100644 index 00000000..ab9a8495 --- /dev/null +++ b/libs/reflection/doc/appendix_b.qbk @@ -0,0 +1,9 @@ +[/ Boost.Reflection - optional libraries ] +[/ Copyright 2007 Mariano G. Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:appendix_b Appendix B - Placeholder] +[endsect] diff --git a/libs/reflection/doc/appendix_c.qbk b/libs/reflection/doc/appendix_c.qbk new file mode 100644 index 00000000..f5d595c5 --- /dev/null +++ b/libs/reflection/doc/appendix_c.qbk @@ -0,0 +1,10 @@ +[/ Boost.Reflection - OS X ] +[/ Copyright 2007 Mariano G. Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:appendix_c Appendix C - Placeholder] + +[endsect] diff --git a/libs/reflection/doc/extension.qbk b/libs/reflection/doc/extension.qbk new file mode 100644 index 00000000..52c9ef0a --- /dev/null +++ b/libs/reflection/doc/extension.qbk @@ -0,0 +1,144 @@ +[/ Boost.Reflection - main doc ] +[/ Copyright 2008 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:extension Boost.Extension Interoperability] +Reflections are designed to work with Boost.Extension, or with +shared libraries in general. A simple example is included in +examples/extension/. + +Declaring the reflected class itself is similar to the +process for doing the same in Boost.Extension. + +Here's how the Jamfile for these libraries is defined. Note +that, like in Boost.Extension, one can rename the shared library +extensions, for consistency across platforms. Here, we use the +.reflection suffix for each shared library. +`` +import type : change-generated-target-suffix ; +import type : change-generated-target-prefix ; +type.change-generated-target-suffix SHARED_LIB : : reflection ; +type.change-generated-target-prefix SHARED_LIB : : lib ; +exe extension-reflection : extension/extension.cpp ; +lib car_lib : extension/car_lib.cpp : shared ; +`` + +The code in the shared library is defined in car_lib.cpp. + +We define two classes to export as reflections. +Although both of these classes are derived from a common +base, this is certainly not necessary. If we were using +Boost.Extension factories, this would be required. +`` +class suv : public car +{ +public: + suv(const char * name) : car(name) {} + virtual const char * get_type(void) { return "It's an SUV."; } + virtual ~suv(void) {} +}; + +class compact : public car +{ +public: + compact(const char * name) : car(name) {} + virtual const char * get_type(void) { return "It's a compact."; } + virtual ~compact(void) {} +}; +`` + +Just like Boost.Extension, an external function needs to be +defined that will be called by the main module. + +extern "C" +void BOOST_EXTENSION_EXPORT_DECL +extension_export_car(std::map reflection_map) { + reflection & first = reflection_map["suv"]; + reflection & second = reflection_map["compact"]; + + // Create a reflector for each type that is being reflected. + reflector suv_reflector(&first); + reflector compact_reflector(&second); + + // Reflect the constructor with a `const char*` arg + suv_reflector.reflect_constructor(); + compact_reflector.reflect_constructor(); + + // Reflect a function for each + suv_reflector.reflect(&suv::get_type, "get_type"); + compact_reflector.reflect(&compact::get_type, "get_type"); +} + +This is all that is necessary to export one constructor and one +function for each class. + +Now, in extension.cpp, we combine Boost.Extension and +Boost.Reflection code to load and use the reflections declared +in the shared library. + +Create a mapping of reflections to strings that +will be populated inside the shared library. +`` +std::map reflection_map; +`` +Load the shared library using Boost.Extension. +`` +boost::extensions::shared_library lib + ((std::string(BOOST_EXTENSION_DIR_START) + + "libcar_lib.extension").c_str()); +lib.open(); +`` +Call an exported function to populate +reflection_map. +`` +lib.get &> + ("extension_export_car")(reflection_map); +if (reflection_map.size() != size_t(2)) { + std::cout << "Could not load reflections!"; + return 1; +} +`` +Pull out two reflections that were named "suv" and +"compact" respectively. +`` +reflection & first_reflection = + reflection_map["suv"]; +reflection & second_reflection = + reflection_map["compact"]; +`` +Use the get_constructor function to find a constructor +that takes one argument, a const char*. +`` +instance_constructor first_constructor = + first_reflection.get_constructor(); +`` +Use the constructor retrieved to create an instance. +Warning! instances should only be used with functions +and constructors generated by a single reflection object. +`` +instance first_instance = + first_constructor("First Instance"); +`` +Get a function to call on this instance. +`` +boost::reflections::function first_function = + first_reflection.get_function("get_type"); +std::cout << "First reflection: " << first_function(first_instance) + << std::endl; + `` +Repeat the steps for the second reflection. +`` +instance_constructor second_constructor = + second_reflection.get_constructor(); +instance second_instance = + second_constructor("Second Instance"); +boost::reflections::function second_function = + second_reflection.get_function("get_type"); +std::cout << "Second reflection: " << second_function(second_instance) + << std::endl; +`` +[endsect] \ No newline at end of file diff --git a/libs/reflection/doc/faq.qbk b/libs/reflection/doc/faq.qbk new file mode 100644 index 00000000..8a113423 --- /dev/null +++ b/libs/reflection/doc/faq.qbk @@ -0,0 +1,10 @@ +[/ Boost.Reflection - faq ] +[/ Copyright 2007 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:faq FAQ] + +[endsect] \ No newline at end of file diff --git a/libs/reflection/doc/interpreter.qbk b/libs/reflection/doc/interpreter.qbk new file mode 100644 index 00000000..4f60b11d --- /dev/null +++ b/libs/reflection/doc/interpreter.qbk @@ -0,0 +1,75 @@ +[/ Boost.Reflection - Interpreter prototype ] +[/ Copyright 2007 Mariano G. Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:interpreter Interpreter prototype] + +In this section we will describe one of the examples. This example is special +because it is one of the reasons that motivated the development of the +library. + +The example is a prototype of a C++ interpreter. Its main goal is to allow +the user to create instances of the reflected interfaces and then +call its methods via the reflection too. + +Nowadays the interpreter is just work-in-progress of a prototype. So you should +understand its uncompleteness, but now it has the following features: + +# Instance creation with parameters. + +# Method call with parameters. + +Anyway this features are limited to one interface (car interface) and also +constrained by the number of parameters (mostly because of pattern matching +issues). + +To show the current syntax we think that the best is to show an example +session. + +In this session we create some instances and call the car::start method. Also +we show what happens when we predicated over unknown instances. + +Let's see the session: + +[table +^Boost.Reflection example - Prototype C++ interpreter.\n +\n +> beetle=Compact(VWBeetle)\n +Instance [beetle] created.\n +> beetle.start()\n +VWBeetle started.\n + --> 1\n +> betle.start()\n +Instance betle not found.\n +> test\n +The command "test" is invalid.\n +> Cherokee=SUV(GrandCherokee)\n +Instance [Cherokee] created.\n +> Cherokee.start()\n +GrandCherokee started.\n + --> 1\n +> Ch.start()\n +Instance Ch not found.\n] + +In this session you could see: + +# we create a new Compact car instance. Its name is VWBeetle and we store it +in a instance variable called beetle. + +# we call start in the newly created instance. It yields true. + +# we try to call an unknown instance. + +# we type a wrong command. + +# then we create another instance and we start it. + + +Of course this interpreter is very limited, but it shows the potential of +Boost.Reflection to develop software that use the meta-information of its +structure. + +[endsect] diff --git a/libs/reflection/doc/introduction.qbk b/libs/reflection/doc/introduction.qbk new file mode 100644 index 00000000..974d4f8e --- /dev/null +++ b/libs/reflection/doc/introduction.qbk @@ -0,0 +1,27 @@ +[/ Boost.Reflection - intro ] +[/ Copyright 2007 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:introduction Introduction] + +The goal of this library is to provide runtime reflection for C++ classes, and to allow the +same across shared library boundaries. It is an offshoot of the Extension library, which +provides dynamic class loading across shared libraries. [link extension Extension Interoperability] +provides an example of using these libraries together. + +Boost.Reflection does not provide automatic reflection of classes. Instead, the class data must +be manually reflected. This does offer some benefits however: + +* This can result in better performance, since only the necessary functions are reflected. +* Arbitrary classes can be reflected without modification. +* It is possible to make a reflected interface that is quite different from the original interface. + + +This library is currently in development in preparation for a review for inclusion in the +Boost C++ Libraries. For ongoing status updates, check +[@http://boost-extension.blogspot.com/ C++ Plugins and Reflection]. + +[endsect] diff --git a/libs/reflection/doc/performance_analysis.qbk b/libs/reflection/doc/performance_analysis.qbk new file mode 100644 index 00000000..7b9436a2 --- /dev/null +++ b/libs/reflection/doc/performance_analysis.qbk @@ -0,0 +1,12 @@ +[/ Boost.Reflection - performance analysis ] +[/ Copyright 2007 Mariano G. Consoni and Jeremy Pack] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:performance_analysis Performance Analysis] + +No performance analysis has yet been performed on the Reflection library. + +[endsect] \ No newline at end of file diff --git a/libs/reflection/doc/quick_start.qbk b/libs/reflection/doc/quick_start.qbk new file mode 100644 index 00000000..ece5c477 --- /dev/null +++ b/libs/reflection/doc/quick_start.qbk @@ -0,0 +1,13 @@ +[/ Boost.Reflection - quick start ] +[/ Copyright 2007 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:quick_start Quick Start] + +For basic usage of the library, check out the tutorials. + +[endsect] + diff --git a/libs/reflection/doc/reflection.qbk b/libs/reflection/doc/reflection.qbk new file mode 100644 index 00000000..7c9c9a8f --- /dev/null +++ b/libs/reflection/doc/reflection.qbk @@ -0,0 +1,24 @@ +[/ Boost.Extension - main doc ] +[/ Copyright 2008-2008 Jeremy Pack and Mariano Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[library Boost.Extension + [quickbook 1.4] + [copyright 2008 Jeremy Pack] + [authors [Pack, Jeremy]] + [purpose + Factory management and facilities for using shared libraries] + [license + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + [@http://www.boost.org/LICENSE_1_0.txt http://www.boost.org/LICENSE_1_0.txt]) + ] + [category Programming Interfaces] + [last-revision $Date: 2008/2/12 19:25:00 $] +] + + +[include appendices.qbk] diff --git a/libs/reflection/doc/tutorial1.qbk b/libs/reflection/doc/tutorial1.qbk new file mode 100644 index 00000000..2e3a8e36 --- /dev/null +++ b/libs/reflection/doc/tutorial1.qbk @@ -0,0 +1,72 @@ +[/ Boost.Reflection - first tutorial ] +[/ Copyright 2007 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + + +[section:tutorial01 Tutorial 1] + +The source code for this tutorial can be found in examples/tutorial_1/. + +The `reflection` class can hold information about a reflected class. For this example, +begin by creating a class that we can reflect: +`` +class HelloWorld { +public: + void printHelloWorld() { + std::cout << "Hello World!" << std::endl; + } +}; +`` +Reflected classes do not need to have any special markup - any normal class can be reflected. +Now create a function to reflect out information about this class: +`` +reflection GetReflection() { + // This reflection will be returned + reflection r; + + // Use a reflector which will only reflect out constructors + // and functions for the HelloWorld class. + reflector current_reflector(&r); + + // Reflect an argless constructor + // To reflect a constructor that takes a "const string&" and an int, + // call current_reflector.reflect_constructor(). + current_reflector.reflect_constructor(); + + // Reflect a member function returning void and having no parameters. + // To reflect a member returning a float and taking a single int as + // an argument, use: current_reflector.reflect(). + current_reflector.reflect(&HelloWorld::printHelloWorld, + "print hello world"); + return r; +} +`` +Now, somewhere else in the program, it is possible to use this function to get a generic +reflection instance, which can be used to instantiate the class and call its reflected +function. + +`` +reflection r = GetReflection(); + +// Make sure this class supports a default constructor, and a function +// identified as "print hello world" that has a void return value and +// takes no arguments. If we wanted a constructor that took one int argument, +// the call would become r.has_constructor(). If we wanted a function +// that took two float arguments, and returned a double, the call would be +// r.has_function("print hello world"). +if (r.get_constructor().valid() and + r.get_function("print hello world").valid()) { + + // Get and call the constructor to create an instance. + instance i = r.get_constructor()(); + + // Get and call the function called "print hello world". + r.get_function("print hello world")(i); +} else { + std::cerr << "Unable to find a required method."; +} +`` +[endsect] \ No newline at end of file diff --git a/libs/reflection/doc/tutorial2.qbk b/libs/reflection/doc/tutorial2.qbk new file mode 100644 index 00000000..4a52f5f4 --- /dev/null +++ b/libs/reflection/doc/tutorial2.qbk @@ -0,0 +1,10 @@ +[/ Boost.Reflection - second tutorial ] +[/ Copyright 2007 Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:tutorial02 Tutorial 2] +This is a placeholder. +[endsect] \ No newline at end of file diff --git a/libs/reflection/doc/tutorial3.qbk b/libs/reflection/doc/tutorial3.qbk new file mode 100644 index 00000000..6ac2ccb2 --- /dev/null +++ b/libs/reflection/doc/tutorial3.qbk @@ -0,0 +1,13 @@ +[/ Boost.Reflection - third tutorial ] +[/ Copyright 2007 Mariano G. Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + + +[section:tutorial03 Tutorial 3] + +This is a placeholder. + +[endsect] \ No newline at end of file diff --git a/libs/reflection/doc/tutorial4.qbk b/libs/reflection/doc/tutorial4.qbk new file mode 100644 index 00000000..9b791833 --- /dev/null +++ b/libs/reflection/doc/tutorial4.qbk @@ -0,0 +1,12 @@ +[/ Boost.Reflection - fourth tutorial ] +[/ Copyright 2007 Mariano G. Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:tutorial04 Tutorial 4] + +This is a placeholder. + +[endsect] \ No newline at end of file diff --git a/libs/reflection/doc/tutorial5.qbk b/libs/reflection/doc/tutorial5.qbk new file mode 100644 index 00000000..32e9674f --- /dev/null +++ b/libs/reflection/doc/tutorial5.qbk @@ -0,0 +1,12 @@ +[/ Boost.Reflection - fifth tutorial ] +[/ Copyright 2007 Mariano G. Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:tutorial05 Tutorial 5] + +This is a placeholder. + +[endsect] \ No newline at end of file diff --git a/libs/reflection/doc/tutorial6.qbk b/libs/reflection/doc/tutorial6.qbk new file mode 100644 index 00000000..c669da42 --- /dev/null +++ b/libs/reflection/doc/tutorial6.qbk @@ -0,0 +1,12 @@ +[/ Boost.Reflection - sixth tutorial ] +[/ Copyright 2007 Mariano G. Consoni ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section:tutorial06 Tutorial 6] + +This is a placeholder. + +[endsect] \ No newline at end of file diff --git a/libs/reflection/doc/tutorials.qbk b/libs/reflection/doc/tutorials.qbk new file mode 100644 index 00000000..5c744f73 --- /dev/null +++ b/libs/reflection/doc/tutorials.qbk @@ -0,0 +1,18 @@ +[/ Boost.Reflection - tutorials ] +[/ Copyright 2007 Mariano G. Consoni and Jeremy Pack ] +[/ Distributed under the Boost Software License, Version 1.0. (See] +[/ accompanying file LICENSE_1_0.txt or copy at ] +[/ http://www.boost.org/LICENSE_1_0.txt) ] +[/ See http://www.boost.org/ for latest version. ] + +[section Tutorials] +[include tutorial1.qbk] + +[/ [include tutorial2.qbk] +[/ [include tutorial3.qbk] +[/ [include tutorial4.qbk] +[/ [include tutorial5.qbk] + + + +[endsect] \ No newline at end of file diff --git a/libs/reflection/examples/Jamfile.v2 b/libs/reflection/examples/Jamfile.v2 new file mode 100644 index 00000000..0cc652b4 --- /dev/null +++ b/libs/reflection/examples/Jamfile.v2 @@ -0,0 +1,37 @@ +# Boost.Extension - examples Jamfile +# +# Copyright 2007 Jeremy Pack +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# +# See http://www.boost.org/ for latest version. +# + +import type : change-generated-target-suffix ; +import type : change-generated-target-prefix ; +type.change-generated-target-suffix SHARED_LIB : : extension ; +type.change-generated-target-prefix SHARED_LIB : : lib ; +import os ; + +local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; +local BOOST_SANDBOX_ROOT = [ os.environ BOOST_SANDBOX_ROOT ] ; +project + : requirements + ../../../ + $(BOOST_ROOT) + $(BOOST_SANDBOX_ROOT) + gcc:dl + gcc:"-Wl,-rpath,'$ORIGIN'" + darwin:DYLD_LIBRARY_PATH=./ + : + ; + +# extension integration example +exe extension-reflection : extension/extension.cpp ; +lib car_lib : extension/car_lib.cpp : shared ; + +install ../test/ : car_lib extension-reflection ; + +# Tutorial 1: Hello World +exe hello_world : tutorial_1/hello_world.cpp ; diff --git a/libs/reflection/examples/car.hpp b/libs/reflection/examples/car.hpp new file mode 100644 index 00000000..234e9645 --- /dev/null +++ b/libs/reflection/examples/car.hpp @@ -0,0 +1,55 @@ +/* + * Boost.Reflection / basic example (car interface) + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include + +class car { +private: + std::string name_; + bool started_; + +public: + car(std::string car_name) : name_(car_name), started_(false) {} + + bool start(void) { + std::cout << name_ << " started." << std::endl; + started_ = true; + return true; + } + + bool turn(float angle) + { + if(started_) { + std::cout << "Turning " << name_ << " "<< angle << " degrees." + << std::endl; + return true; + } else { + std::cout << "Cannot turn before starting the engine of " + << name_ << "." << std::endl; + return false; + } + } + + void accelerate(float qty, bool direction) + { + if(started_) { + std::cout << "Accelerating " << name_ << " "<< qty << " m/h." + << std::endl; + } else { + std::cout << "Cannot accelerate before starting the engine of " + << name_ << "." << std::endl; + } + } + + virtual ~car(void) {} +}; diff --git a/libs/reflection/examples/data_members.cpp b/libs/reflection/examples/data_members.cpp new file mode 100644 index 00000000..dd8447f5 --- /dev/null +++ b/libs/reflection/examples/data_members.cpp @@ -0,0 +1,53 @@ +/* + * Boost.Reflection / Data Members example - Tutorial 1 + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include + +#include + +using boost::reflections::reflection; +using boost::reflections::instance; + +void get_reflection(reflection& r); + +int main(int argc, char ** argv) { + reflection r = GetReflection(); + + // Make sure this class supports a default constructor, and a function + // identified as "print hello world" that has a void return value and + // takes no arguments. If we wanted a constructor that took one int argument, + // the call would become r.has_constructor(). If we wanted a function + // that took two float arguments, and returned a double, the call would be + // r.has_function("print hello world"). + if (r.get_constructor().valid() and + r.get_function("print hello world").valid()) { + + // Get and call the constructor to create an instance. + instance i = r.get_constructor()(); + + // Get and call the function called "print hello world". + r.get_function("print hello world")(i); + } else { + std::cerr << "Unable to find a required method."; + } +} + +struct data_holder { + std::string my_string; + int my_int; +}; + +void get_reflection(reflection& r) { + r.reflect() + .constructor() + .data(&HelloWorld::my_int, "my integer") + .data(&HelloWorld::my_string, "my string"); +} \ No newline at end of file diff --git a/libs/reflection/examples/extension/car_lib.cpp b/libs/reflection/examples/extension/car_lib.cpp new file mode 100644 index 00000000..5803c7df --- /dev/null +++ b/libs/reflection/examples/extension/car_lib.cpp @@ -0,0 +1,51 @@ +/* + * Boost.Extension / car library implementations + * + * (C) Copyright Mariano G. Consoni 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include "../car.hpp" +#include +#include +using namespace boost::reflections; + +// Although both of these classes are derived from a common +// base, this is certainly not necessary. If we were using +// Boost.Extension factories, this would be required. +class suv { +public: + suv(const char * name) {} + const char* get_type(void) { return "It's an SUV."; } + void start() {} + bool start(int target_speed) {} + short year; +}; + +class compact : public car +{ +public: + compact(const char * name) : car(name) {} + virtual const char * get_type(void) { return "It's a compact."; } + virtual ~compact(void) {} +}; + + +extern "C" +void BOOST_EXTENSION_EXPORT_DECL +extension_export_car(std::map reflection_map) { + reflection_map["suv"].reflect() + .constructor() + .function(&suv::get_type, "get_type") + .data(&suv::year, "year") + .function(&suv::start, "start") + .function(&suv::start, "start"); + + reflection_map["compact"].reflect() + .constructor() + .function(&compact::get_type, "get_type"); +} diff --git a/libs/reflection/examples/extension/extension.cpp b/libs/reflection/examples/extension/extension.cpp new file mode 100644 index 00000000..f101d586 --- /dev/null +++ b/libs/reflection/examples/extension/extension.cpp @@ -0,0 +1,88 @@ +/* + * Boost.Reflection / extension integration example + * + * (C) Copyright Mariano G. Consoni 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include + + +int main(void) +{ + using boost::reflections::instance; + using boost::reflections::instance_constructor; + using boost::reflections::reflection; + + // Create a mapping of reflections to strings that + // will be populated inside the shared library. + std::map reflection_map; + + // Load the shared library using Boost.Extension + boost::extensions::shared_library lib + ("libcar_lib.extension"); + lib.open(); + if (!lib.is_open()) + return 2; + if (!lib.get &>("extension_export_car")) { + return 3; + } + // Call an exported function to populate + // reflection_map + lib.get &> + ("extension_export_car")(reflection_map); + if (reflection_map.size() != size_t(2) || + reflection_map.find("suv") == reflection_map.end() || + reflection_map.find("compact") == reflection_map.end()) { + std::cout << "Could not load reflections!"; + return 1; + } + + // Pull out two reflections that were named "suv" and + // "compact" respectively. + reflection & first_reflection = + reflection_map["suv"]; + reflection & second_reflection = + reflection_map["compact"]; + + // Use the get_constructor function to find a constructor + // that takes one argument, a const char*. + instance_constructor first_constructor = + first_reflection.get_constructor(); + + // Use the constructor retrieved to create an instance. + // Warning! instances should only be used with functions + // and constructors generated by a single reflection object. + instance first_instance = + first_constructor("First Instance"); + + // Get a function to call on this instance. + boost::reflections::function first_function = + first_reflection.get_function("get_type"); + std::cout << "First reflection: " << first_function(first_instance) + << std::endl; + + // Repeat the steps for the second reflection. + instance_constructor second_constructor = + second_reflection.get_constructor(); + instance second_instance = + second_constructor("Second Instance"); + boost::reflections::function second_function = + second_reflection.get_function("get_type"); + std::cout << "Second reflection: " << second_function(second_instance) + << std::endl; +} diff --git a/libs/reflection/examples/interpreter/interpreter.cpp b/libs/reflection/examples/interpreter/interpreter.cpp new file mode 100644 index 00000000..6c61cd69 --- /dev/null +++ b/libs/reflection/examples/interpreter/interpreter.cpp @@ -0,0 +1,161 @@ +/* + * Boost.Reflection / intepreter prototype example + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include + +#include +#include +#include + +#include + +#include "../car.hpp" +#include + +#include + +#ifdef USE_READLINE +#include +#include +#endif + + +typedef std::list > factory_list; + + +/// this map stores the instanced variables +std::map context; + +/// parse and execute a command +void parse_command(const std::string &line, factory_list &f, + boost::extension::reflection &car_reflection) +{ + boost::regex re, call_re; + boost::cmatch matches, call_matches; + + std::string creation_pattern("(\\w*)=(\\w*)\\((\\w*)\\)"); + std::string call_pattern("(\\w*).(\\w*)\\(\\)"); + + try { + re = creation_pattern; + } catch (boost::regex_error& e) { + std::cout << creation_pattern << " is not a valid regular expression: \"" + << e.what() << "\"" << std::endl; + return; + } + + try { + call_re = call_pattern; + } catch (boost::regex_error& e) { + std::cout << call_pattern << " is not a valid regular expression: \"" + << e.what() << "\"" << std::endl; + return; + } + + + if(boost::regex_match(line.c_str(), matches, re)) { + + if(matches.size() == 4) { + std::string instance(matches[1].first, matches[1].second); + std::string method(matches[2].first, matches[2].second); + std::string parameter1(matches[3].first, matches[3].second); + + for (std::list >::iterator current_car = f.begin(); + current_car != f.end(); + ++current_car) { + if(current_car->get_info() == method) { + // FIXME: free + car *created_car(current_car->create(parameter1)); + context[instance] = created_car; + + std::cout << "Instance [" << instance << "] created." << std::endl; + } + } + } + return; + } + + + if(boost::regex_match(line.c_str(), call_matches, call_re)) { + + if(call_matches.size() == 3) { + std::string instance(call_matches[1].first, call_matches[1].second); + std::string method(call_matches[2].first, call_matches[2].second); + + std::map::iterator m = context.find(instance); + if(m != context.end()) { + std::cout << " --> " + << car_reflection.call(m->second, method) + << std::endl; + } else { + std::cout << "Instance " << instance << " not found." << std::endl; + } + } + return; + } + + std::cout << "The command \"" << line << "\" is invalid." + << std::endl; + +} + + +int main(void) +{ + using namespace boost::extensions; + + factory_map fm; + load_single_library(fm, "libcar_lib.extension", + "extension_export_car"); + std::list > & car_factory_list = + fm.get(); + if(car_factory_list.size() < 2) { + std::cout << "Error - the classes were not found (" + << car_factory_list.size() << ").\n"; + std::exit(-1); + } + + std::cout << std::endl + << " Boost.Reflection example - Prototype C++ interpreter." + << std::endl << std::endl; + + boost::extension::reflection car_reflection("A Car!"); + car_reflection.add(&car::start, "start"); + car_reflection.add(&car::turn, "turn", "turn_angle"); + + + while(1) { + std::string line; + std::cout << "> "; + std::cin >> line; + +#ifdef USE_READLINE + char *line_chrptr = readline("> "); + std::string line(line_chrptr); + if(line.length() != 0) { + add_history(line.c_str()); + } +#endif + + parse_command(line, car_factory_list, car_reflection); + +#ifdef USE_READLINE + free(line_chrptr); +#endif + } + + return 0; +} diff --git a/libs/reflection/examples/main.cpp b/libs/reflection/examples/main.cpp new file mode 100644 index 00000000..ade29ff7 --- /dev/null +++ b/libs/reflection/examples/main.cpp @@ -0,0 +1,51 @@ +/* + * Boost.Reflection / basic example + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include + +#include "car.hpp" +#include + + +int main(void) +{ + + /* Here we declare that we are reflecting out a Car. The second + template parameter declares that we will describe this reflection + using a string. Any arbitrary type could be used here. */ + boost::extension::reflection car_reflection("A Car!"); + + /* Here we add two methods to the reflection. The library must + correctly parse and remember the parameters and return type of these + methods. Here, we elect to describe the first method using a number + and the second using a string. For the second method, the library + knows now that it is a function of Car called turn that takes a float + named "turn_angle". */ + car_reflection.add(&car::start, 3); + car_reflection.add(&car::turn, "turn", "turn_angle"); + + // create some instances to use our reflection + car porsche_911("Porsche 911"); + car ferrari_f40("Ferrari F40"); + + // call methods + car_reflection.call(&porsche_911, 3); + car_reflection.call(&porsche_911, "turn", .5f); + + car_reflection.call(&ferrari_f40, "turn", .10f); + + return 0; +} diff --git a/libs/reflection/examples/tutorial_1/hello_world.cpp b/libs/reflection/examples/tutorial_1/hello_world.cpp new file mode 100644 index 00000000..7854c9f9 --- /dev/null +++ b/libs/reflection/examples/tutorial_1/hello_world.cpp @@ -0,0 +1,58 @@ +/* + * Boost.Reflection / Hello World example - Tutorial 1 + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include + +#include + +using boost::reflections::reflection; +using boost::reflections::instance; + +reflection GetReflection(); + +int main(int argc, char ** argv) { + reflection r = GetReflection(); + + // Make sure this class supports a default constructor, and a function + // identified as "print hello world" that has a void return value and + // takes no arguments. If we wanted a constructor that took one int argument, + // the call would become r.has_constructor(). If we wanted a function + // that took two float arguments, and returned a double, the call would be + // r.has_function("print hello world"). + if (r.get_constructor().valid() && + r.get_function("print hello world").valid()) { + + // Get and call the constructor to create an instance. + instance i = r.get_constructor()(); + + // Get and call the function called "print hello world". + r.get_function("print hello world")(i); + } else { + std::cerr << "Unable to find a required method."; + } +} + +class HelloWorld { +public: + void printHelloWorld() { + std::cout << "Hello World!" << std::endl; + } +}; + +reflection GetReflection() { + // This reflection will be returned + reflection r; + r.reflect() + .constructor() + .function(&HelloWorld::printHelloWorld, + "print hello world"); + return r; +} \ No newline at end of file diff --git a/libs/reflection/fake.cpp b/libs/reflection/fake.cpp new file mode 100644 index 00000000..814e30d0 --- /dev/null +++ b/libs/reflection/fake.cpp @@ -0,0 +1,12 @@ +/* This file is used to create empty library + * It is useful to test header files. + * + * (C) Copyright Mariano G. Consoni 2007 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + +#include diff --git a/libs/reflection/project-root.jam b/libs/reflection/project-root.jam new file mode 100644 index 00000000..a37c8cf5 --- /dev/null +++ b/libs/reflection/project-root.jam @@ -0,0 +1,7 @@ +# Copyright Rene Rivera 2007. +# +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For instructions see Jamfile.v2, or "bjam --help". diff --git a/libs/reflection/test/Jamfile.v2 b/libs/reflection/test/Jamfile.v2 new file mode 100644 index 00000000..8c74fa5e --- /dev/null +++ b/libs/reflection/test/Jamfile.v2 @@ -0,0 +1,37 @@ +# Boost.Reflection - tests Jamfile +# +# Copyright 2008 Mariano G. Consoni +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# +# See http://www.boost.org/ for latest version. +# + +using testing ; +import os ; +local BOOST_ROOT = [ os.environ BOOST_ROOT ] ; +project + : requirements + $(BOOST_ROOT)/libs/test/build//boost_unit_test_framework/shared + ../../../ + $(BOOST_ROOT) + BOOST_TEST_NO_AUTO_LINK=1 + gcc:BOOST_TEST_DYN_LINK + gcc:dl + darwin:DYLD_LIBRARY_PATH=../bin/ + : + ; + +test-suite reflection_tests_all +: + [ run inheritance_test.cpp ] + [ run parameter_map_test.cpp ] + [ run basic_test.cpp ] + [ run single_param_test.cpp ] + [ run shared_library_test.cpp ] + [ run multi_param_test.cpp ] + [ run parameter_info_test.cpp ] + [ run data_test.cpp ] +: +; diff --git a/libs/reflection/test/basic_test.cpp b/libs/reflection/test/basic_test.cpp new file mode 100644 index 00000000..328754bb --- /dev/null +++ b/libs/reflection/test/basic_test.cpp @@ -0,0 +1,109 @@ +/* + * Boost.Reflection / basic unit test + * + * (C) Copyright Mariano G. Consoni and Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include + +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include +#include + +class car { +public: + car(int year) { + } + int start(int speed) { + return 3 + speed; + } +}; +using namespace boost::reflections; +BOOST_AUTO_TEST_CASE(argless) +{ + reflection car_reflection; + car_reflection.reflect() + .constructor() + .function(&car::start, "start"); +// Check for argless constructor + BOOST_CHECK(car_reflection.get_constructor().valid()); + instance car_instance = + car_reflection.get_constructor().call(4); + bool start_valid = car_reflection.get_function("start").valid(); + BOOST_CHECK(start_valid); + // Make sure it doesn't have this undeclared method + BOOST_CHECK(!car_reflection.get_function("stop").valid()); + function f = + car_reflection.get_function("start"); + int result = f(car_instance, 86); + BOOST_CHECK_EQUAL(result, 89); +} +class porsche : protected car { +public: + porsche(int year) : car(year), year_(year) { + } + int get_year() { + return year_; + } + void start(float speed) { + } + int mileage(float day, double time) { + return 3; + } +private: + int year_; +}; +BOOST_AUTO_TEST_CASE(single_arg) +{ + reflection car_reflection; + car_reflection.reflect() + .constructor() + .function(&porsche::start, "start") + .function(&porsche::mileage, "mileage") + .function(&porsche::get_year, "get_year"); + // Check for argless constructor + BOOST_CHECK(car_reflection.get_constructor().valid()); + BOOST_CHECK(!car_reflection.get_constructor().valid()); + boost::reflections::instance car_instance = + car_reflection.get_constructor()(1987); + function f0(car_reflection.get_function("mileage")); + BOOST_CHECK(f0.valid()); + function f1(car_reflection.get_function("start")); + BOOST_CHECK(f1.valid()); + // Make sure it doesn't have this undeclared method + BOOST_CHECK(!car_reflection.get_function("stop").valid()); + f1(car_instance, 21.0f); + function f2 = car_reflection.get_function("get_year"); + BOOST_CHECK(f2.valid()); + int year = f2(car_instance); + BOOST_CHECK_EQUAL(year, 1987); +} +porsche * get_porsche(float year) { + return new porsche(static_cast(year)); +} +BOOST_AUTO_TEST_CASE(single_arg_factory) +{ + /* + boost::reflections::reflector * car_reflector = + new boost::reflections::reflector(); + boost::reflections::reflection car_reflection(car_reflector); + car_reflector->reflect_constructor(); + car_reflector->reflect_factory(&get_porsche, "get_porsche"); + car_reflector->reflect(&car::start, "start"); + // Check for argless constructor + BOOST_CHECK(car_reflection.has_constructor()); + boost::reflections::instance car_instance = car_reflection.construct(); + BOOST_CHECK(car_reflection.has_method("start")); + // Make sure it doesn't have this undeclared method + BOOST_CHECK(!car_reflection.has_method("stop")); + car_reflector.call(car_reflection, "start");*/ +} + diff --git a/libs/reflection/test/data_test.cpp b/libs/reflection/test/data_test.cpp new file mode 100644 index 00000000..91cd9b42 --- /dev/null +++ b/libs/reflection/test/data_test.cpp @@ -0,0 +1,53 @@ +/* + * Boost.Reflection / basic single parameter unit test + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include + +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include + +#include + +#include +using namespace boost::reflections; + +struct data_holder { + std::string my_string; + int my_int; + std::string double_string() { + return my_string + my_string; + } +}; + +using namespace boost::reflections; + +BOOST_AUTO_TEST_CASE(simple) +{ + reflection r; + r.reflect() + .constructor() + .function(&data_holder::double_string, "double_string") + .data(&data_holder::my_string, "my string") + .data(&data_holder::my_int, "my integer"); + + instance i = r.get_constructor()(); + data d = r.get_data("my string"); + BOOST_CHECK(d.valid()); + std::string& s = d(i); + BOOST_CHECK(s.empty()); + s = "Hello!"; + std::string result = r.get_function("double_string")(i); + BOOST_CHECK_EQUAL(result, + std::string("Hello!Hello!")); +} \ No newline at end of file diff --git a/libs/reflection/test/inheritance_test.cpp b/libs/reflection/test/inheritance_test.cpp new file mode 100644 index 00000000..3c2d40c9 --- /dev/null +++ b/libs/reflection/test/inheritance_test.cpp @@ -0,0 +1,93 @@ +/* + * Boost.Reflection / inheritance tests + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include + +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include +#include +#include + +using namespace boost::reflections; + +class A { + public: + virtual ~A() {} + virtual char get_val() { + return 'A'; + } +}; + +class B : public A { + public: + virtual char get_val() { + return 'B'; + } +}; + +class C : virtual public A { + public: + virtual char get_val() { + return 'C'; + } +}; + +class D : virtual public A { + public: + virtual char get_val() { + return 'D'; + } +}; + +class E : public D, public C { + public: + virtual char get_val() { + return 'E'; + } +}; + +class DSub : public D { + public: +}; + +template +void TestClass() { + reflection r; + r.reflect() + .constructor() + .function(&T::get_val, "get_val"); + + instance i = r.get_constructor()(); + BOOST_CHECK_EQUAL(Name, r.get_function("get_val")(i)); + +} + +BOOST_AUTO_TEST_CASE(shared_library_basic_test) { + TestClass(); + TestClass(); + TestClass(); + TestClass(); + TestClass(); + // TestClass(); +} + +BOOST_AUTO_TEST_CASE(DSubTest) { + reflection r; + r.reflect() + .constructor() + .function(&DSub::get_val, "get_val"); + + instance i = r.get_constructor()(); + BOOST_CHECK_EQUAL('D', r.get_function("get_val")(i)); +} \ No newline at end of file diff --git a/libs/reflection/test/multi_param_test.cpp b/libs/reflection/test/multi_param_test.cpp new file mode 100644 index 00000000..8a232c62 --- /dev/null +++ b/libs/reflection/test/multi_param_test.cpp @@ -0,0 +1,62 @@ +/* + * Boost.Reflection / basic many parameter unit test + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include + +#define BOOST_EXTENSION_USE_PP 1 + +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include +#include + +class car { +public: + car(float cost, const char * color, int quantity) : cost_(cost) {} + float get_cost(const char * color) {return cost_;} + int start(bool fast, float direction) { + if (fast) + return 60; + else + return 30; + } +private: + float cost_; +}; +using namespace boost::reflections; +BOOST_AUTO_TEST_CASE(argless) +{ + reflection car_reflection; + car_reflection.reflect() + .constructor() + .function(&car::start, "start") + .function(&car::get_cost, "get_cost"); + // Check for argless constructor + BOOST_CHECK((car_reflection.get_constructor + ().valid())); + instance car_instance = + car_reflection.get_constructor() + .call(10.0f, "red", 2); + function f1 = + car_reflection.get_function("start"); + BOOST_CHECK(f1.valid()); + int ret_val = f1(car_instance, false, 90.0f); + BOOST_CHECK_EQUAL + (ret_val, 30); + BOOST_CHECK_EQUAL + ((car_reflection.get_function("get_cost") + .call(car_instance, "blue")), 10.0f); + function f2 = + car_reflection.get_function("get_cost"); + BOOST_CHECK_EQUAL((f2(car_instance, "green")), 10.0f); +} diff --git a/libs/reflection/test/parameter_info_test.cpp b/libs/reflection/test/parameter_info_test.cpp new file mode 100644 index 00000000..9c729eb5 --- /dev/null +++ b/libs/reflection/test/parameter_info_test.cpp @@ -0,0 +1,47 @@ +/* + * Boost.Reflection / basic unit test + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include + +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include +#include + +class car { +public: + int start() { + return 3; + } +}; + +using namespace boost::reflections; +BOOST_AUTO_TEST_CASE(argless) { + basic_reflection car_reflection; + car_reflection.reflect() + .constructor() + .function(&car::start, "start", "speed"); + + // Check for argless constructor + BOOST_CHECK(car_reflection.get_constructor().valid()); + instance car_instance = + car_reflection.get_constructor().call(); + BOOST_CHECK(car_reflection.get_function("start").valid()); + // Make sure it doesn't have this undeclared method + BOOST_CHECK(!car_reflection.get_function("stop").valid()); + BOOST_CHECK_EQUAL + (car_reflection.get_function("start").call(car_instance), 3); + function f = + car_reflection.get_function("start"); + BOOST_CHECK_EQUAL(f(car_instance), 3); +} \ No newline at end of file diff --git a/libs/reflection/test/parameter_map_test.cpp b/libs/reflection/test/parameter_map_test.cpp new file mode 100644 index 00000000..f49d1be5 --- /dev/null +++ b/libs/reflection/test/parameter_map_test.cpp @@ -0,0 +1,129 @@ +/* + * Boost.Reflection / basic unit test + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include +#include + +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include +#include + +using boost::reflections::parameter_map; +using boost::reflections::generic_parameter; +using boost::reflections::parameter; +BOOST_AUTO_TEST_CASE(construction) { + parameter* p = new parameter(5); + parameter_map m; + m.insert(std::make_pair("some_integer", p)); +} + +// Verify that the parameter map deletes it's elements +// when it is done with them. +class counted_creation { +public: + counted_creation() { + ++total_objects; + } + counted_creation(const counted_creation& left) { + ++total_objects; + } + ~counted_creation() { + --total_objects; + } + static int total_objects; +}; +int counted_creation::total_objects = 0; +BOOST_AUTO_TEST_CASE(destroy_elements) { + { + parameter* p = + new parameter(counted_creation()); + BOOST_CHECK_EQUAL(counted_creation::total_objects, 1); + parameter_map m; + m.insert(std::make_pair("some_integer", p)); + BOOST_CHECK_EQUAL(counted_creation::total_objects, 1); + } + BOOST_CHECK_EQUAL(counted_creation::total_objects, 0); +} + +BOOST_AUTO_TEST_CASE(float_convert) { + parameter* p = new parameter(5); + p->converts_to(); + p->converts_to(); + generic_parameter<>* g = p; + + BOOST_CHECK(g->can_cast()); + BOOST_CHECK(g->can_cast()); + BOOST_CHECK(!g->can_cast()); + BOOST_CHECK(!g->can_cast()); + + BOOST_CHECK_EQUAL(5.0f, g->cast()); + BOOST_CHECK_EQUAL(5.0, g->cast()); + BOOST_CHECK_EQUAL(5, g->cast()); + parameter_map m; + m.insert(std::make_pair("some_integer", p)); + m.insert(std::make_pair("some_other_integer", + new parameter(12))); + g = m.get_first("some_integer"); + + BOOST_CHECK(g->can_cast()); + BOOST_CHECK(g->can_cast()); + BOOST_CHECK(!g->can_cast()); + BOOST_CHECK(!g->can_cast()); + + BOOST_CHECK_EQUAL(5.0f, g->cast()); + BOOST_CHECK_EQUAL(5.0, g->cast()); + BOOST_CHECK_EQUAL(5, g->cast()); + + g = m.get_first("some_other_integer"); + + BOOST_CHECK(g->can_cast()); + + BOOST_CHECK_EQUAL(12, g->cast()); +} + +void FloatCeilingToInt(float* f, int* i) { + *i = static_cast(std::ceil(*f)); +} + +BOOST_AUTO_TEST_CASE(converts_to_with_func) { + parameter* p = new parameter(4.9f); + p->converts_to_with_func(&FloatCeilingToInt); + BOOST_CHECK(p->can_cast()); + BOOST_CHECK_EQUAL(p->cast(), 5); +} + +class base { +}; +class derived : public base { +}; + +void silly_convert(derived** b, float* f) { + *f = 86; +} + +BOOST_AUTO_TEST_CASE(ptr_convert) { + derived d; + parameter* p = new parameter(&d); + p->converts_to(); + p->converts_to_with_func(&silly_convert); + generic_parameter<>* g = p; + + BOOST_CHECK(g->can_cast()); + BOOST_CHECK(g->can_cast()); + BOOST_CHECK(g->can_cast()); + BOOST_CHECK(!g->can_cast()); + + BOOST_CHECK_EQUAL(&d, g->cast()); + BOOST_CHECK_EQUAL(86, g->cast()); +} \ No newline at end of file diff --git a/libs/reflection/test/parameters_test.cpp b/libs/reflection/test/parameters_test.cpp new file mode 100644 index 00000000..f3054f71 --- /dev/null +++ b/libs/reflection/test/parameters_test.cpp @@ -0,0 +1,42 @@ +/* + * Boost.Reflection / parameter map unit test + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include + +#define BOOST_EXTENSION_USE_PP 1 + +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include +#include +#include +using namespace boost::reflections; +BOOST_AUTO_TEST_CASE(paramter_map) { + parameter_map pm; + int m = 5; + int j = 6; + parameter it = + pm.insert(m, "integer m"); + it->converts_to(); + it->converts_to(); + it->converts_to(); + it->converts_to(); + it = pm.insert(j, "integer m"); + it->converts_to(); + BOOST_CHECK_EQUAL(pm.has()); + float val = pm.get("integer j"); + BOOST_CHECK_EQUAL(val, 5.0f); + float val2 = pm.get("integer j"); + BOOST_CHECK_EQUAL(val2, 6.0f); +} diff --git a/libs/reflection/test/shared_library_test.cpp b/libs/reflection/test/shared_library_test.cpp new file mode 100644 index 00000000..015477fd --- /dev/null +++ b/libs/reflection/test/shared_library_test.cpp @@ -0,0 +1,60 @@ +/* + * Boost.Reflection / basic single parameter unit test + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include + +#define BOOST_EXTENSION_USE_PP 1 + +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include +#include +#include +#include +#include +#include cd +#include + +using namespace boost::reflections; +using namespace boost::extensions; + +BOOST_AUTO_TEST_CASE(shared_library_basic_test) { + std::map reflection_map; + shared_library lib + ("libcar_lib.extension"); + BOOST_CHECK(lib.open()); + lib.get&> + ("extension_export_car")(reflection_map); + BOOST_CHECK_EQUAL(reflection_map.size(), size_t(2)); + // Let's create the reflection and add the methods + reflection & first_reflection = + reflection_map["suv"]; + reflection & second_reflection = + reflection_map["compact"]; + + instance_constructor first_constructor = + first_reflection.get_constructor(); + instance first_instance = + first_constructor("First Instance"); + function first_function = + first_reflection.get_function("get_type"); + BOOST_CHECK_EQUAL(first_function(first_instance), "It's an SUV."); + + instance_constructor second_constructor = + second_reflection.get_constructor(); + instance second_instance = + second_constructor("Second Instance"); + function second_function = + second_reflection.get_function("get_type"); + BOOST_CHECK_EQUAL(second_function(second_instance), "It's a compact."); +} diff --git a/libs/reflection/test/single_param_test.cpp b/libs/reflection/test/single_param_test.cpp new file mode 100644 index 00000000..a6302e15 --- /dev/null +++ b/libs/reflection/test/single_param_test.cpp @@ -0,0 +1,60 @@ +/* + * Boost.Reflection / basic single parameter unit test + * + * (C) Copyright Jeremy Pack 2008 + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/ for latest version. + */ + + +#include +#include + +#define BOOST_EXTENSION_USE_PP 1 + +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK 1 +#include +#include + +class car { +public: + car(float cost) : cost_(cost) {} + float get_cost() {return cost_;} + int start(bool fast) { + if (fast) + return 60; + else + return 30; + } +private: + float cost_; +}; +using namespace boost::reflections; +BOOST_AUTO_TEST_CASE(argless) +{ + reflection car_reflection; + car_reflection.reflect() + .constructor() + .function(&car::start, "start") + .function(&car::get_cost, "get_cost"); + // Check for argless constructor + BOOST_CHECK(car_reflection.get_constructor().valid()); + instance car_instance = + car_reflection.get_constructor().call(10.0f); + function f1 = + car_reflection.get_function("start"); + BOOST_CHECK(f1.valid()); + int ret_val = f1(car_instance, false); + BOOST_CHECK_EQUAL + (ret_val, 30); + BOOST_CHECK_EQUAL + (car_reflection.get_function("get_cost") + .call(car_instance), 10.0f); + function f2 = + car_reflection.get_function("get_cost"); + BOOST_CHECK_EQUAL(f2(car_instance), 10.0f); +} diff --git a/libs/task/build/Jamfile.v2 b/libs/task/build/Jamfile.v2 new file mode 100644 index 00000000..6f9cf91e --- /dev/null +++ b/libs/task/build/Jamfile.v2 @@ -0,0 +1,89 @@ +# Boost.Task Library Build Jamfile + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import os ; +import feature ; +import indirect ; +import path ; + +project boost/task + : source-location + ../src + : requirements + /boost/tasklet//boost_tasklet + /boost/thread//boost_thread + /boost/system//boost_system + static:BOOST_TASKS_BUILD_LIB=1 + shared:BOOST_TASKS_BUILD_DLL=1 + multi + : default-build + multi + ; + +local rule default_taskapi ( ) +{ + local api = posix ; + if [ os.name ] = "NT" { api = win32 ; } + return $(api) ; +} + +feature.feature taskapi : posix win32 : propagated ; +feature.set-default taskapi : [ default_taskapi ] ; + +alias task_sources + : ## win32 sources ## + callable.cpp + context.cpp + fast_semaphore.cpp + poolsize.cpp + semaphore_windows.cpp + spin/auto_reset_event.cpp + spin/barrier.cpp + spin/condition.cpp + spin/count_down_event.cpp + spin/manual_reset_event.cpp + spin/mutex.cpp + stacksize.cpp + watermark.cpp + detail/worker.cpp + detail/worker_group.cpp + detail/wsq.cpp + : ## requirements ## + win32 + ; + +alias task_sources + : ## posix sources ## + callable.cpp + context.cpp + fast_semaphore.cpp + poolsize.cpp + semaphore_posix.cpp + spin/auto_reset_event.cpp + spin/barrier.cpp + spin/condition.cpp + spin/count_down_event.cpp + spin/manual_reset_event.cpp + spin/mutex.cpp + stacksize.cpp + watermark.cpp + detail/worker.cpp + detail/worker_group.cpp + detail/wsq.cpp + : ## requirements ## + posix + ; + +explicit task_sources ; + +lib boost_task + : task_sources + : shared:BOOST_TASKS_USE_DLL=1 + static:BOOST_TASKS_USE_LIB=1 + ; + +boost-install boost_task ; diff --git a/libs/task/doc/Jamfile.v2 b/libs/task/doc/Jamfile.v2 new file mode 100644 index 00000000..087ef08c --- /dev/null +++ b/libs/task/doc/Jamfile.v2 @@ -0,0 +1,36 @@ +# Boost.ThreadPool Library Documentation Jamfile + +# Copyright (C) 2008 Oliver Kowalke + +# Use, modification and distribution is subject to the Boost Software License, +# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +path-constant boost-images : ../../../doc/src/images ; + +xml task : task.qbk ; + +boostbook standalone + : + task + : + # HTML options first: + # Use graphics not text for navigation: + navig.graphics=1 + # How far down we chunk nested sections, basically all of them: + chunk.section.depth=3 + # Don't put the first section on the same page as the TOC: + chunk.first.sections=1 + # How far down sections get TOC's + toc.section.depth=10 + # Max depth in each TOC: + toc.max.depth=3 + # How far down we go with TOC's + generate.section.toc.level=10 + # Path for links to Boost: + boost.root=../../../.. + # Path for libraries index: + boost.libraries=../../../../libs/libraries.htm + # Use the main Boost stylesheet: + html.stylesheet=../../../../doc/html/boostbook.css + ; diff --git a/libs/task/doc/acknowledgements.qbk b/libs/task/doc/acknowledgements.qbk new file mode 100644 index 00000000..5f2b5a1e --- /dev/null +++ b/libs/task/doc/acknowledgements.qbk @@ -0,0 +1,13 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:acknowledgements Acknowledgments] + +I'd like to thank Vicente J. Botet Escriba for his comments. + +[endsect] diff --git a/libs/task/doc/as_sub_task.qbk b/libs/task/doc/as_sub_task.qbk new file mode 100644 index 00000000..63617cee --- /dev/null +++ b/libs/task/doc/as_sub_task.qbk @@ -0,0 +1,40 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:as_sub_task Execute as Sub-Task] + +__as_sub_task__ is a convenient way to execute a __sub_task__. If the parent task is executed inside a __thread_pool__ the __sub_task__ is put into the local-queue of the +__worker_thread__ in the other case the __sub_task__ will be executed in a new thread. __as_sub_task__ is used as default __ep__ for __fn_async__, + + boost::task::handle< long > h( + boost::task::async( + boost::task::make_task( fibonacci, 10), + boost::task::as_sub_task() ) ); + + +[section:as_sub_task Class `as_sub_task`] + + #include + + struct as_sub_task + { + template< typename R > + handle< R > operator()( task< R >); + }; + + +[section `template< typename R > handle< R > operator()( task< R > t)`] +[variablelist +[[Effects:] [moves task in a new thread or thread-pool and returns an handle associated with the task]] +[[Throws:] [`boost::thread_resource_error`]] +] +[endsect] + +[endsect] + +[endsect] diff --git a/libs/task/doc/async_completion_token.qbk b/libs/task/doc/async_completion_token.qbk new file mode 100644 index 00000000..3b5c5cc5 --- /dev/null +++ b/libs/task/doc/async_completion_token.qbk @@ -0,0 +1,315 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:async_completion_token Asynchronous Completion Token] + +[heading Synopsis] + +The __act__ dispatches processing tasks in response to the completion of asynchronous operations. __act__ uniquely identifies +the task and state necessary to process the result of the operation [footnote see [@http://www.cs.wustl.edu/~schmidt/PDF/ACT.pdf 'Asynchronous Completion Token'], Douglas Schmidt]. + +__handle__ represents an __act__. It will be returned by __async__ and is associated with the submitted __task__. + + long fibonacci( long n) + { + if ( n == 0) return 0; + if ( n == 1) return 1; + long k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + long tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + return k1; + } + + void main() + { + // create task + boost::tasks::task< long > t( fibonacci, 10); + + // move task ownership to executor + boost::tasks::handle< long > h( + boost::tasks::async( + boost::move( t), + boost::tasks::new_thread() ) ); + + std::cout << "is ready == " << std::boolalpha << h.is_ready() << "\n"; + + // wait for task completion + h.wait(); + + std::cout << "has value == " << std::boolalpha << h.has_value() << "\n"; + std::cout << "has exception == " << std::boolalpha << h.has_exception() << "\n"; + + // return result + std::cout << "fibonacci(10) == " << h.get() << std::endl; + } + +[heading Interruption] + +Each invokation of __fn_async__ returns an __handle__ which allows to control the associated __task__ (passed to __fn_async__). This includes +the ability to interrupt an __task__ if it is cooperative. Cooperative means that the __task__ contains __interruption_points__ or checks for +interruption requests [footnote see [@http://www.ddj.com/architect/207100682 'Interrupt Politely'], Herb Sutter]. + +* __fn_interrupt__: interrupt __task__ and return immediately + +* __fn_interrupt_and_wait__: interrupt and wait until __task__ was removed from __worker_thread__ + +* __fn_interrupt_and_wait_for__: interrupt and wait until __task__ was removed from __worker_thread__ or time duration has elapsed + +* __fn_interrupt_and_wait_until__: interrupt and wait until __task__ was removed from __worker_thread__ or time point has reached + +* __fn_interruption_requested__: return bool if interruption was requested + + long cooperative( long n) + { + boost::this_thread::interruption_point(); // interruption point + + if ( n == 0) return 0; + if ( n == 1) return 1; + long k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + if ( boost::this_thread::interruption_requested() ) // check if interruption was requested + return; + + long tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + + boost::this_thread::interruption_point(); // interruption point + + return k1; + } + + void main() + { + // task, to be executed asynchronously + boost::tasks::task< long > t( cooperative, 10); + + // move task to async. executor + boost::tasks::handle< long > h( + boost::tasks::async( + boost::move( t), + boost::tasks::new_thread() ) ); + + // interrupt task and wait until task is removed by worker-thread + h.interrupt_and_wait(); + + std::cout << "is ready == " << std::boolalpha << h.is_ready() << "\n"; + std::cout << "has value == " << std::boolalpha << h.has_value() << "\n"; + std::cout << "has exception == " << std::boolalpha << h.has_exception() << "\n"; + + // access result + // throws boost::tasks::task_interrupted + std::cout << h.get() << std::endl; + } + +[note If the task is still pending (not executed yet) when an interruption is requested - the task is not removed from the queue, it is marked to be interrupted instead.] + + +[heading Completion] + +__boost_task__ provides function __waitfor_all__ waits for all handles passed to this function to become ready + + void main() + { + std::vector handles< boost::tasks::handle< long > > results; + results.reserve( 10); + + for ( int i = 0; i < 10; ++i) + { + boost::tasks::task< long > t( fibonacci, i); + + results.push_back( + boost::tasks::async( + boost::move( t) ) ); + } + + // wait until all tasks are ready + boost::tasks::waitfor_all( results.begin(), results.end() ); + + int k = 0; + std::vector< boost::tasks::handle< long > >::iterator e( results.end() ); + for ( + std::vector< boost::tasks::handle< long > >::iterator i( results.begin() ); + i != e; + ++i) + std::cout << "fibonacci(" << k++ << ") == " << i->get() << std::endl; + } + +[section:handle Class template `handle`] + + #include + + template< typename R > + class handle + { + handle(); + + template< typename F > + handle( F const&, context const&); + + void interrupt(); + void interrupt_and_wait(); + void interrupt_and_wait_until( system_time const& abs_time); + template< typename TimeDuration > + void interrupt_and_wait_for( Duration const& rel_time); + bool interruption_requested(); + + R get(); + bool is_ready() const; + bool has_value() const; + bool has_exception() const; + void wait() const; + bool wait_until( system_time const& abs_time); + template< typename TimeDuration > + bool wait_for( TimeDuration const& rel_time); + + void swap( handle< R > & other); + }; + + template< typename Iterator > + friend void waitfor_all( Iterator begin, Iterator end); + + template< typename T1, typename T2 > + friend void waitfor_all( T1 & t1, T2 & t2); + ... + template< typename T1, typename T2, typename T3, typename T4, typename T5 > + friend void waitfor_all( handle< T1 > & t1, handle< T2 > & t2, handle< T3 > & t3, handle< T4 > & t4, handle< T5 > & t5); + +[section `handle()`] +[variablelist +[[Effects:] [constructs an empty (invalid) handle]] +[[Throws:] [Nothing]] +] +[endsect] + +[section `template< typename F > handle( F const&, context const&)`] +[variablelist +[[Effects:] [constructs an handle associated with an future and an context]] +[[Throws:] [Nothing]] +] +[endsect] + +[section `bool interruption_requested()`] +[variablelist +[[Effects:] [checks if interruption is already requested]] +[[Throws:] [Nothing]] +] +[endsect] + +[section `void interrupt()`] +[variablelist +[[Effects:] [requests task interruption; doesn not block (immediatly returns)]] +[[Throws:] [Nothing]] +] +[endsect] + +[section `void interrupt_and_wait()`] +[variablelist +[[Effects:] [requests task interruption and blocks until worker-thread stops task]] +[[Throws:] [`boost::thread_resource_error`]] +] +[endsect] + +[section `bool interrupt_and_wait_until( system_time const&)`] +[variablelist +[[Effects:] [requests task interruption and blocks until worker-thread stops task or time-point elapsed]] +[[Returns:] [false if the the time specified by abs_time was reached, true otherwise]] +[[Throws:] [`boost::thread_resource_error`]] +] +[endsect] + +[section `template< typename TimeDuration > interrupt_and_wait_for( TimeDuration const&)`] +[variablelist +[[Effects:] [requests task interruption and blocks until worker-thread stops task or time-duration elapsed]] +[[Returns:] [false if the the time specified by rel_time was reached, true otherwise]] +[[Throws:] [`boost::thread_resource_error`]] +] +[endsect] + +[section `R get()`] +[variablelist +[[Effects:] [requests the result]] +[[Throws:] [`boost::task::task_interrupted`, `boost::task::task_uninialized`, `boost::task::task_rejected`, `boost::task::broken_task`]] +] +[endsect] + +[section `void wait()`] +[variablelist +[[Effects:] [blocks caller until task is done]] +[[Throws:] [`boost::task::task_interrupted`, `boost::task::task_uninialized`, `boost::task::task_rejected`, `boost::task::broken_task`]] +] +[endsect] + +[section `template< typename TimeDuration > wait_for( TimeDuration const&)`] +[variablelist +[[Effects:] [blocks caller until task is done]] +[[Throws:] [`boost::task::task_interrupted`, `boost::task::task_uninialized`, `boost::task::task_rejected`, `boost::task::broken_task`]] +] +[endsect] + +[section `bool wait_until( system_time const& abs_time) const`] +[variablelist +[[Effects:] [blocks caller until task is done]] +[[Throws:] [`boost::task::task_interrupted`, `boost::task::task_uninialized`, `boost::task::task_rejected`, `boost::task::broken_task`]] +] +[endsect] + +[section `bool is_ready()`] +[variablelist +[[Effects:] [checks if task is done]] +[[Throws:] [Nothing]] +] +[endsect] + +[section `bool has_value()`] +[variablelist +[[Effects:] [checks if task is done and a result value is set]] +[[Throws:] [Nothing]] +] +[endsect] + +[section `bool has_exception()`] +[variablelist +[[Effects:] [checks if task is done and an exception is set]] +[[Throws:] [Nothing]] +] +[endsect] + +[section `void swap( handle< R > & other)`] +[variablelist +[[Effects:] [swapps handle]] +[[Throws:] [Nothing]] +] +[endsect] + +[section Non-member function `wait_for_all()`] + + template< typename Iterator > + void waitfor_all( Iterator begin, Iterator end); + + template< typename T1, typename T2 > + void waitfor_all( T1 & t1, T2 & t2); + ... + template< typename T1, typename T2, typename T3, typename T4, typename T5 > + void waitfor_all( handle< T1 > & t1, handle< T2 > & t2, handle< T3 > & t3, handle< T4 > & t4, handle< T5 > & t5); + +[variablelist +[[Effects:] [waits for all handles to become ready]] +[[Throws:] [`boost::task::task_interrupted`, `boost::task::task_rejected`]] +] +[endsect] + +[endsect] + +[endsect] diff --git a/libs/task/doc/async_execution.qbk b/libs/task/doc/async_execution.qbk new file mode 100644 index 00000000..a812d14c --- /dev/null +++ b/libs/task/doc/async_execution.qbk @@ -0,0 +1,90 @@ +[/ Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:async_execution Asynchronous Execution] + +[heading Synopsis] + +In order to execute a task it has to be passed to function __fn_async__. The task is store inside +the __ep__ (may be given as an additional argument to __fn_async__) and will by means of __ep__. + + +[section:async Non-member function `async()`] + +Function __fn_async__ applies the moved __task__ to the __ep__ which executes the __task__ (for this purpose __ep__ is +required to provide `handle< R > operator()( task< R > && t)`). +The function returns a __handle__ which controls the submitted __task__. + + long fibonacci( long n) + { + if ( n == 0) return 0; + if ( n == 1) return 1; + long k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + long tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + return k1; + } + + void main() + { + // task computing fibonacci(10) + // move the task to executor + boost::tasks::handle< long > h1( + boost::tasks::async( + boost::tasks::make_task( fibonacci, 10) ) ); + + // task computing fibonacci(5) + boost::task< long > t( fibonacci, 5); + // move the task to executor + boost::tasks::handle< long > h2( + boost::tasks::async( + boost::move( t) ) ); + + // access the results + std::cout << "fibonacci(10) == " << h1.get() << std::endl; + std::cout << "fibonacci(5) == " << h2.get() << std::endl; + } + + +[section:async Non-member function `async()`] + + #include + + template< typename R > + handle< R > async( task< R > t); + + template< typename R > + handle< R > async( task< R > t, own_thread ep); + + template< typename R > + handle< R > async( task< R > t, new_thread ep); + + template< typename R, typename Channel > + handle< R > async( task< R > t, pool< Channel > & ep); + + template< typename R, typename Channel, typename Attr > + handle< R > async( task< R > t, Attr attr, pool< Channel > & ep); + + template< typename R, typename Strategy > + handle< R > async( task< R > t, tasklets::scheduler< Strategy > & ep); + +[variablelist +[[Effects:] [moves task to an asyncrounous executer and returns a handle associated with the task]] +[[Throws:] [`boost::thread_resource_error`]] +] +[endsect] + +[endsect] + +[include async_completion_token.qbk] +[include execution_policies.qbk] + +[endsect] diff --git a/libs/task/doc/execution_policies.qbk b/libs/task/doc/execution_policies.qbk new file mode 100644 index 00000000..363477d6 --- /dev/null +++ b/libs/task/doc/execution_policies.qbk @@ -0,0 +1,21 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:execution_policy Execution Policies] + +In contrast to synchronous methods asynchronous methods do not block the program flow when a time consuming operation is executed. +The application continues executing the current context and when the result of the asynchronous method is required the __act__ can be used. + +A __ep__ describes how a __task__ gets asynchronously executed and provides a __act__ to manage the __task__. + +[include own_thread.qbk] +[include new_thread.qbk] +[include as_sub_task.qbk] +[include threadpool.qbk] + +[endsect] diff --git a/libs/task/doc/fork_join.qbk b/libs/task/doc/fork_join.qbk new file mode 100644 index 00000000..72411ace --- /dev/null +++ b/libs/task/doc/fork_join.qbk @@ -0,0 +1,78 @@ +/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:forkjoin Fork/Join] + +Fork/Join algorithms are recursive divide-and-conquer algorithms which repeatedly splitt into sub-tasks until they become small +enough to solve using simple, short sequential methods, so that they run in parallel on multiple cores. + +The fork operation creates new __sub_tasks__ which can run in parallel. The current __task__ is not proceeded in the join operation +until the forked __sub_tasks__ have completed. In the meantime the __worker_thread__ executes other tasks from its local __worker_queue__. + +`` + long serial_fib( long n) + { + if( n < 2) return n; + else return serial_fib( n - 1) + serial_fib( n - 2); + } + + long parallel_fib( long n, long cutof) + { + if ( n < cutof) return serial_fib( n); + else + { + // fork sub-task by moving the task + // ownership to the thread-pool + // sub-task computes fibonacci(n-1) + h1 = boost::task::async( + boost::task::make_task( + parallel_fib, + n - 1, + cutof) ); + + // fork sub-task by moving the task + // ownership to the thread-pool + // sub-task computes fibonacci(n-2) + h2 = boost::task::async( + boost::task::make_task( + parallel_fib, + n - 2, + cutof) ); + + // join the results of both sub-tasks + // if one of the both sub-tasks is not ready + // the worker-thread does not block, it executes other + // task from its local-queue + return h1.get() + h2.get(); + } + } + + void main() + { + boost::task::static_pool< boost::task::unbounded_fifo > pool( boost::task::poolsize( 5) ); + + // compute fibonacci-number 10 + // for numbers < 5 do inline calculation + boost::task::task< long > t( + parallel_fib, + 10, + 5); + + // move task ownership to thread-pool + boost::task::handle< long > h( + boost::task::async( + boost::move( t), + pool) ); + + // access result + std::cout << "fibonacci(10) == " << h.get() << std::endl; + } +`` + + +[endsect] diff --git a/libs/task/doc/handle.qbk b/libs/task/doc/handle.qbk new file mode 100644 index 00000000..262191a7 --- /dev/null +++ b/libs/task/doc/handle.qbk @@ -0,0 +1,333 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:handle Class template `handle`] + +[heading Synopsis] + +__handle__ represents an __act__. It will be returned by __async__ and is associated with the submitted __task__. + + long fibonacci( long n) + { + if ( n == 0) return 0; + if ( n == 1) return 1; + long k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + long tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + return k1; + } + + void main() + { + // create task + boost::tasks::task< long > t( fibonacci, 10); + + // move task ownership to executor + boost::tasks::handle< long > h( + boost::tasks::async( + boost::move( t), + boost::tasks::new_thread() ) ); + + std::cout << "is ready == " << std::boolalpha << h.is_ready() << "\n"; + + // wait for task completion + h.wait(); + + std::cout << "has value == " << std::boolalpha << h.has_value() << "\n"; + std::cout << "has exception == " << std::boolalpha << h.has_exception() << "\n"; + + // return result + std::cout << "fibonacci(10) == " << h.get() << std::endl; + } + +[heading Interruption] + +Each invokation of __fn_async__ returns an __handle__ which allows to control the associated __task__ (passed to __fn_async__). This includes +the ability to interrupt an __task__ if it is cooperative. Cooperative means that the __task__ contains __interruption_points__ or checks for +interruption requests [footnote see [@http://www.ddj.com/architect/207100682 'Interrupt Politely'], Herb Sutter]. + +* __fn_interrupt__: interrupt __task__ and return immediately + +* __fn_interrupt_and_wait__: interrupt and wait until __task__ was removed from __worker_thread__ + +* __fn_interrupt_and_wait_for__: interrupt and wait until __task__ was removed from __worker_thread__ or time duration has elapsed + +* __fn_interrupt_and_wait_until__: interrupt and wait until __task__ was removed from __worker_thread__ or time point has reached + +* __fn_interruption_requested__: return bool if interruption was requested + + long cooperative( long n) + { + boost::this_thread::interruption_point(); // interruption point + + if ( n == 0) return 0; + if ( n == 1) return 1; + long k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + if ( boost::this_thread::interruption_requested() ) // check if interruption was requested + return; + + long tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + + boost::this_thread::interruption_point(); // interruption point + + return k1; + } + + void main() + { + // task, to be executed asynchronously + boost::tasks::task< long > t( cooperative, 10); + + // move task to async. executor + boost::tasks::handle< long > h( + boost::tasks::async( + boost::move( t), + boost::tasks::new_thread() ) ); + + // interrupt task and wait until task is removed by worker-thread + h.interrupt_and_wait(); + + std::cout << "is ready == " << std::boolalpha << h.is_ready() << "\n"; + std::cout << "has value == " << std::boolalpha << h.has_value() << "\n"; + std::cout << "has exception == " << std::boolalpha << h.has_exception() << "\n"; + + // access result + // throws boost::tasks::task_interrupted + std::cout << h.get() << std::endl; + } + +[note If the task is still pending (not executed yet) when an interruption is requested - the task is not removed from the queue, it is marked to be interrupted instead.] + + +[heading Completion] + +__boost_task__ provides function __waitfor_all__ waits for all handles passed to this function to become ready + + void main() + { + std::vector handles< boost::tasks::handle< long > > results; + results.reserve( 10); + + for ( int i = 0; i < 10; ++i) + { + boost::tasks::task< long > t( fibonacci, i); + + results.push_back( + boost::tasks::async( + boost::move( t) ) ); + } + + // wait until all tasks are ready + boost::tasks::waitfor_all( results.begin(), results.end() ); + + int k = 0; + std::vector< boost::tasks::handle< long > >::iterator e( results.end() ); + for ( + std::vector< boost::tasks::handle< long > >::iterator i( results.begin() ); + i != e; + ++i) + std::cout << "fibonacci(" << k++ << ") == " << i->get() << std::endl; + } + +[section:handle Class template `handle`] + + #include + + template< typename R > + class handle + { + handle(); + + template< typename F > + handle( F const&, context const&); + + void interrupt(); + void interrupt_and_wait(); + void interrupt_and_wait_until( system_time const& abs_time); + template< typename TimeDuration > + void interrupt_and_wait_for( Duration const& rel_time); + bool interruption_requested(); + + R get(); + bool is_ready() const; + bool has_value() const; + bool has_exception() const; + void wait() const; + bool wait_until( system_time const& abs_time); + template< typename TimeDuration > + bool wait_for( TimeDuration const& rel_time); + + void swap( handle< R > & other); + }; + + template< typename Iterator > + friend void waitfor_all( Iterator begin, Iterator end); + + template< typename T1, typename T2 > + friend void waitfor_all( T1 & t1, T2 & t2); + ... + template< typename T1, typename T2, typename T3, typename T4, typename T5 > + friend void waitfor_all( handle< T1 > & t1, handle< T2 > & t2, handle< T3 > & t3, handle< T4 > & t4, handle< T5 > & t5); + +[section `handle()`] +[variablelist +[[Effects:] [constructs an empty (invalid) handle]] +[[Throws:] [Nothing]] +] +[endsect] + +[section `template< typename F > handle( F const&, context const&)`] +[variablelist +[[Effects:] [constructs an handle associated with an future and an context]] +[[Throws:] [Nothing]] +] +[endsect] + +[section `bool interruption_requested()`] +[variablelist +[[Effects:] [checks if interruption is already requested]] +[[Throws:] [Nothing]] +] +[endsect] + +[section `void interrupt()`] +[variablelist +[[Effects:] [requests task interruption; doesn not block (immediatly returns)]] +[[Throws:] [Nothing]] +] +[endsect] + +[section `void interrupt_and_wait()`] +[variablelist +[[Effects:] [requests task interruption and blocks until worker-thread stops task]] +[[Throws:] [`boost::thread_resource_error`]] +] +[endsect] + +[section `bool interrupt_and_wait_until( system_time const&)`] +[variablelist +[[Effects:] [requests task interruption and blocks until worker-thread stops task or time-point elapsed]] +[[Returns:] [false if the the time specified by abs_time was reached, true otherwise]] +[[Throws:] [`boost::thread_resource_error`]] +] +[endsect] + +[section `template< typename TimeDuration > interrupt_and_wait_for( TimeDuration const&)`] +[variablelist +[[Effects:] [requests task interruption and blocks until worker-thread stops task or time-duration elapsed]] +[[Returns:] [false if the the time specified by rel_time was reached, true otherwise]] +[[Throws:] [`boost::thread_resource_error`]] +] +[endsect] + +[section `R get()`] +[variablelist +[[Effects:] [requests the result]] +[[Throws:] [`boost::task::task_interrupted`, `boost::task::task_uninialized`, `boost::task::task_rejected`, `boost::task::broken_task`]] +] +[endsect] + +[section `void wait()`] +[variablelist +[[Effects:] [blocks caller until task is done]] +[[Throws:] [`boost::task::task_interrupted`, `boost::task::task_uninialized`, `boost::task::task_rejected`, `boost::task::broken_task`]] +] +[endsect] + +[section `template< typename TimeDuration > wait_for( TimeDuration const&)`] +[variablelist +[[Effects:] [blocks caller until task is done]] +[[Throws:] [`boost::task::task_interrupted`, `boost::task::task_uninialized`, `boost::task::task_rejected`, `boost::task::broken_task`]] +] +[endsect] + +[section `bool wait_until( system_time const& abs_time) const`] +[variablelist +[[Effects:] [blocks caller until task is done]] +[[Throws:] [`boost::task::task_interrupted`, `boost::task::task_uninialized`, `boost::task::task_rejected`, `boost::task::broken_task`]] +] +[endsect] + +[section `bool is_ready()`] +[variablelist +[[Effects:] [checks if task is done]] +[[Throws:] [Nothing]] +] +[endsect] + +[heading Member function `has_value()`] + + bool has_value() + +[variablelist +[[Effects:] [checks if task is done and a result value is set]] +[[Throws:] [Nothing]] +] + + +[heading Member function `has_exception()`] + + bool has_exception() + +[variablelist +[[Effects:] [checks if task is done and an exception is set]] +[[Throws:] [Nothing]] +] + + +[heading Member function `get_future()`] + + shared_future< R > & get_future() + +[variablelist +[[Effects:] [returns a reference to the internal shared_future< R >]] +[[Throws:] [Nothing]] +] + + +[heading Member function `swap()`] + + void swap( handle< R > & other) + +[variablelist +[[Effects:] [swapps handle]] +[[Throws:] [Nothing]] +] + + +[section Non-member function `wait_for_all()`] + + template< typename Iterator > + void waitfor_all( Iterator begin, Iterator end); + + template< typename T1, typename T2 > + void waitfor_all( T1 & t1, T2 & t2); + + ... + + template< typename T1, typename T2, typename T3, typename T4, typename T5 > + void waitfor_all( handle< T1 > & t1, handle< T2 > & t2, handle< T3 > & t3, handle< T4 > & t4, handle< T5 > & t5); + +[variablelist +[[Effects:] [waits for all handles to become ready]] +[[Throws:] [`boost::task::task_interrupted`, `boost::task::task_rejected`]] +] +[endsect] + +[endsect] + +[endsect] diff --git a/libs/task/doc/meta_functions.qbk b/libs/task/doc/meta_functions.qbk new file mode 100644 index 00000000..fc889bde --- /dev/null +++ b/libs/task/doc/meta_functions.qbk @@ -0,0 +1,60 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:meta_functions Meta functions] + +If the __thread_pool__ supports attributes (like priorities) __has_attribute__ evaluates to `true` at compile-time (derived from +boost::mpl::bool_). The type of the attribute is determined by __attribute_type__. + + // thread-pool with priority scheduling + // type of priority is int + typdef boost::task::static_pool< boost::task::unbounded_priority< int > > pool_type; + + // test if thread-pool supports priorities at compile time + std::cout << std::boolalpha << boost::task::has_attribute< pool_type >::value << "\n"; + + // access the type used for priority + std::cout << typeid( boost::task::attribute_type< pool_type >::type).name() << std::endl; + +[section:has_attribute Meta function `has_attribute`] + + #include + + template< typename Pool > + struct has_attribute : public mpl::bool_< + is_same< + detail::has_priority, + typename Pool::scheduler_type::priority_tag_type + >::value + > + {}; + +[variablelist +[[Effects:] [returns true if Pool supports attributes (priority-scheduling)]] +[[Throws:] [nothing]] +] +[endsect] + + +[section:attribute_type Meta function `attribute_type`] + + #include + + template< typename Pool > + struct attribute_type + { + typedef typename Pool::scheduler_type::attribute_type type; + }; + +[variablelist +[[Effects:] [returns type of attribute]] +[[Throws:] [nothing]] +] +[endsect] + +[endsect] diff --git a/libs/task/doc/new_thread.qbk b/libs/task/doc/new_thread.qbk new file mode 100644 index 00000000..d1ed3b97 --- /dev/null +++ b/libs/task/doc/new_thread.qbk @@ -0,0 +1,86 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:new_thread Execute in Separat Thread] + +[heading Synopsis] + +__new_thread__ creates a new thread and executes the task in this thread (asynchronous). The created thread gets +joined by handle. +The returned __handle__ joins the thread in its destructor (if the last reference gets out of scope). + + long fibonacci( long n) + { + if ( n == 0) return 0; + if ( n == 1) return 1; + long k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + long tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + return k1; + } + + void main() + { + boost::task::task< long > t( fibonacci, 10); + + boost::task::handle< long > h( + boost::task::async( + boost::move( t), + boost::task::new_thread() ) ); + + std::cout << "fibonacci(10) == " << h.get() << std::endl; + } + + +[caution Always store the returned __act__ in a variable because __handle__ joins the thread in its destructor (if the last +reference gets out of scope). ] + + +In the example below both `a_function()` and `another_function()` are executed synchron because the returned __handle__ is not stored in +a variable. Thatswhy the __worker_thread__ is joined after return from __fn_async__! + + boost::task::task< void > t1( a_function); + boost::task::task< void > t2( another_function); + + // handles are not retrieved + // both task executed in sequence + boost::task::async( + boost::move( t1), + boost::task::new_thread() ) ); + boost::task::async( + boost::move( t2), + boost::task::new_thread() ) ); + + +[section:new_thread Class `new_thread`] + + #include + + struct new_thread + { + template< typename R > + handle< R > operator()( task< R >); + }; + + +[section `template< typename R > handle< R > operator()( task< R > t)`] +[variablelist +[[Effects:] [moves task in a new thread an returns an handle associated with the task]] +[[Throws:] [`boost::thread_resource_error`]] +] +[endsect] + +[endsect] + +[endsect] + + diff --git a/libs/task/doc/overview.qbk b/libs/task/doc/overview.qbk new file mode 100644 index 00000000..4eb6c41f --- /dev/null +++ b/libs/task/doc/overview.qbk @@ -0,0 +1,140 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:overview Overview] + +__boost_task__ provides a framework for asynchronous execution of tasks (small units of code that can be executed independently and parallel). + +* __task__, a __callable__ representing a fine-grained __work_item__: + * support of task synchron. via primitives like conditions, barriers, event-variables and channels (message exchange) + without blocking worker-thread in the __thread_pool__ + +* __handle__, works as a __act__ associated with a task: + * __fn_interrupt__, __fn_interrupt_and_wait__, ... allow to cancel an cooperative task + * __fn_get__ retrieve value or exception of task execution + * __fn_is_ready__ test if task was executed + * __fn_wait__, __fn_wait_for__ and __fn_wait_until__ block until task is executed and the result is set + * functions __waitfor_all__ to wait for all handles + +* __async__, executes a task by means of __eps__ + * executes task in current thread + * executes task in a newly created thread (thread will be destroyed after completion) + * task gets executed by a __worker_thread__ of a custom __thread_pool__ (for instance with priority or smart scheduling) + * executes task in newly created thread or in a pool of __worker_threads__ depending on whether the parent-task is already executed in a __thread_pool__ + +* __thread_pools__ with __work_stealing__ algorithm and __fork_join__ semantics + + +In order to use the classes and functions described here, you can either include the specific headers specified by the descriptions of each class or function, +or include the master library header: + + #include + +which includes all the other headers in turn. + +Used namespaces are: + + namespace boost::tasks + namespace boost::this_task + + +[heading Example] + + + long fibonacci( long n) + { + if ( n == 0) return 0; + if ( n == 1) return 1; + long k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + long tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + return k1; + } + + void main() + { + // create a thread-pool + boost::tasks::static_pool< boost::tasks::unbounded_fifo > > pool( boost::tasks::poolsize( 5) ); + + // execute tasks in thread-pool + // move tasks ownership to executor + boost::tasks::handle< long > h1( + boost::tasks::async( + boost::tasks::make_task( fibonacci, 10), + pool); + boost::tasks::handle< long > h2( + boost::tasks::async( + boost::tasks::make_task( fibonacci, 5), + boost::move( t2), + pool); + + std::cout << "h1: is ready == " << std::boolalpha << h1.is_ready() << "\n"; + std::cout << "h2: is ready == " << std::boolalpha << h2.is_ready() << "\n"; + + // wait for completion of both tasks + boost::tasks::waitfor_all( h1, h2); + + std::cout << "h1: is ready == " << std::boolalpha << h1.is_ready() << "\n"; + std::cout << "h2: is ready == " << std::boolalpha << h2.is_ready() << "\n"; + std::cout << "h1: has value == " << std::boolalpha << h1.has_value() << "\n"; + std::cout << "h2: has value == " << std::boolalpha << h2.has_value() << "\n"; + std::cout << "h1: has exception == " << std::boolalpha << h1.has_exception() << "\n"; + std::cout << "h2: has exception == " << std::boolalpha << h2.has_exception() << "\n"; + + // get results + std::cout << "fibonacci(10) == " << h1.get() << std::endl; + std::cout << "fibonacci(5) == " << h2.get() << std::endl; + } + + +[heading References] + +* [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2185.html N2185]: Proposed Text for Parallel Task Execution, written by Peter Dimov. + +* [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2276.html N2276]: Thread Pools and Futures, written by Anthony Williams. + +* [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2802.html N2802]: A plea to reconsider detach-on-destruction for thread objects, written by Hans-J. Boehm. + +* [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2880.html N2880]: C++ object lifetime interactions with the threads API, written by Hans-J. Boehm and Lawrence Crowl. + +* [@http://herbsutter.wordpress.com 'Sutter’s Mill'] by Herb Sutter + +* mailing list of C++ standard committee's Library Working Group + +[warning This library is NOT an official Boost library] + +[note Please note that __boost_task__ is not optimized yet.] + +[note __boost_tasklet__ requires [*Boost Library 1.41.0] .] + +__boost_task__ depends uppon __boost_atomic__, __boost_move__ and __boost_tasklet__. + + +[heading Tested Platforms] + +__boost_task__ has been tested on the following platforms and compilers: + +* Debian GNU/Linux 2.6.31.6 (x86_64), GCC 4.3.4 +* Ubuntu GNU/Linux 2.6.28.11 (x86), ICC 11.1 +* FreeBSD 8.0 (x86), GCC 4.2.1 +* OpenSolaris 2009.06 (x86_64), GCC 4.3.2 +* Windows XP Professional (x86), MSVC 9.0 + + +[heading How to build and install] + +* download the sources from +[@http://www.boost-consulting.com/vault/index.php?directory=Concurrent%20Programming Boost Vault] +* extract the archive into the boost-source directory +* call [''bjam toolset= --with-task install'] in order to build and install the library + +[endsect] diff --git a/libs/task/doc/own_thread.qbk b/libs/task/doc/own_thread.qbk new file mode 100644 index 00000000..bef3e24f --- /dev/null +++ b/libs/task/doc/own_thread.qbk @@ -0,0 +1,62 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:own_thread Execute in Current Thread] + +[heading Synopsis] + +__own_thread__ executes the task in the current thread (synchronous execution). + + long fibonacci( long n) + { + if ( n == 0) return 0; + if ( n == 1) return 1; + long k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + long tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + return k1; + } + + void main() + { + boost::task::task< long > t( fibonacci, 10); + + boost::task::handle< long > h( + boost::task::async( + boost::move( t), + boost::task::own_thread() ) ); + + std::cout << "fibonacci(10) == " << h.get() << std::endl; + } + + +[section:own_thread Class `own_thread`] + + #include + + struct own_thread + { + template< typename R > + handle< R > operator()( task< R > t); + }; + +[section `template< typename R > handle< R > operator()( task< R > t)`] +[variablelist +[[Effects:] [moves task in the current thread an returns an handle associated with the task]] +[[Throws:] [Nothing]] +] +[endsect] + +[endsect] + +[endsect] + diff --git a/libs/task/doc/processor_binding.qbk b/libs/task/doc/processor_binding.qbk new file mode 100644 index 00000000..49055859 --- /dev/null +++ b/libs/task/doc/processor_binding.qbk @@ -0,0 +1,30 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:processor_binding Processor binding] + +For some applications it is convenient to bind the __worker_threads__ to processors/cores of the system. For this purpose __fn_bind_to_processors__ must +be given to constructor instead __pool_size__ so that a __worker_thread__ is created an bound the the core. + +`` + typedef boost::task::static_pool< + boost::task::unbounded_queue< boost::tp::fifo > + > pool_type; + + // constructs thread-pool with worker-threads as + // CPUs/Cores are available on the system + pool_type pool( pool_type::bind_to_processors() ); +`` + +The constructor takes additional arguments for the [link_work_stealing work-stealing algorithm] and [link_queue high-] and [link_queue low-watermark] too. + +[note __boost_task__ does provide this feature only for Windows, Linux, AIX, HP-UX, Solaris and FreeBSD.] + + +[endsect] + diff --git a/libs/task/doc/queue.qbk b/libs/task/doc/queue.qbk new file mode 100644 index 00000000..eb180391 --- /dev/null +++ b/libs/task/doc/queue.qbk @@ -0,0 +1,118 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:queue Global Queue] + +A __queue__ synchronizes the access between application threads and __worker_threads__ and implements policies for limitating the amount of queued tasks and how those tasks are scheduled ( first-in-first-out, priority ordering etc.) + + +[heading Bounded Queues] + +__bounded_queue__ limits the number of queued (pending) tasks in order to prevent resource exhaustion. +For this purpose a high- and low-watermark has to be passed at construction. +__hwm__ sets the maximum of pending tasks. If this limited is reached all threads which submit a task will be set to sleep (blocked). If it is equal to __lwm__ everytime a +sleeping producer thread will be woken up and puts its task if one worker thread has taken a task from the queue. +__lwm__ sets the threshold when blocked threads get woken up. If it is less than __hwm__ all sleeping producer threads will be woken up if +the amount of pending tasks reaches __lwm__. + + +[heading Unbounded Queues] + +__unbounded_queue__ allows ann unlimited number of tasks to be queued. +The insertion of an __task__ will never block. If the queue becomes empty __worker_threads__ will be set to sleep until new tasks are enqueued. + + +[heading Task Scheduling] + +For scheduling of tasks inside the queue following strategies are available: + +* fifo: first enqueued task is dequeued first + +* priority: the next item dequeued from the queue depends on its associated priority attribute and sorting criterion applied to the queue (template arguments) + + // thread-pool with priority scheduling + // tasks with higher priority are + // scheduled first + boost::task::static_pool< boost::task::unbounded_priority_queue< int > > pool( boost::task::poolsize( 5) ); + + boost::task::task< void > t1( some_fn); + boost::task::task< void > t2( another_fn); + + // move task t1 with priority 5 to thread-pool + boost::task::async( + boost::move( t1), + 5, + pool); + + // move task t2 with priority 3 to thread-pool + boost::task::async( + boost::move( t2), + 3, + pool); + + +* smart: enqueue- and dequeue-operations are determined by the template arguments und the task-attribute. The library provides an default let only one kind of task stored inside the queue (gets replaced by new one) + + long fibonacci_fn( long n) + { + if ( n == 0) return 0; + if ( n == 1) return 1; + long k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + long tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + return k1; + } + + typedef boost::task::static_pool< boost::task::unbounded_smart_queue< int > > pool_type; + + void main() + { + pool_type pool( boost::task::poolsize( 1) ); + + ... + + boost::task::task< long > t1( + boost::bind( fibonacci_fn, 10) ); + boost::task::task< long > t2( + boost::bind( fibonacci_fn, 5) ); + + // replaced by later task with same attribute == 2 + // if still pending in pool + boost::task::async( + boost::move( t1), + 2, + pool); + + // will replace previous pending task with attribute == 2 + boost::task::async( + boost::move( t2), + 2, + pool); + } + + +__boost_task__ provides following queues: + +* bounded_fifo + +* bounded_priority_queue< Attr, Comp = std::less< Attr > > + +* bounded_smart_queue< Attr, Comp, Enq = detail::replace_oldest, Deq = detail::take_oldest > + + +* unbounded_fifo + +* unbounded_priority_queue< Attr, Comp = std::less< Attr > > + +* unbounded_smart_queue< Attr, Comp, Enq = detail::replace_oldest, Deq = detail::take_oldest > + +[endsect] diff --git a/libs/task/doc/scheduler.qbk b/libs/task/doc/scheduler.qbk new file mode 100644 index 00000000..d68eb7f8 --- /dev/null +++ b/libs/task/doc/scheduler.qbk @@ -0,0 +1,125 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:scheduling Scheduling] + +The scheduling policy determines how tasks are scheduled inside the __queue__. + + +[heading fifo] + +First inserted pending __task__ gets taken first. + + +[heading priority] + +Each __task__ is submitted to the pool with a priority attribute. The type and ordering of the priority is user-defined. + +`` + // thread-pool with priority scheduling + // tasks with higher priority are + // scheduled first + boost::task::static_pool< + boost::task::unbounded_queue< + boost::task::priority< int > > + > pool( boost::task::poolsize( 5) ); + + boost::task::task< void > t1( some_fn); + boost::task::task< void > t2( another_fn); + + // move task t1 with priority 5 to thread-pool + boost::task::async( + boost::move( t1), + 5, + pool); + + // move task t2 with priority 3 to thread-pool + boost::task::async( + boost::move( t2), + 3, + pool); +`` + +In this example the tasks get scheduled by the assigned integer (third argument of __fn_async__). The __task__ with the +lowest priority gets scheduled first (taken by a __worker_thread__). The ordering can be changed by the second argument +of __priority__ (the default is `std::greater< Attr >`). + +`` + // thread-pool with priority scheduling + // tasks with lower priority are + // scheduled first + boost::task::static_pool< + boost::task::unbounded_queue< + boost::task::priority< int, std::less< int > > + > + > pool( boost::task::poolsize( 5) ); +`` + + +[heading smart] + +Each inserted __task__ is associated with an attribute. The scheduler gets an put- and take-policy as template arguments. +The corresponding policy gets applied for each insertion and removal. + +__boost_task__ provides __replace_oldest__ as put- policy and __take_oldest__ as take-policy. Both policies allow the +replacement of older (pending) tasks in the scheduler by new ones. + +`` + long fibonacci_fn( long n) + { + if ( n == 0) return 0; + if ( n == 1) return 1; + long k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + long tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + return k1; + } + + typedef boost::task::static_pool< + boost::task::unbounded_queue< + boost::task::smart< + int, + std::less< int >, + boost::task::replace_oldest, + boost::task::take_oldest + > + > + > pool_type; + + void main() + { + pool_type pool( boost::task::poolsize( 1) ); + + ... + + boost::task::task< long > t1( + boost::bind( fibonacci_fn, 10) ); + boost::task::task< long > t2( + boost::bind( fibonacci_fn, 5) ); + + // replaced by later task with same attribute == 2 + // if still pending in pool + boost::task::async( + boost::move( t1), + 2, + pool); + + // will replace previous pending task with attribute == 2 + boost::task::async( + boost::move( t2), + 2, + pool); + } +`` + + +[endsect] diff --git a/libs/task/doc/shutdown.qbk b/libs/task/doc/shutdown.qbk new file mode 100644 index 00000000..b0d2326f --- /dev/null +++ b/libs/task/doc/shutdown.qbk @@ -0,0 +1,106 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:pool_shutdown Shutdown] + +__boost_task__ allows to shutdown a __thread_pool__ explicitly via functions __fn_shutdown__ and __fn_shutdown_now__. The +destructor of the pool calls __fn_shutdown__ if not already done so that all __worker_threads__ are joined. + +If __fn_shutdown__ is called - the the pool is set the closed state and all __worker_threads__ are joined until all pending tasks are processed. +No futher tasks can be submitted. + + long fibonacci_fn( long n) + { + if ( n == 0) return 0; + if ( n == 1) return 1; + long k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + long tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + return k1; + } + + typedef boost::task::static_pool< boost::task::unbounded_fifo > pool_type; + + void main() + { + pool_type pool( boost::task::poolsize( 1) ); + + ... + + boost::task::task< long > t1( fibonacci_fn, 10); + boost::task::task< long > t2( fibonacci_fn, 5); + + boost::task::handle< long > h1( + boost::task::async( + boost::move( t1), + pool) ); + boost::task::handle< long > h2( + boost::task::async( + boost::move( t2), + pool) ); + + // waits until all pending tasks are finished + pool.shutdown(); + + std::cout << "fibonacci(10) == " << h1.get() << "\n"; + std::cout << "fibonacci(5) == " << h2.get() << std::endl; + } + +[note The deconstructor calls __fn_shutdown__ if the pool was not shutdown yet.] + + +The function __fn_shutdown_now__ closes the pool, interrupts and then joins all __worker_threads__. Pending tasks are unprocessed. + + long fibonacci_fn( long n) + { + if ( n == 0) return 0; + if ( n == 1) return 1; + long k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + long tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + return k1; + } + + typedef boost::task::static_pool< boost::task::unbounded_fifo > pool_type; + + void main() + { + pool_type pool( boost::task::poolsize( 1) ); + + ... + + boost::task::task< long > t1( fibonacci_fn, 10); + boost::task::task< long > t2( fibonacci_fn, 5); + + boost::task::handle< long > h1( + boost::task::async( + boost::move( t1), + pool) ); + boost::task::handle< long > h2( + boost::task::async( + boost::move( t2), + pool) ); + + // requests task interruption and + // joins all worker-threads + pool.shutdown_now(); + + // accessing the result may throw task_interrupted + std::cout << "fibonacci(10) == " << h1.get() << "\n"; + std::cout << "fibonacci(5) == " << h2.get() << std::endl; + } + +[endsect] diff --git a/libs/task/doc/spin_barrier.qbk b/libs/task/doc/spin_barrier.qbk new file mode 100644 index 00000000..3257f653 --- /dev/null +++ b/libs/task/doc/spin_barrier.qbk @@ -0,0 +1,47 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] + +[section:barriers Barriers] + +A barrier is a simple concept. Also known as a __rendezvous__, it is a synchronization point between multiple +contexts of execution. The barrier is configured for a particular number of tasks (`n`), and as +tasks reach the barrier they must wait until all `n` tasks have arrived. Once the `n`-th task has reached the +barrier, all the waiting tasks can proceed, and the barrier is reset. + +[section:barrier Class `barrier`] + + #include + + class barrier + { + public: + barrier( unsigned int initial); + + bool wait(); + }; + +Instances of __spin_barrier__ are not copyable or movable. + +[section:constructor `barrier( unsigned int initial)`] +[variablelist +[[Effects:] [Construct a barrier for `initial` tasks.]] +[[Throws:] [__invalid_argument__ if an error occurs.]] +] +[endsect] + +[section:wait `bool wait()`] +[variablelist +[[Effects:] [Block until `initial` tasks have called `wait` on `*this`. When the `initial`-th task calls `wait`, +all waiting tasks are unblocked, and the barrier is reset. ]] +[[Returns:] [`true` for exactly one task from each batch of waiting tasks, `false` otherwise.]] +[[Throws:] [__task_error__ if an error occurs.]] +] +[endsect] + +[endsect] + +[endsect] diff --git a/libs/task/doc/spin_condition_variables.qbk b/libs/task/doc/spin_condition_variables.qbk new file mode 100644 index 00000000..6ab25e11 --- /dev/null +++ b/libs/task/doc/spin_condition_variables.qbk @@ -0,0 +1,199 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] + +[section:conditions Condition Variables] + +[heading Synopsis] + +The class `condition` provides a mechanism for one task to wait for notification `condition`. When the task is +woken from the wait, then it checks to see if the appropriate condition is now true, and continues if so. If the +condition is not true, then the task then calls `wait` again to resume waiting. In the simplest case, this +condition is just a boolean variable: + + boost::tasks::spin::condition cond; + boost::tasks::spin::mutex mtx; + bool data_ready; + + void process_data(); + + void wait_for_data_to_process() + { + boost::unique_lock< boost::tasks::spin::mutex > lk( mtx); + while ( ! data_ready) + { + cond.wait( lk); + } + process_data(); + } + +Notice that the `lk` is passed to `wait`: `wait` will atomically add the task to the set of tasks waiting on the +condition variable, and unlock the mutex. When the task is woken, the mutex will be locked again before the call +to `wait` returns. This allows other tasks to acquire the mutex in order to update the shared data, and ensures +that the data associated with the condition is correctly synchronized. + +In the mean time, another task sets the condition to `true`, and then calls either `notify_one` or `notify_all` on +the condition variable to wake one waiting task or all the waiting tasks respectively. + + void retrieve_data(); + void prepare_data(); + + void prepare_data_for_processing() + { + retrieve_data(); + prepare_data(); + { + boost::lock_guard< boost::tasks::spin::mutex > lk( mtx); + data_ready = true; + } + cond.notify_one(); + } + +Note that the same mutex is locked before the shared data is updated, but that the mutex does not have to be locked +across the call to `notify_one`. + +[section:condition Class `condition`] + + #include + + class condition + { + public: + condition(); + ~condition(); + + void notify_one(); + void notify_all(); + + void wait( boost::unique_lock< boost::tasks::spin::mutex > & lk); + + template< typename Pred > + void wait( boost::unique_lock< boost::tasks::spin::mutex > & lk, Pred pred); + + template< typename LockType > + void wait( LockType & lk); + + template< typename LockType, typename Pred > + void wait( LockType & lk, Pred predicate); + + void timed_wait( boost::unique_lock< boost::tasks::spin::mutex > & lk, system_time const& abs_time); + + template< typename TimeDuration > + void timed_wait( boost::unique_lock< boost::tasks::spin::mutex > & lk, TimeDuration const& rel_time); + + template< typename Pred > + void timed_wait( boost::unique_lock< boost::tasks::spin::mutex > & lk, system_time const& abs_time, Pred pred); + + template< typename TimeDuration, typename Pred > + void timed_wait( boost::unique_lock< boost::tasks::spin::mutex > & lk, TimeDuration const& rel_time, Pred pred); + + template< typename LockType > + void timed_wait( LockType & lk, system_time const& abs_time); + + template< typename TimeDuration, typename LockType > + void timed_wait( LockType & lk, TimeDuration const& rel_time); + + template< typename LockType, typename Pred > + void timed_wait( LockType & lk, system_time const& abs_time, Pred predicate); + + template< typename LockType, typename TimeDuration, typename Pred > + void timed_wait( LockType & lk, TimeDuration const& rel_time, Pred predicate); + }; + +[section:constructor `condition()`] +[variablelist +[[Effects:] [Constructs an object of class `condition`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:destructor `~condition()`] +[variablelist +[[Precondition:] [All tasks waiting on `*this` have been notified by a call to `notify_one` or `notify_all` +(though the respective calls to `wait` need not have returned).]] +[[Effects:] [Destroys the object.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:notify_one `void notify_one()`] +[variablelist +[[Effects:] [If any tasks are currently __blocked__ waiting on `*this` in a call to `wait`, unblocks one of +those tasks.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:notify_all `void notify_all()`] +[variablelist +[[Effects:] [If any tasks are currently __blocked__ waiting on `*this` in a call to `wait`, unblocks all of +those tasks.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:wait `void wait( boost::unique_lock< boost::tasks::spin::mutex > & lk)`] +[variablelist +[[Precondition:] [`lk` is locked by the current task, and either no other +task is currently waiting on `*this`, or the execution of the `mutex()` member +function on the `lk` objects supplied in the calls to `wait` in all the tasks +currently waiting on `*this` would return the same value as `lk->mutex()` for +this call to `wait`.]] +[[Effects:] [Atomically call `lk.unlock()` and blocks the current task. The +task will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, or spuriously. When the task is unblocked (for whatever +reason), the lock is reacquired by invoking `lk.lock()` before the call to +`wait` returns. The lock is also reacquired by invoking `lk.lock()` if the +function exits with an exception.]] +[[Postcondition:] [`lk` is locked by the current task.]] +[[Throws:] [__task_error__ if an error +occurs. __task_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __task__ object associated with the current task of execution.]] +] +[endsect] + +[section:wait_predicate `template< typename Pred > void wait( boost::unique_lock< boost::tasks::spin::mutex > & lk, Pred pred)`] +[variablelist +[[Effects:] [As-if `` +while ( ! pred()) +{ + wait( lk); +} +``]] + +] +[endsect] + +[section:wait_t `template< typename LockType > void wait( LockType & lk)`] +[variablelist +[[Effects:] [Atomically call `lk.unlock()` and blocks the current task. The +task will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, or spuriously. When the task is unblocked (for whatever +reason), the lock is reacquired by invoking `lk.lock()` before the call to +`wait` returns. The lock is also reacquired by invoking `lk.lock()` if the +function exits with an exception.]] +[[Postcondition:] [`lk` is locked by the current task.]] +[[Throws:] [__task_error__ if an error +occurs. __task_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __task__ object associated with the current task of execution.]] +] +[endsect] + +[section:wait_predicate_t `template< typename LockType, typename Pred > void wait( LockType & lk, Pred pred)`] +[variablelist +[[Effects:] [As-if `` +while ( ! pred()) +{ + wait( lock); +} +``]] + +] +[endsect] + +[endsect] + +[endsect] diff --git a/libs/task/doc/spin_event_variables.qbk b/libs/task/doc/spin_event_variables.qbk new file mode 100644 index 00000000..415938fe --- /dev/null +++ b/libs/task/doc/spin_event_variables.qbk @@ -0,0 +1,289 @@ +[/ + (C) Copyright 2009 Oliver Kowalke + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] + +[section:eventvar_ref Event Variables] + +[heading Synopsis] + +__boost_task__ provides event variables to facilitate coordination between tasks. +A event-variable has two states `set` (`signaled`) or `reset` (`nonsignaled`). + + boost::tasks::spin::auto_reset_event ev; + + void process_data(); + + void wait_for_data_to_process() + { + ev.wait(); + process_data(); + } + +`wait` will atomically add the task to the set of tasks waiting on the event +variable. When the task is woken, the event variable will be reset again. + +In the mean time, another task signals the event variable by calling +`set` on the event variable to wake one waiting task. + + void retrieve_data(); + void prepare_data(); + + void prepare_data_for_processing() + { + retrieve_data(); + prepare_data(); + ev.set(); + } + + +[section:auto_reset_event Class `auto_reset_event`] + +[heading Synopsis] + +When the ['auto_reset_event] gets signaled, any one task will see this particular signal. When a task observes +the signal by waiting on the event, it is automatically transitioned back to non-signaled state. Any tasks can +subsequently set the event. + + #include + + class auto_reset_event : private boost::noncopyable + { + public: + explicit auto_reset_event( bool isset = false); + + ~auto_reset_event(); + + void set(); + + void wait(); + + bool try_wait(); + + bool timed_wait( system_time const& abs_time); + + templatey typename TimeDuration > + bool timed_wait( TimeDuration const& rel_time); + }; + +[section:constructor `explicit auto_reset_event( bool isset = false)`] +[variablelist +[[Effects:] [Constructs an object of class `auto_reset_event`. If isset is `true` +the variable is set.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:destructor `~auto_reset_event()`] +[variablelist +[[Precondition:] [All tasks waiting on `*this` have been notified by a call to +`set` (though the respective calls to `wait` need not have returned).]] +[[Effects:] [Destroys the object.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:set `void set()`] +[variablelist +[[Effects:] [If any tasks are currently __blocked__ waiting on `*this` in a call +to `wait`, unblocks one of those tasks.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:wait `void wait()`] +[variablelist +[[Effects:] [Blocks the current task. The task will unblock when notified by a call +to `this->set()`. When the task is unblocked, the variable is reset before `wait` +returns.]] +[[Throws:] [__task_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __task__ object associated with the current task of execution.]] +] +[endsect] + +[section:try_wait `bool try_wait()`] +[variablelist +[[Effects:] [Returns `true` if the event variable is set otherwise `false`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[endsect] + + +[section:manual_reset_event Class `manual_reset_event`] + +[heading Synopsis] + +The ['manual_reset_event] remains signaled until it is manually reset. Multiple tasks +wait on the same event and observe the same signal. + + #include + + class manual_reset_event : private boost::noncopyable + { + public: + explicit manual_reset_event( bool isset = false); + + ~manual_reset_event(); + + void set(); + + void reset(); + + void wait(); + + bool try_wait(); + + bool timed_wait( system_time const& abs_time); + + templatey typename TimeDuration > + bool timed_wait( TimeDuration const& rel_time); + }; + +[section:constructor `explicit manual_reset_event( bool isset = false)`] +[variablelist +[[Effects:] [Constructs an object of class `manual_reset_event`. If isset is `true` +the variable is set.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:destructor `~manual_reset_event()`] +[variablelist +[[Precondition:] [All tasks waiting on `*this` have been notified by a call to +`set` (though the respective calls to `wait` need not have returned).]] +[[Effects:] [Destroys the object.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:set `void set()`] +[variablelist +[[Effects:] [If any tasks are currently __blocked__ waiting on `*this` in a call +to `wait`, unblocks those tasks. The variable remains signaled until `this->reset()` +gets called.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:reset `void reset()`] +[variablelist +[[Effects:] [The event variable gets nonsignaled and tasks calling `this->wait()` +will block.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:wait `void wait()`] +[variablelist +[[Effects:] [Blocks the current task. The task will unblock when notified by a call +to `this->set()`. When the task is unblocked, the variable remains set.]] +[[Throws:] [__task_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __task__ object associated with the current task of execution.]] +] +[endsect] + +[section:trywait `boo try_wait()`] +[variablelist +[[Effects:] [Returns `true` if the event variable is set otherwise `false`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[endsect] + + +[section:count_down_event Class `count_down_event`] + +[heading Synopsis] + +The ['count_down_event] decrements an internal counter (set in the constructor) and all +waiting tasks are blocked until the count reaches zero. + + #include + + class count_down_event : private boost::noncopyable + { + public: + explicit count_down_event( unsigned int initial); + + ~count_down_event(); + + unsigned int initial() const; + + unsigned int current() const; + + bool is_set() const; + + void set(); + + void wait(); + + bool timed_wait( system_time const& abs_time); + + template< typename TimeDuration > + bool timed_wait( TimeDuration const& rel_time); + }; + +[section:constructor `explicit count_down_event( unsigned int initial)`] +[variablelist +[[Effects:] [Constructs an object of class `count_down_event` with initial value.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:destructor `~count_down_event()`] +[variablelist +[[Precondition:] [All tasks waiting on `*this` have been notified by a call to +`set` (though the respective calls to `wait` need not have returned).]] +[[Effects:] [Destroys the object.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:initial `unsigned int initial()`] +[variablelist +[[Effects:] [Returns the initial value the event variable was initialized with.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:current `unsigned int current()`] +[variablelist +[[Effects:] [Returns the value the variable currently holds.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:is_set `bool is_set()`] +[variablelist +[[Effects:] [Returns `true` if the varaible has reached zero.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:set `void set()`] +[variablelist +[[Effects:] [Decrements the current count. If the count reaches zero and any tasks are +currently __blocked__ waiting on `*this` in a call to `wait`, unblocks those tasks. +The variable remains signaled.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:wait `void wait()`] +[variablelist +[[Effects:] [Blocks the current task. The task will unblock when notified by a call +to `this->set()` and the count of the event variable reaches zero. When the task is +unblocked, the variable remains set.]] +[[Throws:] [__task_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __task__ object associated with the current task of execution.]] +] +[endsect] + +[endsect] + +[endsect] diff --git a/libs/task/doc/spin_fifos.qbk b/libs/task/doc/spin_fifos.qbk new file mode 100644 index 00000000..adcdafaf --- /dev/null +++ b/libs/task/doc/spin_fifos.qbk @@ -0,0 +1,206 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + +[section:fifos (Un)Bounded fifos] + +__boost_task__ provides a bounded and a unbounded fifo suitable to synchonize tasks via message passing. + + typedef boost::tasks::spin::unbounded_channel< int > fifo_t; + + void send( fifo_t fifo) + { + for ( int i = 0; i < 5; ++i) + fifo.put( i); + fifo.deactivate(); + } + + void recv( fifo_t fifo) + { + boost::optional< int > value; + while ( fifo.take( value) ) + { std::cout << "received " << * value << std::endl; } + } + + boost::tasks::scheduler<> sched; + fifo_t fifo; + sched.make_task( send, fifo); + sched.make_task( recv, fifo); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + +[section:unbounded_channel Class template `unbounded_channel`] + + #include + + template< typename T > + class unbounded_channel : private noncopyable + { + public: + unbounded_channel(); + + void deactivate(); + + bool empty(); + + void put( T const& t); + + bool take( boost::optional< T > & va); + + bool take( boost::optional< T > & va, system_time const& abs_time); + + template< typename TimeDuration > + bool take( boost::optional< T > & va, TimeDuration const& rel_time); + + bool try_take( boost::optional< T > & va); + }; + +[section:constructor `unbounded_channel()`] +[variablelist +[[Effects:] [Constructs an object of class `unbounded_channel`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:deactivate `void deactivate()`] +[variablelist +[[Effects:] [Deactivates the fifo. No values can be put after calling `this->deactivate`. tasks blocked in +`this->take()` will be return.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:empty `bool empty()`] +[variablelist +[[Effects:] [Returns `true` if the fifo currently contains no data.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:put `void put( T const& t)`] +[variablelist +[[Effects:] [Enqueues the value in the fifo and wakes up a task waiting for new data available from the +fifo.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:take `bool take( boost::optional< T > & va)`] +[variablelist +[[Effects:] [Dequeues a value from the fifo. If no data is available from the fifo the task gets suspended until +new data are enqueued (return value `true` and va contains dequeued value) or the fifo gets deactiveted and +the function returns `false`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:try_take `bool try_take( boost::optional< T > & va)`] +[variablelist +[[Effects:] [Dequeues a value from the fifo. If no data is available from the fifo the function returns `false`. +Otherwise it returns `true` and `va` contains the dequed value.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[endsect] + + +[section:bounded_channel Class template `bounded_channel`] + + #include + + template< typename T > + class bounded_channel : private noncopyable + { + public: + bounded_channel( std::size_t wm); + + bounded_channel( std::size_t hwm, std::size_t lwm); + + void deactivate(); + + bool empty(); + + void put( T const& t); + + void put( T const& t, system_time const& abs_time); + + template< typename TimeDuration > + void put( T const& t, TimeDuration const& rel_time); + + bool take( boost::optional< T > & va); + + bool take( boost::optional< T > & va, system_time const& abs_time); + + template< typename TimeDuration > + bool take( boost::optional< T > & va, TimeDuration const& rel_time); + + bool try_take( boost::optional< T > & va); + }; + +[section:constructor `bounded_channel( std::size_t wm)`] +[variablelist +[[Effects:] [Constructs an object of class `bounded_channel` which will contain a maximum of `wm` items.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:constructor2 `bounded_channel( std::size_t hwm, std::size_t lwm)`] +[variablelist +[[Effects:] [Constructs an object of class `bounded_channel` which will contain a maximum of `hwm` items.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:deactivate `void deactivate()`] +[variablelist +[[Effects:] [Deactivates the fifo. No values can be put after calling `this->deactivate`. tasks blocked in +`this->take()` will be return.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:empty `bool empty()`] +[variablelist +[[Effects:] [Returns `true` if the fifo currently contains no data.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:put `void put( T const& t)`] +[variablelist +[[Effects:] [Enqueues the value in the fifo and wakes up a task waiting for new data available from the +fifo. If the watermark has reached the task putting the value will be supended until at least one item +was dequeued.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:take `bool take( boost::optional< T > & va)`] +[variablelist +[[Effects:] [Dequeues a value from the fifo. If no data is available from the fifo the task gets suspended until +new data are enqueued (return value `true` and va contains dequeued value) or the fifo gets deactiveted and +the function returns `false`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:try_take `bool try_take( boost::optional< T > & va)`] +[variablelist +[[Effects:] [Dequeues a value from the fifo. If no data is available from the fifo the function returns `false`. +Otherwise it returns `true` and `va` contains the dequed value.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[endsect] + +[endsect] diff --git a/libs/task/doc/spin_future_ref.qbk b/libs/task/doc/spin_future_ref.qbk new file mode 100644 index 00000000..c79451ab --- /dev/null +++ b/libs/task/doc/spin_future_ref.qbk @@ -0,0 +1,928 @@ +[/ + (C) Copyright 2008-9 Anthony Williams. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] + +[section:reference Futures Reference] + +[section:future_state `state` enum] + + namespace future_state + { + enum state {uninitialized, waiting, ready}; + } + +[endsect] + +[section:unique_future `unique_future` class template] + + template + class unique_future + { + unique_future(unique_future & rhs);// = delete; + unique_future& operator=(unique_future& rhs);// = delete; + + public: + typedef future_state::state state; + + unique_future(); + ~unique_future(); + + // move support + unique_future(unique_future && other); + unique_future& operator=(unique_future && other); + + void swap(unique_future& other); + + // retrieving the value + R&& get(); + + // functions to check state + state get_state() const; + bool is_ready() const; + bool has_exception() const; + bool has_value() const; + + // waiting for the result to be ready + void wait() const; + template + bool timed_wait(Duration const& rel_time) const; + bool timed_wait_until(boost::system_time const& abs_time) const; + }; + +[section:default_constructor Default Constructor] + + unique_future(); + +[variablelist + +[[Effects:] [Constructs an uninitialized future.]] + +[[Postconditions:] [[unique_future_is_ready_link `this->is_ready`] returns `false`. [unique_future_get_state_link +`this->get_state()`] returns __uninitialized__.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:destructor Destructor] + + ~unique_future(); + +[variablelist + +[[Effects:] [Destroys `*this`.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:move_constructor Move Constructor] + + unique_future(unique_future && other); + +[variablelist + +[[Effects:] [Constructs a new future, and transfers ownership of the asynchronous result associated with `other` to `*this`.]] + +[[Postconditions:] [[unique_future_get_state_link `this->get_state()`] returns the value of `other->get_state()` prior to the +call. `other->get_state()` returns __uninitialized__. If `other` was associated with an asynchronous result, that result is now +associated with `*this`. `other` is not associated with any asynchronous result.]] + +[[Throws:] [Nothing.]] + +[[Notes:] [If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.]] + +] + +[endsect] + +[section:move_assignment Move Assignment Operator] + + unique_future& operator=(unique_future && other); + +[variablelist + +[[Effects:] [Transfers ownership of the asynchronous result associated with `other` to `*this`.]] + +[[Postconditions:] [[unique_future_get_state_link `this->get_state()`] returns the value of `other->get_state()` prior to the +call. `other->get_state()` returns __uninitialized__. If `other` was associated with an asynchronous result, that result is now +associated with `*this`. `other` is not associated with any asynchronous result. If `*this` was associated with an asynchronous +result prior to the call, that result no longer has an associated __unique_future__ instance.]] + +[[Throws:] [Nothing.]] + +[[Notes:] [If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.]] + +] + +[endsect] + +[section:swap Member function `swap()`] + + void swap(unique_future & other); + +[variablelist + +[[Effects:] [Swaps ownership of the asynchronous results associated with `other` and `*this`.]] + +[[Postconditions:] [[unique_future_get_state_link `this->get_state()`] returns the value of `other->get_state()` prior to the +call. `other->get_state()` returns the value of `this->get_state()` prior to the call. If `other` was associated with an +asynchronous result, that result is now associated with `*this`, otherwise `*this` has no associated result. If `*this` was +associated with an asynchronous result, that result is now associated with `other`, otherwise `other` has no associated result.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + + +[section:get Member function `get()`] + + R&& get(); + R& unique_future::get(); + void unique_future::get(); + +[variablelist + +[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready as-if by a call to +__unique_future_wait__, and retrieves the result (whether that is a value or an exception).]] + +[[Returns:] [If the result type `R` is a reference, returns the stored reference. If `R` is `void`, there is no return +value. Otherwise, returns an rvalue-reference to the value stored in the asynchronous result.]] + +[[Postconditions:] [[unique_future_is_ready_link `this->is_ready()`] returns `true`. [unique_future_get_state_link +`this->get_state()`] returns __ready__.]] + +[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result +associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception stored in the +asynchronous result in place of a value.]] + +[[Notes:] [`get()` is an ['interruption point].]] + +] + +[endsect] + +[section:wait Member function `wait()`] + + void wait(); + +[variablelist + +[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready. If the result is not ready on +entry, and the result has a ['wait callback] set, that callback is invoked prior to waiting.]] + +[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result +associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the +['wait callback] if such a callback is called.]] + +[[Postconditions:] [[unique_future_is_ready_link `this->is_ready()`] returns `true`. [unique_future_get_state_link +`this->get_state()`] returns __ready__.]] + +[[Notes:] [`wait()` is an ['interruption point].]] + +] + +[endsect] + +[section:timed_wait_duration Member function `timed_wait()`] + + template + bool timed_wait(Duration const& wait_duration); + +[variablelist + +[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready, or the time specified by +`wait_duration` has elapsed. If the result is not ready on entry, and the result has a ['wait callback] set, that callback is +invoked prior to waiting.]] + +[[Returns:] [`true` if `*this` is associated with an asynchronous result, and that result is ready before the specified time has +elapsed, `false` otherwise.]] + +[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result +associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the +['wait callback] if such a callback is called.]] + +[[Postconditions:] [If this call returned `true`, then [unique_future_is_ready_link `this->is_ready()`] returns `true` and +[unique_future_get_state_link `this->get_state()`] returns __ready__.]] + +[[Notes:] [`timed_wait()` is an ['interruption point]. `Duration` must be a type that meets the Boost.DateTime time duration requirements.]] + +] + +[endsect] + +[section:timed_wait_absolute Member function `timed_wait()`] + + bool timed_wait(boost::system_time const& wait_timeout); + +[variablelist + +[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready, or the time point specified by +`wait_timeout` has passed. If the result is not ready on entry, and the result has a ['wait callback] set, that callback is invoked +prior to waiting.]] + +[[Returns:] [`true` if `*this` is associated with an asynchronous result, and that result is ready before the specified time has +passed, `false` otherwise.]] + +[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result +associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the +['wait callback] if such a callback is called.]] + +[[Postconditions:] [If this call returned `true`, then [unique_future_is_ready_link `this->is_ready()`] returns `true` and +[unique_future_get_state_link `this->get_state()`] returns __ready__.]] + +[[Notes:] [`timed_wait()` is an ['interruption point].]] + +] + +[endsect] + + +[section:is_ready Member function `is_ready()`] + + bool is_ready(); + +[variablelist + +[[Effects:] [Checks to see if the asynchronous result associated with `*this` is set.]] + +[[Returns:] [`true` if `*this` is associated with an asynchronous result, and that result is ready for retrieval, `false` +otherwise.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:has_value Member function `has_value()`] + + bool has_value(); + +[variablelist + +[[Effects:] [Checks to see if the asynchronous result associated with `*this` is set with a value rather than an exception.]] + +[[Returns:] [`true` if `*this` is associated with an asynchronous result, that result is ready for retrieval, and the result is a +stored value, `false` otherwise.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:has_exception Member function `has_exception()`] + + bool has_exception(); + +[variablelist + +[[Effects:] [Checks to see if the asynchronous result associated with `*this` is set with an exception rather than a value.]] + +[[Returns:] [`true` if `*this` is associated with an asynchronous result, that result is ready for retrieval, and the result is a +stored exception, `false` otherwise.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:get_state Member function `get_state()`] + + future_state::state get_state(); + +[variablelist + +[[Effects:] [Determine the state of the asynchronous result associated with `*this`, if any.]] + +[[Returns:] [__uninitialized__ if `*this` is not associated with an asynchronous result. __ready__ if the asynchronous result +associated with `*this` is ready for retrieval, __waiting__ otherwise.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + + +[endsect] + +[section:shared_future `shared_future` class template] + + template + class shared_future + { + public: + typedef future_state::state state; + + shared_future(); + ~shared_future(); + + // copy support + shared_future(shared_future const& other); + shared_future& operator=(shared_future const& other); + + // move support + shared_future(shared_future && other); + shared_future(unique_future && other); + shared_future& operator=(shared_future && other); + shared_future& operator=(unique_future && other); + + void swap(shared_future& other); + + // retrieving the value + R get(); + + // functions to check state, and wait for ready + state get_state() const; + bool is_ready() const; + bool has_exception() const; + bool has_value() const; + + // waiting for the result to be ready + void wait() const; + template + bool timed_wait(Duration const& rel_time) const; + bool timed_wait_until(boost::system_time const& abs_time) const; + }; + +[section:default_constructor Default Constructor] + + shared_future(); + +[variablelist + +[[Effects:] [Constructs an uninitialized future.]] + +[[Postconditions:] [[shared_future_is_ready_link `this->is_ready`] returns `false`. [shared_future_get_state_link +`this->get_state()`] returns __uninitialized__.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:get Member function `get()`] + + const R& get(); + +[variablelist + +[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready as-if by a call to +__shared_future_wait__, and returns a `const` reference to the result.]] + +[[Returns:] [If the result type `R` is a reference, returns the stored reference. If `R` is `void`, there is no return +value. Otherwise, returns a `const` reference to the value stored in the asynchronous result.]] + +[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the +result associated with `*this` is not ready at the point of the call, and the current thread is interrupted.]] + +[[Notes:] [`get()` is an ['interruption point].]] + +] + +[endsect] + +[section:wait Member function `wait()`] + + void wait(); + +[variablelist + +[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready. If the result is not ready on +entry, and the result has a ['wait callback] set, that callback is invoked prior to waiting.]] + +[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result +associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the +['wait callback] if such a callback is called.]] + +[[Postconditions:] [[shared_future_is_ready_link `this->is_ready()`] returns `true`. [shared_future_get_state_link +`this->get_state()`] returns __ready__.]] + +[[Notes:] [`wait()` is an ['interruption point].]] + +] + +[endsect] + +[section:timed_wait_duration Member function `timed_wait()`] + + template + bool timed_wait(Duration const& wait_duration); + +[variablelist + +[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready, or the time specified by +`wait_duration` has elapsed. If the result is not ready on entry, and the result has a ['wait callback] set, that callback is +invoked prior to waiting.]] + +[[Returns:] [`true` if `*this` is associated with an asynchronous result, and that result is ready before the specified time has +elapsed, `false` otherwise.]] + +[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result +associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the +['wait callback] if such a callback is called.]] + +[[Postconditions:] [If this call returned `true`, then [shared_future_is_ready_link `this->is_ready()`] returns `true` and +[shared_future_get_state_link `this->get_state()`] returns __ready__.]] + +[[Notes:] [`timed_wait()` is an ['interruption point]. `Duration` must be a type that meets the Boost.DateTime time duration requirements.]] + +] + +[endsect] + +[section:timed_wait_absolute Member function `timed_wait()`] + + bool timed_wait(boost::system_time const& wait_timeout); + +[variablelist + +[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready, or the time point specified by +`wait_timeout` has passed. If the result is not ready on entry, and the result has a ['wait callback] set, that callback is invoked +prior to waiting.]] + +[[Returns:] [`true` if `*this` is associated with an asynchronous result, and that result is ready before the specified time has +passed, `false` otherwise.]] + +[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result +associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the +['wait callback] if such a callback is called.]] + +[[Postconditions:] [If this call returned `true`, then [shared_future_is_ready_link `this->is_ready()`] returns `true` and +[shared_future_get_state_link `this->get_state()`] returns __ready__.]] + +[[Notes:] [`timed_wait()` is an ['interruption point].]] + +] + +[endsect] + +[section:is_ready Member function `is_ready()`] + + bool is_ready(); + +[variablelist + +[[Effects:] [Checks to see if the asynchronous result associated with `*this` is set.]] + +[[Returns:] [`true` if `*this` is associated with an asynchronous result, and that result is ready for retrieval, `false` +otherwise.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:has_value Member function `has_value()`] + + bool has_value(); + +[variablelist + +[[Effects:] [Checks to see if the asynchronous result associated with `*this` is set with a value rather than an exception.]] + +[[Returns:] [`true` if `*this` is associated with an asynchronous result, that result is ready for retrieval, and the result is a +stored value, `false` otherwise.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:has_exception Member function `has_exception()`] + + bool has_exception(); + +[variablelist + +[[Effects:] [Checks to see if the asynchronous result associated with `*this` is set with an exception rather than a value.]] + +[[Returns:] [`true` if `*this` is associated with an asynchronous result, that result is ready for retrieval, and the result is a +stored exception, `false` otherwise.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:get_state Member function `get_state()`] + + future_state::state get_state(); + +[variablelist + +[[Effects:] [Determine the state of the asynchronous result associated with `*this`, if any.]] + +[[Returns:] [__uninitialized__ if `*this` is not associated with an asynchronous result. __ready__ if the asynchronous result +associated with `*this` is ready for retrieval, __waiting__ otherwise.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + + +[endsect] + +[section:promise `promise` class template] + + template + class promise + { + promise(promise & rhs);// = delete; + promise & operator=(promise & rhs);// = delete; + public: + // template explicit promise(Allocator a); + + promise(); + ~promise(); + + // Move support + promise(promise && rhs); + promise & operator=(promise&& rhs); + + void swap(promise& other); + // Result retrieval + unique_future get_future(); + + // Set the value + void set_value(R& r); + void set_value(R&& r); + void set_exception(boost::exception_ptr e); + + template + void set_wait_callback(F f); + }; + +[section:default_constructor Default Constructor] + + promise(); + +[variablelist + +[[Effects:] [Constructs a new __promise__ with no associated result.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:move_constructor Move Constructor] + + promise(promise && other); + +[variablelist + +[[Effects:] [Constructs a new __promise__, and transfers ownership of the result associated with `other` to `*this`, leaving `other` +with no associated result.]] + +[[Throws:] [Nothing.]] + +[[Notes:] [If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.]] + +] + +[endsect] + +[section:move_assignment Move Assignment Operator] + + promise& operator=(promise && other); + +[variablelist + +[[Effects:] [Transfers ownership of the result associated with `other` to `*this`, leaving `other` with no associated result. If there +was already a result associated with `*this`, and that result was not ['ready], sets any futures associated with that result to +['ready] with a __broken_promise__ exception as the result. ]] + +[[Throws:] [Nothing.]] + +[[Notes:] [If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.]] + +] + +[endsect] + +[section:destructor Destructor] + + ~promise(); + +[variablelist + +[[Effects:] [Destroys `*this`. If there was a result associated with `*this`, and that result is not ['ready], sets any futures +associated with that task to ['ready] with a __broken_promise__ exception as the result.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:get_future Member Function `get_future()`] + + unique_future get_future(); + +[variablelist + +[[Effects:] [If `*this` was not associated with a result, allocate storage for a new asynchronous result and associate it with +`*this`. Returns a __unique_future__ associated with the result associated with `*this`. ]] + +[[Throws:] [__future_already_retrieved__ if the future associated with the task has already been retrieved. `std::bad_alloc` if any +memory necessary could not be allocated.]] + +] + +[endsect] + +[section:set_value Member Function `set_value()`] + + void set_value(R&& r); + void set_value(const R& r); + void promise::set_value(R& r); + void promise::set_value(); + +[variablelist + +[[Effects:] [If `*this` was not associated with a result, allocate storage for a new asynchronous result and associate it with +`*this`. Store the value `r` in the asynchronous result associated with `*this`. Any threads blocked waiting for the asynchronous +result are woken.]] + +[[Postconditions:] [All futures waiting on the asynchronous result are ['ready] and __unique_future_has_value__ or +__shared_future_has_value__ for those futures shall return `true`.]] + +[[Throws:] [__promise_already_satisfied__ if the result associated with `*this` is already ['ready]. `std::bad_alloc` if the memory +required for storage of the result cannot be allocated. Any exception thrown by the copy or move-constructor of `R`.]] + +] + +[endsect] + +[section:set_exception Member Function `set_exception()`] + + void set_exception(boost::exception_ptr e); + +[variablelist + +[[Effects:] [If `*this` was not associated with a result, allocate storage for a new asynchronous result and associate it with +`*this`. Store the exception `e` in the asynchronous result associated with `*this`. Any threads blocked waiting for the asynchronous +result are woken.]] + +[[Postconditions:] [All futures waiting on the asynchronous result are ['ready] and __unique_future_has_exception__ or +__shared_future_has_exception__ for those futures shall return `true`.]] + +[[Throws:] [__promise_already_satisfied__ if the result associated with `*this` is already ['ready]. `std::bad_alloc` if the memory +required for storage of the result cannot be allocated.]] + +] + +[endsect] + +[section:set_wait_callback Member Function `set_wait_callback()`] + + template + void set_wait_callback(F f); + +[variablelist + +[[Preconditions:] [The expression `f(t)` where `t` is a lvalue of type __packaged_task__ shall be well-formed. Invoking a copy of +`f` shall have the same effect as invoking `f`]] + +[[Effects:] [Store a copy of `f` with the asynchronous result associated with `*this` as a ['wait callback]. This will replace any +existing wait callback store alongside that result. If a thread subsequently calls one of the wait functions on a __unique_future__ +or __shared_future__ associated with this result, and the result is not ['ready], `f(*this)` shall be invoked.]] + +[[Throws:] [`std::bad_alloc` if memory cannot be allocated for the required storage.]] + +] + +[endsect] + +[endsect] + +[section:packaged_task `packaged_task` class template] + + template + class packaged_task + { + packaged_task(packaged_task&);// = delete; + packaged_task& operator=(packaged_task&);// = delete; + + public: + // construction and destruction + template + explicit packaged_task(F const& f); + + explicit packaged_task(R(*f)()); + + template + explicit packaged_task(F&& f); + + // template + // explicit packaged_task(F const& f, Allocator a); + // template + // explicit packaged_task(F&& f, Allocator a); + + ~packaged_task() + {} + + // move support + packaged_task(packaged_task&& other); + packaged_task& operator=(packaged_task&& other); + + void swap(packaged_task& other); + // result retrieval + unique_future get_future(); + + // execution + void operator()(); + + template + void set_wait_callback(F f); + }; + +[section:task_constructor Task Constructor] + + template + packaged_task(F const &f); + + packaged_task(R(*f)()); + + template + packaged_task(F&&f); + +[variablelist + +[[Preconditions:] [`f()` is a valid expression with a return type convertible to `R`. Invoking a copy of `f` shall behave the same +as invoking `f`.]] + +[[Effects:] [Constructs a new __packaged_task__ with a copy of `f` stored as the associated task.]] + +[[Throws:] [Any exceptions thrown by the copy (or move) constructor of `f`. `std::bad_alloc` if memory for the internal data +structures could not be allocated.]] + +] + +[endsect] + +[section:move_constructor Move Constructor] + + packaged_task(packaged_task && other); + +[variablelist + +[[Effects:] [Constructs a new __packaged_task__, and transfers ownership of the task associated with `other` to `*this`, leaving `other` +with no associated task.]] + +[[Throws:] [Nothing.]] + +[[Notes:] [If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.]] + +] + +[endsect] + +[section:move_assignment Move Assignment Operator] + + packaged_task& operator=(packaged_task && other); + +[variablelist + +[[Effects:] [Transfers ownership of the task associated with `other` to `*this`, leaving `other` with no associated task. If there +was already a task associated with `*this`, and that task has not been invoked, sets any futures associated with that task to +['ready] with a __broken_promise__ exception as the result. ]] + +[[Throws:] [Nothing.]] + +[[Notes:] [If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.]] + +] + +[endsect] + +[section:destructor Destructor] + + ~packaged_task(); + +[variablelist + +[[Effects:] [Destroys `*this`. If there was a task associated with `*this`, and that task has not been invoked, sets any futures +associated with that task to ['ready] with a __broken_promise__ exception as the result.]] + +[[Throws:] [Nothing.]] + +] + +[endsect] + +[section:get_future Member Function `get_future()`] + + unique_future get_future(); + +[variablelist + +[[Effects:] [Returns a __unique_future__ associated with the result of the task associated with `*this`. ]] + +[[Throws:] [__task_moved__ if ownership of the task associated with `*this` has been moved to another instance of +__packaged_task__. __future_already_retrieved__ if the future associated with the task has already been retrieved.]] + +] + +[endsect] + +[section:call_operator Member Function `operator()()`] + + void operator()(); + +[variablelist + +[[Effects:] [Invoke the task associated with `*this` and store the result in the corresponding future. If the task returns normally, +the return value is stored as the asynchronous result, otherwise the exception thrown is stored. Any threads blocked waiting for the +asynchronous result associated with this task are woken.]] + +[[Postconditions:] [All futures waiting on the asynchronous result are ['ready]]] + +[[Throws:] [__task_moved__ if ownership of the task associated with `*this` has been moved to another instance of +__packaged_task__. __task_already_started__ if the task has already been invoked.]] + +] + +[endsect] + +[section:set_wait_callback Member Function `set_wait_callback()`] + + template + void set_wait_callback(F f); + +[variablelist + +[[Preconditions:] [The expression `f(t)` where `t` is a lvalue of type __packaged_task__ shall be well-formed. Invoking a copy of +`f` shall have the same effect as invoking `f`]] + +[[Effects:] [Store a copy of `f` with the task associated with `*this` as a ['wait callback]. This will replace any existing wait +callback store alongside that task. If a thread subsequently calls one of the wait functions on a __unique_future__ or +__shared_future__ associated with this task, and the result of the task is not ['ready], `f(*this)` shall be invoked.]] + +[[Throws:] [__task_moved__ if ownership of the task associated with `*this` has been moved to another instance of +__packaged_task__.]] + +] + +[endsect] + + +[endsect] + +[section:wait_for_all Non-member function `wait_for_all()`] + + template + void wait_for_all(Iterator begin,Iterator end); + + template + void wait_for_all(F1& f1,F2& f2); + + template + void wait_for_all(F1& f1,F2& f2,F3& f3); + + template + void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4); + + template + void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5); + +[variablelist + +[[Preconditions:] [The types `Fn` shall be specializations of +__unique_future__ or __shared_future__, and `Iterator` shall be a +forward iterator with a `value_type` which is a specialization of +__unique_future__ or __shared_future__.]] + +[[Effects:] [Waits until all of the specified futures are ['ready].]] + +[[Throws:] [Any exceptions thrown by a call to `wait()` on the specified futures.]] + +[[Notes:] [`wait_for_all()` is an ['interruption point].]] + +] + + +[endsect] + + +[endsect] diff --git a/libs/task/doc/spin_futures.qbk b/libs/task/doc/spin_futures.qbk new file mode 100644 index 00000000..ec1d016b --- /dev/null +++ b/libs/task/doc/spin_futures.qbk @@ -0,0 +1,181 @@ +[/ + (C) Copyright 2008-9 Anthony Williams. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] + +[section:futures Futures] + +[template future_state_link[link_text] [link task.spin_synchronization.futures.reference.future_state [link_text]]] +[def __uninitialized__ [future_state_link `boost::future_state::uninitialized`]] +[def __ready__ [future_state_link `boost::future_state::ready`]] +[def __waiting__ [future_state_link `boost::future_state::waiting`]] + +[def __future_uninitialized__ `boost::future_uninitialized`] +[def __broken_promise__ `boost::broken_promise`] +[def __future_already_retrieved__ `boost::future_already_retrieved`] +[def __task_moved__ `boost::task_moved`] +[def __task_already_started__ `boost::task_already_started`] +[def __promise_already_satisfied__ `boost::promise_already_satisfied`] + +[def __task_interrupted__ `boost::task_interrupted`] + + +[template unique_future_link[link_text] [link task.spin_synchronization.futures.reference.unique_future [link_text]]] +[def __unique_future__ [unique_future_link `boost::unique_future`]] + +[template unique_future_get_link[link_text] [link task.spin_synchronization.futures.reference.unique_future.get [link_text]]] +[def __unique_future_get__ [unique_future_get_link `boost::unique_future::get()`]] + +[template unique_future_wait_link[link_text] [link task.spin_synchronization.futures.reference.unique_future.wait [link_text]]] +[def __unique_future_wait__ [unique_future_wait_link `boost::unique_future::wait()`]] + +[template unique_future_is_ready_link[link_text] [link task.spin_synchronization.futures.reference.unique_future.is_ready [link_text]]] +[def __unique_future_is_ready__ [unique_future_is_ready_link `boost::unique_future::is_ready()`]] + +[template unique_future_has_value_link[link_text] [link task.spin_synchronization.futures.reference.unique_future.has_value [link_text]]] +[def __unique_future_has_value__ [unique_future_has_value_link `boost::unique_future::has_value()`]] + +[template unique_future_has_exception_link[link_text] [link task.spin_synchronization.futures.reference.unique_future.has_exception [link_text]]] +[def __unique_future_has_exception__ [unique_future_has_exception_link `boost::unique_future::has_exception()`]] + +[template unique_future_get_state_link[link_text] [link task.spin_synchronization.futures.reference.unique_future.get_state [link_text]]] +[def __unique_future_get_state__ [unique_future_get_state_link `boost::unique_future::get_state()`]] + +[template shared_future_link[link_text] [link task.spin_synchronization.futures.reference.shared_future [link_text]]] +[def __shared_future__ [shared_future_link `boost::shared_future`]] + +[template shared_future_get_link[link_text] [link task.spin_synchronization.futures.reference.shared_future.get [link_text]]] +[def __shared_future_get__ [shared_future_get_link `boost::shared_future::get()`]] + +[template shared_future_wait_link[link_text] [link task.spin_synchronization.futures.reference.shared_future.wait [link_text]]] +[def __shared_future_wait__ [shared_future_wait_link `boost::shared_future::wait()`]] + +[template shared_future_is_ready_link[link_text] [link task.spin_synchronization.futures.reference.shared_future.is_ready [link_text]]] +[def __shared_future_is_ready__ [shared_future_is_ready_link `boost::shared_future::is_ready()`]] + +[template shared_future_has_value_link[link_text] [link task.spin_synchronization.futures.reference.shared_future.has_value [link_text]]] +[def __shared_future_has_value__ [shared_future_has_value_link `boost::shared_future::has_value()`]] + +[template shared_future_has_exception_link[link_text] [link task.spin_synchronization.futures.reference.shared_future.has_exception [link_text]]] +[def __shared_future_has_exception__ [shared_future_has_exception_link `boost::shared_future::has_exception()`]] + +[template shared_future_get_state_link[link_text] [link task.spin_synchronization.futures.reference.shared_future.get_state [link_text]]] +[def __shared_future_get_state__ [shared_future_get_state_link `boost::shared_future::get_state()`]] + +[template promise_link[link_text] [link task.spin_synchronization.futures.reference.promise [link_text]]] +[def __promise__ [promise_link `boost::promise`]] + +[template packaged_task_link[link_text] [link task.spin_synchronization.futures.reference.packaged_task [link_text]]] +[def __packaged_task__ [packaged_task_link `boost::packaged_task`]] +[/ +[template wait_for_any_link[link_text] [link task.spin_synchronization.futures.reference.wait_for_any [link_text]]] +[def __wait_for_any__ [wait_for_any_link `boost::wait_for_any()`]] +] +[template wait_for_all_link[link_text] [link task.spin_synchronization.futures.reference.wait_for_all [link_text]]] +[def __wait_for_all__ [wait_for_all_link `boost::wait_for_all()`]] + + +[section:overview Overview] + +The futures library provides a means of handling synchronous future values, whether those values are generated by another task, or +on a single task in response to external stimuli, or on-demand. + +This is done through the provision of four class templates: __unique_future__ and __shared_future__ which are used to retrieve the +asynchronous results, and __promise__ and __packaged_task__ which are used to generate the asynchronous results. + +An instance of __unique_future__ holds the one and only reference to a result. Ownership can be transferred between instances using +the move constructor or move-assignment operator, but at most one instance holds a reference to a given asynchronous result. When +the result is ready, it is returned from __unique_future_get__ by rvalue-reference to allow the result to be moved or copied as +appropriate for the type. + +On the other hand, many instances of __shared_future__ may reference the same result. Instances can be freely copied and assigned, +and __shared_future_get__ returns a `const` reference so that multiple calls to __shared_future_get__ are safe. You can move an +instance of __unique_future__ into an instance of __shared_future__, thus transferring ownership of the associated asynchronous +result, but not vice-versa. + +You can wait for futures either individually or with one of the __wait_for_any__ and __wait_for_all__ functions. + +[endsect] + +[section:creating Creating asynchronous values] + +You can set the value in a future with either a __promise__ or a __packaged_task__. A __packaged_task__ is a callable object that +wraps a function or callable object. When the packaged task is invoked, it invokes the contained function in turn, and populates a +future with the return value. This is an answer to the perennial question: "how do I return a value from a task?": package the +function you wish to run as a __packaged_task__ and pass the packaged task to the task constructor. The future retrieved from the +packaged task can then be used to obtain the return value. If the function throws an exception, that is stored in the future in +place of the return value. + + int calculate_the_answer_to_life_the_universe_and_everything() + { return 42; } + + boost::tasks::spin::packaged_task< int > pt( calculate_the_answer_to_life_the_universe_and_everything); + boost::tasks::spin::unique_future< int > fi( pt.get_future() ); + + boost::task task( boost::move( pt) ); // launch task on a task + + fi.wait(); // wait for it to finish + + assert( fi.is_ready() ); + assert( fi.has_value() ); + assert( ! fi.has_exception() ); + assert( fi.get_state() == boost::future_state::ready); + assert( fi.get() == 42); + + +A __promise__ is a bit more low level: it just provides explicit functions to store a value or an exception in the associated +future. A promise can therefore be used where the value may come from more than one possible source, or where a single operation may +produce multiple values. + + boost::tasks::spin::promise< int > pi; + boost::tasks::spin::unique_future< int > fi; + fi = pi.get_future(); + + pi.set_value( 42); + + assert( fi.is_ready() ); + assert( fi.has_value() ); + assert( ! fi.has_exception() ); + assert( fi.get_state() == boost::future_state::ready); + assert( fi.get() == 42); + +[endsect] + +[section:lazy_futures Wait Callbacks and Lazy Futures] + +Both __promise__ and __packaged_task__ support ['wait callbacks] that are invoked when a task blocks in a call to `wait()` or +`timed_wait()` on a future that is waiting for the result from the __promise__ or __packaged_task__, in the task that is doing the +waiting. These can be set using the `set_wait_callback()` member function on the __promise__ or __packaged_task__ in question. + +This allows ['lazy futures] where the result is not actually computed until it is needed by some task. In the example below, the +call to `f.get()` invokes the callback `invoke_lazy_task`, which runs the task to set the value. If you remove the call to +`f.get()`, the task is not ever run. + + int calculate_the_answer_to_life_the_universe_and_everything() + { return 42; } + + void invoke_lazy_task( boost::tasks::spin::packaged_task< int > & task) + { + try + { task(); } + catch( boost::task_already_started const&) + {} + } + + int main() + { + boost::tasks::spin::packaged_task< int > task( calculate_the_answer_to_life_the_universe_and_everything); + task.set_wait_callback( invoke_lazy_task); + boost::tasks::spin::unique_future< int > f( task.get_future() ); + + assert( f.get() == 42); + } + + +[endsect] + +[include spin_future_ref.qbk] + +[endsect] diff --git a/libs/task/doc/spin_mutexes.qbk b/libs/task/doc/spin_mutexes.qbk new file mode 100644 index 00000000..192b6342 --- /dev/null +++ b/libs/task/doc/spin_mutexes.qbk @@ -0,0 +1,43 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] + +[section:mutex_types Mutex Types] + +[section:mutex Class `mutex`] + + #include + + class mutex : private boost::noncopyable + { + public: + mutex(); + ~mutex(); + + void lock(); + + bool timed_lock( system_time const& abs_time); + + template< typename TimeDuration > + bool timed_lock( TimeDuration const& rel_time); + + bool try_lock(); + + void unlock(); + + typedef unique_lock< mutex > scoped_lock; + }; + +__spin_mutex__ implements the __lockable_concept__ to provide an exclusive-ownership mutex. At most one task can own the +lock on a given instance of __spin_mutex__ at any time. Multiple concurrent calls to __lock__, __try_lock__ and +__unlock__ shall be permitted. + +[note __spin_mutex__ is ['not] bound to a __scheduler__ and can only be used by tasks +managed by different schedulers or code not running in a task.] + +[endsect] + +[endsect] diff --git a/libs/task/doc/spin_synchronization.qbk b/libs/task/doc/spin_synchronization.qbk new file mode 100644 index 00000000..ea7f7896 --- /dev/null +++ b/libs/task/doc/spin_synchronization.qbk @@ -0,0 +1,18 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + +[section:spin_synchronization Synchronization] + +Synch. objects using spin-wait ops. reside in namespace `boost::tasks::spin`. + +[include spin_mutexes.qbk] +[include spin_condition_variables.qbk] +[include spin_barrier.qbk] +[include spin_event_variables.qbk] +[include spin_fifos.qbk] +[include spin_futures.qbk] +[endsect] diff --git a/libs/task/doc/static_pool.qbk b/libs/task/doc/static_pool.qbk new file mode 100644 index 00000000..efed2a94 --- /dev/null +++ b/libs/task/doc/static_pool.qbk @@ -0,0 +1,301 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:static_pool Thread-Pool with fixed ] + +__boost_task__ provides __static_pool__ - which contains an fixed set of pre-forked __worker_threads__ (the size of the pool doesn't change during its lifetime). +__static_pool__ supports move semantics. + + boost::task::_static_pool< // pool type + boost::task::unbounded_fifo // queue where application threads enqueue tasks + > pool( + boost::task::poolsize( 6), // pool with 6 pre-forked worker-threads + boost::posix_time::posix_time::milliseconds( 50), // time to sleep if no work-item available + boost::task::scanns( 10) ); // iterations over local-queues before sleep + + +The first argument of the constructor specifies how many __worker_threads__ the pool will contain. The second +and third argument are used by the [link_work_stealing __work_stealing__] algorithm. + +[note If __bounded_queue__ is used as queuing policy the constructor has two additional arguments . ] + +__static_pool__ provides functionality to check the status of the pool - __fn_closed__ returns true when the pool was +shutdown and __fn_size__returns the number of __worker_threads__. + +[section:static_pool Class template `static_pool`] + + #include + + template< typename Channel > + class static_pool : private noncopyable + { + public: + static_pool(); + + explicit static_pool( + poolsize const& psize, + posix_time::time_duration const& asleep = posix_time::microseconds( 10), + scanns const& scns = scanns( 20) ); + + explicit static_pool( + poolsize const& psize, + high_watermark const& hwm, + low_watermark const& lwm, + posix_time::time_duration const& asleep = posix_time::milliseconds( 100), + scanns const& scns = scanns( 20) ); + + static_pool( static_pool &&); + + static_pool & operator=( static_pool &&); + + # if defined(BOOST_HAS_PROCESSOR_BINDINGS) + explicit static_pool( + <>, + posix_time::time_duration const& asleep = posix_time::microseconds( 10), + scanns const& scns = scanns( 20) ); + + explicit static_pool( + <>, + high_watermark const& hwm, + low_watermark const& lwm, + posix_time::time_duration const& asleep = posix_time::milliseconds( 100), + scanns const& scns = scanns( 20) ); + + static <> bind_to_processors(); + # endif + + ~static_pool(); + + std::size_t size(); + + void shutdown(); + void shutdown_now(); + + bool closed(); + + void interrupt_all_worker(); + + const std::size_t upper_bound(); + void upper_bound( high_watermark const& hwm); + const std::size_t lower_bound(); + void lower_bound( low_watermark const& lwm); + + template< typename R > + handle< R > submit( task< R > && t); + + template< typename R, typename Attr > + handle< R > submit( task< R > && t, Attr const& attr); + + void swap( static_pool & other); + + operator unspecified_bool_type() const; + bool operator!() const; + }; + + +[section `static_pool()`] +[variablelist +[[Effects:] [constructs an unitialized pool]] +[[Throws:] [nothing]] +] +[endsect] + +[section `explicit static_pool( + <>, + posix_time::time_duration const& asleep = posix_time::microseconds( 10), + scanns const& scns = scanns( 20) )`] +[variablelist +[[Preconditions:] [operating system provides functionality for processor binding]] +[[Effects:] [constructs a pool - for each processor a worker-thread is created and bound to one processor - global-queue can queue an unlimited number of tasks]] +[[Throws:] [`boost::thread_resource_error`, `boost::task::invalid_scanns`, `boost::task::invalid_timeduration`]] +[[Notes:] [constructor has to be called if a unbounded-queue is used and `bind_to_processors()` must be set as first argument]] +] +[endsect] + +[section `explicit static_pool( + poolsize const& psize, + posix_time::time_duration const& asleep = posix_time::microseconds( 10), + scanns const& scns = scanns( 20) )`] +[variablelist +[[Effects:] [constructs a pool containing psize worker-threads - global-queue can queue an unlimited number of tasks]] +[[Throws:] [`boost::thread_resource_error`, `boost::task::invalid_scanns`, `boost::task::invalid_timeduration`]] +[[Notes:] [constructor has to be called if a unbounded-queue is used]] +] +[endsect] + +[section `explicit static_pool( + <>, + high_watermark const& hwm, + low_watermark const& lwm, + posix_time::time_duration const& asleep = posix_time::milliseconds( 100), + scanns const& scns = scanns( 20) )`] +[variablelist +[[Preconditions:] [operating system provides functionality for processor binding]] +[[Effects:] [constructs a pool - for each processor a worker-thread is created and bound to one processor - global-queue can only queue a limited number of tasks]] +[[Throws:] [`boost::thread_resource_error`, `boost::task::invalid_scanns`, `boost::task::invalid_timeduration`, `boost::task::invalid_watermark`]] +[[Notes:] [constructor has to be called if a bounded-queue is used and `bind_to_processors()` must be set as first argument]] +] +[endsect] + +[section `explicit static_pool( + poolsize const& psize, + high_watermark const& hwm, + low_watermark const& lwm, + posix_time::time_duration const& asleep = posix_time::milliseconds( 100), + scanns const& scns = scanns( 20) )`] +[variablelist +[[Effects:] [constructs a pool containing psize worker-threads - global-queue can only queue a limited number of tasks]] +[[Throws:] [`boost::thread_resource_error`, `boost::task::invalid_scanns`, `boost::task::invalid_timeduration`, `boost::task::invalid_watermark`]] +[[Notes:] [constructor has to be called if a bounded-queue is used]] +] +[endsect] + +[section `static_pool( static_pool &&)`] +[variablelist +[[Effects:] [creates an pool out of another one which gets zeroed out]] +[[Throws:] [nothing]] +] +[endsect] + +[section `static_pool & operator=( static_pool &&)`] +[variablelist +[[Effects:] [creates an pool out of another one which gets zeroed out]] +[[Throws:] [nothing]] +] +[endsect] + +[section `~static_pool()`] +[variablelist +[[Effects:] [calls `shutdown()` if not already called]] +[[Throws:] [nothing]] +] +[endsect] + +[section `<> bind_to_processors()`] +[variablelist +[[Effects:] [used in order to let the pool create worker-threads as cores are available and bound the threads to the cores]] +[[Throws:] [nothing]] +] +[endsect] + +[section `std::size_t size()`] +[variablelist +[[Effects:] [returns how many worker-threads are running in the pool]] +[[Throws:] [`boost::task::pool_moved`]] +] +[endsect] + +[section `void shutdown()`] +[variablelist +[[Effects:] [deactivates the queue and joins all worker-threads - the pool is closed]] +[[Throws:] [`boost::thread_interrupted`, `boost::system::system_error`, `boost::task::pool_moved`]] +[[Notes:] [all pending tasks are processed]] +] +[endsect] + +[section `void shutdown_now()`] +[variablelist +[[Effects:] [deactivates the queue, send interruption request to all worker-threads and joins them - the pool is closed]] +[[Throws:] [`boost::thread_interrupted`, `boost::system::system_error`, `boost::task::pool_moved`]] +[[Notes:] [pending tasks are not processed but returned]] +] +[endsect] + +[section `void interrupt_all_worker()`] +[variablelist +[[Effects:] [interrupts all worker-threads without invalidating the pool]] +[[Throws:] [nothing]] +] +[endsect] + +[section `bool closed()`] +[variablelist +[[Effects:] [queries if the pool is closed (pool is shutdown)]] +[[Throws:] [`boost::task::pool_moved`]] +] +[endsect] + +[section `std::size_t upper_bound()`] +[variablelist +[[Preconditions:] [queue is of type bounded-queue]] +[[Effects:] [returns the upper bound of the bounded-queue]] +[[Throws:] [`boost::task::pool_moved`]] +[[Notes:] [can only be used if a bounded-queue is used]] +] +[endsect] + +[section `void upper_bound( high_watermark const& hwm)`] +[variablelist +[[Preconditions:] [queue is of type bounded-queue]] +[[Effects:] [sets the upper bound of the bounded-queue]] +[[Postconditions:] [`this->upper_bound() == hwm`]] +[[Throws:] [`boost::task::invalid_watermark`, `boost::task::pool_moved`]] +[[Notes:] [can only be used if a bounded-queue is used]] +] +[endsect] + +[section `std::size_t lower_bound()`] +[variablelist +[[Preconditions:] [queue is of type bounded-queue]] +[[Effects:] [returns the lower bound of the bounded-queue]] +[[Throws:] [`boost::task::pool_moved`]] +[[Notes:] [can only be used if a bounded-queue is used]] +] +[endsect] + +[section `void lower_bound( low_watermark const& lwm)`] +[variablelist +[[Preconditions:] [queue is of type bounded-queue]] +[[Effects:] [sets the lower bound of the bounded-queue]] +[[Postconditions:] [`this->lower_bound() == lwm`]] +[[Throws:] [`boost::task::invalid_watermark`, `boost::task::pool_moved`]] +[[Notes:] [can only be used if a bounded-queue is used]] +] +[endsect] + +[section `template< typename R > handle< R > submit( task< R > t)`] +[variablelist +[[Preconditions:] [has_attribute< pool >::value == false && ! closed()]] +[[Effects:] [moves an task to the pool and returns an associated handle]] +[[Throws:] [`boost::task::task_rejected`, `boost::task::pool_moved`]] +] +[endsect] + +[section `template< typename R, typename Attr > handle< R > submit( task< R > t, Attr const& attr)`] +[variablelist +[[Preconditions:] [has_attribute< pool >::value == true && ! closed()]] +[[Effects:] [moves an task to the pool and returns an associated handle - task is scheduled by the attribute]] +[[Throws:] [`boost::task::task_rejected`, `boost::task::pool_moved`]] +] +[endsect] + +[section `void swap( static_pool & other)`] +[variablelist +[[Effects:] [swaps pool]] +[[Throws:] [nothing]] +] +[endsect] + +[section `operator unspecified_bool_type() const`] +[variablelist +[[Effects:] [is static_pool valid == does static_pool own ownership]] +[[Throws:] [Nothing]] +] +[endsect] + +[section `bool operator!() const`] +[variablelist +[[Effects:] [is static_pool invalid == static_pool does not have ownership]] +[[Throws:] [Nothing]] +] +[endsect] + +[endsect] + +[endsect] + diff --git a/libs/task/doc/task.qbk b/libs/task/doc/task.qbk new file mode 100644 index 00000000..8f47904d --- /dev/null +++ b/libs/task/doc/task.qbk @@ -0,0 +1,126 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + +[article Task + [quickbook 1.4] + [authors [Kowalke, Oliver]] + [copyright 2009 Oliver Kowalke] + [purpose C++ Library for asynchronous execution of tasks] + [category text] + [license + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + [@http://www.boost.org/LICENSE_1_0.txt]) + ] +] + + +[def __boost_atomic__ [@http://www.chaoticmind.net/~hcb/projects/boost.atomic [*Boost.Atomic]]] +[def __boost_tasklet__ [@http://http://www.boostpro.com/vault/index.php?&direction=0&order=&directory=Concurrent%20Programming [*Boost.Tasklet]]] +[def __boost_move__ [@http://svn.boost.org/svn/boost/sandbox/move [*Boost.Move]]] +[def __boost_task__ [*Boost.Task]] +[def __boost_thread__ [@http://www.boost.org/libs/thread [*Boost.Thread]]] + +[template link_act[link_text] [link task.async_execution.async_completion_token [link_text]]] +[template link_as_subtask[link_text] [link task.async_execution.execution_policy.as_subtask [link_text]]] +[template link_async[link_text] [link task.async_execution.async [link_text]]] +[template link_ep[link_text] [link task.async_execution.execution_policy [link_text]]] +[template link_forkjoin[link_text] [link task.async_execution.execution_policy.threadpool.forkjoin [link_text]]] +[template link_handle[link_text] [link task.async_execution.async_completion_token.handle [link_text]]] +[template link_new_thread[link_text] [link task.async_execution.execution_policy.new_thread [link_text]]] +[template link_own_thread[link_text] [link task.async_execution.execution_policy.own_thread [link_text]]] +[template link_queue[link_text] [link task.async_execution.execution_policy.threadpool.queue [link_text]]] +[template link_task[link_text] [link task.task_management.task [link_text]]] +[template link_threadpool[link_text] [link task.async_execution.execution_policy.threadpool [link_text]]] +[template link_work_stealing[link_text] [link task.async_execution.execution_policy.threadpool.workstealing [link_text]]] + +[def __hardware_concurrency__ `boost::thread::hardware_concurrency()`] + +[def __as_sub_task__ `boost::tasks::as_sub_task`] +[def __attribute_type__ `boost::tasks::attribute_type`] +[def __bounded_queue__ `boost::tasks::bounded_queue`] +[def __dynamic_pool__ `boost::tasks::dynamic_pool`] +[def __has_attribute__ `boost::tasks::has_attribute`] +[def __hwm__ `boost::tasks::high_watermark`] +[def __lwm__ `boost::tasks::low_watermark`] +[def __pool_size__ `boost::tasks::poolsize`] +[def __priority__ `boost::tasks::priority`] +[def __replace_oldest__ `boost::tasks::replace_oldest`] +[def __static_pool__ `boost::tasks::static_pool`] +[def __system_time__ `boost::tasks::system_time`] +[def __take_oldest__ `boost::tasks::take_oldest`] +[def __task_interrupted__ `boost::tasks::task_interrupted`] +[def __unbounded_queue__ `boost::tasks::unbounded_queue`] + +[def __make_task__ `boost::tasks::make_task()`] +[def __waitfor_all__ `boost::tasks::waitfor_all()`] + +[def __fn_tt_interrupt__ `boost::this_task::interrupt()`] +[def __fn_interruption_requested__ `boost::this_thread::interruption_requested()`] +[def __fn_make_task__ `boost::tasks::make_task()`] +[def __fn_runs_in_pool__ `boost::this_task::runs_in_pool()`] +[def __fn_worker_id__ `boost::this_task::worker_id()`] +[def __fn_yield__ `boost::this_task::yield()`] + +[def __fn_async__ `boost::tasks::async()`] +[def __fn_make_task__ `boost::tasks::make_task()`] + +[def __fn_bind_to_processors__ `bind_to_processors()`] +[def __fn_closed__ `close()`] +[def __fn_get__ `get()`] +[def __fn_has_value__ `has_value()`] +[def __fn_has_exception__ `has_exception()`] +[def __fn_interrupt__ `interrupt()`] +[def __fn_interrupt_and_wait__ `interrupt_and_wait()`] +[def __fn_interrupt_and_wait_for__ `interrupt_and_wait_for()`] +[def __fn_interrupt_and_wait_until__ `interrupt_and_wait_until()`] +[def __fn_interruption_requested__ `interruption_requested()`] +[def __fn_is_ready__ `is_ready()`] +[def __fn_operator__ `operator()()`] +[def __fn_size__ `size()`] +[def __fn_shutdown__ `shutdown()`] +[def __fn_shutdown_now__ `shutdown_now()`] +[def __fn_wait__ `wait()`] +[def __fn_wait_for__ `wait_for()`] +[def __fn_wait_until__ `wait_until()`] + + +[def __act__ [link_act ['asynchronous-completion-token]]] +[def __async__ [link_async ['async()]]] +[def __ep__ [link_ep ['execution-policy]]] +[def __eps__ [link_ep ['execution-policies]]] +[def __blocked__ ['blocked]] +[def __callable__ ['callable]] +[def __coop_task__ ['cooperative task]] +[def __duration__ ['Duration]] +[def __fork_join__ [link_forkjoin ['fork/join]]] +[def __handle__ [link_handle ['handle]]] +[def __interruption_point__ ['interruption-point]] +[def __interruption_points__ ['interruption-points]] +[def __new_thread__ [link_new_thread ['new_thread]]] +[def __own_thread__ [link_own_thread ['own_thread]]] +[def __queue__ [link_queue ['queue]]] +[def __task__ [link_task ['task]]] +[def __thread_pool__ [link_threadpool ['thread-pool]]] +[def __thread_pools__ [link_threadpool ['thread-pools]]] +[def __sub_task__ ['sub-task]] +[def __sub_tasks__ ['sub-tasks]] +[def __work_item__ ['work-item]] +[def __work_items__ ['work-items]] +[def __work_stealing__ [link_work_stealing ['work-stealing]]] +[def __worker_queue__ ['worker-queue]] +[def __worker_thread__ ['worker-thread]] +[def __worker_threads__ ['worker-threads]] + + + +[include overview.qbk] +[include task_ref.qbk] +[include async_execution.qbk] +[include spin_synchronization.qbk] +[include todo.qbk] +[include acknowledgements.qbk] diff --git a/libs/task/doc/task_ref.qbk b/libs/task/doc/task_ref.qbk new file mode 100644 index 00000000..97459a10 --- /dev/null +++ b/libs/task/doc/task_ref.qbk @@ -0,0 +1,407 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:task_management Task Management] + +[heading Synopsis] + +A task is a chunk of code that can be executed independently and parallel. + +__task__ represents a __callable__ (provides __fn_operator__) object containing +the unit of code to be execute by a __ep__. Function __fn_async__ accepts a +__task__ and an __ep__ and returns a __act__ allowing to wait for the completion +of the computation of the task, for getting the result of a computation or for +transfering exceptions. +Objects of type __task__ are moveable. + + boost::task< int > t1( parallel_fib, 10); + boost::task< int > t2; // not-a-task + t2 = boost::move( t1); + + +[heading Launching] + +A new task is launched by passing an object of a callable type that can be invoked with no parameters to the +constructor and passing the task to function __fn_async__. The object is then copied into internal storage +of the __ep__, and invoked on the newly created task. + + struct callable + { void operator()(); }; + + boost::task copies_are_safe() + { + callable x; + return boost::task( x); + } // x is destroyed, but the newly-created task has a copy, so this is OK + + boost::task oops() + { + callable x; + return boost::task( boost::ref( x) ); + } // x is destroyed, but the newly-created task still has a reference + // this leads to undefined behaviour + +If you wish to construct an instance of __task__ with a function or callable object that requires arguments to be +supplied, this can be done by passing additional arguments to the __task__ constructor: + + void find_the_question( int the_answer); + + boost::task deep_thought_2( find_the_question, 42); + + // asynchr. execution + boost::tasks::async( boost::move( depp_thought_2) ); + +The arguments are ['copied] into the internal task structure: if a reference is required, use `boost::ref`, just as +for references to callable functions. + +For convinience `boost::tasks::make_task()` is provided: + + // asynchr. execution + boost::tasks::async( + boost::move( boost::tasks::make_task( find_the_question, 42) ), + boost::tasks::new_thread() ); + + +[heading Completion] + +In order to wait for the completion `boost::tasks::handle< R >::wait()` as to be called +on the __handle__ associated with the task. The fucntion returns if the task has completed. +The result of the task can be accessed via `boost::tasks::handle< R >::get()`. + + +[heading Interruption] + +Sometimes it is desired to stop a running task if it is no longer needed. In this case the thread is not killed - it +stops only at well-defined points (__interruption_points__) its execution. +A task can be ['interrupted] by invoking the __fn_interrupt__ member function from __act__ . When the interrupted +task next executes one of the specified __interruption_points__ (or if it is currently __blocked__ whilst executing +one) with interruption enabled, then a __task_interrupted__ exception will be thrown in the interrupted task. +In the context of ['task-interruption] a task is known as cooperative if it checks for an interruption request between +two __interruption_points__ via __fn_interruption_requested__ [footnote see [@http://www.ddj.com/architect/207100682 +'Interrupt Politely'], Herb Sutter]. + + +[heading Predefined Interruption Points] + +The following functions are ['interruption points], which will throw __task_interrupted__ if interruption is +enabled for the current tasklet, and interruption is requested for the current tasklet: + +* `boost::thread::join()` +* `boost::thread::timed_join()` +* `boost::condition_variable::wait()` +* `boost::condition_variable::timed_wait()` +* `boost::condition_variable_any::wait()` +* `boost::condition_variable_any::timed_wait()` +* `boost::tasks::spin::condition::wait()` +* `boost::tasks::spin::condition::timed_wait()` +* `boost::tasks::spin::auto_reset_event::wait()` +* `boost::tasks::spin::auto_reset_event::timed_wait()` +* `boost::tasks::spin::manual_reset_event::wait()` +* `boost::tasks::spin::manual_reset_event::timed_wait()` +* `boost::tasks::spin::count_down_event::wait()` +* `boost::tasks::spin::count_down_event::timed_wait()` +* `boost::thread::sleep()` +* `boost::this_thread::sleep()` +* `boost::this_thread::interruption_point()` + +A __interruption_point__ throws __task_interrupted__ if an interruption was requested. + + long cooperative( long n) + { + // interruption point + boost::this_thread::interruption_point(); + + if ( n == 0) return 0; + if ( n == 1) return 1; + long k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + // check if interruption was requested + if ( boost::this_thread::interruption_requested() ) + return; + + long tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + + // interruption point + boost::this_thread::interruption_point(); + + return k1; + } + + void main() + { + // task for computing fibonacci-number + boost::task< long > t( cooperative, 10) ); + + // execute task in new thread + // move task ownership to executor + boost::tasks::handle< long > h( + boost::tasks::async( + boost::move( t), + boost::tasks::new_thread() ) ); + + // interrupt task an wait until + // the task is removed by the worker-thread + h.interrupt_and_wait(); + + // access the result + // throws boost::task_interrupted + std::cout << "fibonacci(10) == " << h.get() << std::endl; + } + +If a task wishes to avoid being interrupted, it can create an instance of `boost::disable_interruption`. +The effects of an instance of `boost::disable_interruption` can be temporarily reversed by constructing an instance of +`boost::restore_interruption`, passing in the `boost::disable_interruption` object in question. This will restore the +interruption state to what it was when the `boost::disable_interruption` object was constructed, and then disable +interruption again when the `boost::restore_interruption` object is destroyed. + + +[heading Exceptions] + +Exceptions thrown by the function or callable object passed to the __task__ constructor are consumed by the thes +associated __act__. The exception will be re-thrown by the associated __handle__. + + void throwing() + { + ... + throw std::domain_error("domain error"); + ... + } + + void main() + { + // create task throwing std::domain_error + boost::task void > t( throwing); + + // execute task asynchron + // move task ownership to executor + boost::tasks::handle< void > h( + boost::tasks::async( + boost::move( t), + boost::tasks::new_thread() ) ); + + // wait for task completion + // throws std::domain_error + std::cout << h.wait() << std::endl; + } + +Exceptions rethrown by type are: + +* `std::bad_alloc` +* `std::bad_cast` +* `std::bad_exception` +* `std::bad_typeid` +* `std::domain_error` +* `std::invalid_argument` +* `std::ios_base::failure` +* `std::length_error` +* `std::logic_error` +* `std::out_of_range` +* `std::overflow_error` +* `std::range_error` +* `std::runtime_error` +* `std::underflow_error` +* `boost::exception` +* `boost::future_already_set` +* `boost::future_cancel` +* `boost::invalid_thread_argument` +* `boost::lock_error` +* `boost::broken_task` +* `boost::pool_moved` +* `boost::task_already_executed` +* `boost::task_interrupted` +* `boost::task_moved` +* `boost::task_interrupted` +* `boost::task_task_rejected` +* `boost::task_unitialized` + +[warning Don't use the sjlj exception model.] + + +[heading Parent Task] + +Top-level tasks have no parent. A parent task can create child tasks when it creates another task by using +__as_sub_task__ as __ep__. These children are implicitly treated as __sub_tasks__ of the parent task. It is +assumed that that __sub_tasks__ can be executed in any order because only overall operation speed matters +(enabling strategies for fast execution of unordered __work_items__ as [link_work_stealing __work_stealing__]). + + long serial_fib( long n) + { + if( n < 2) return n; + else return serial_fib( n - 1) + serial_fib( n - 2); + } + + long parallel_fib( long n, long cutof) + { + if ( n < cutof) return serial_fib( n); + else + { + // sub-task for computing fibonacci(n-1) + boost::task< long > t1( + parallel_fib, + n - 1, + cutof); + // sub-task for computing fibonacci(n-2) + boost::task< long > t2( + parallel_fib, + n - 2, + cutof); + + // submit a sub-tasks to thread-pool + // move task ownership to executor + boost::tasks::handle< long > h1( + boost::tasks::async( + boost::move( t1) ); + boost::tasks::handle< long > h2( + boost::tasks::async( + boost::move( t2) ); + + // computing fibonacci(n) by + // joining results of both sub-tasks + return h1.get() + h2.get(); + } + } + + void main() + { + // create thread-pool with five worker-threads + boost::tasks::static_pool< boost::tasks::unbounded_fifo > pool( boost::tasks::poolsize( 5) ); + + // create task computing fibonacci-number for 10 + boost::task< long > t( + parallel_fib, + 10, + 5); + + // execute task asynchron in thread-pool + // move task ownership to executor + boost::tasks::handle< long > h( + boost::tasks::async( + boost::move( t), + pool) ); + + // access result + std::cout << "fibonacci(10) == " << h.get() << std::endl; + } + + +[section:task Class template `task`] + + #include + + template< typename R > + class task : private noncopyable + { + public: + task(); + + task( R( * fn)()); + + template< typename Fn > + task( Fn fn); + + template< typename Fn, typename A0, ... > + task( Fn fn, A0 a0, ...); + + task( task && x); + task & operator=( task && x); + + void operator()(); + + void swap( task< R > & other); + + operator unspecified_bool_type() const; + bool operator!() const; + }; + + template< typename Fn > + task< R > make_task( Fn fn); + template< typename Fn, typename A0, ... > + task< R > make_task( Fn fn, A0 a0, ...); + + +[section:default_constructor `task()`] +[variablelist +[[Effects:] [constructs a __task__ instance that refers to __not_a_task__.]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:constructor `task( R( * fn)())`] +[variablelist +[[Effects:] [constructs a __task__ from a function pointer]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:constructor_2 `template< typename Fn > task( Fn const& fn)`] +[variablelist +[[Effects:] [constructs a __task__ from a function object]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:multi_argument_constructor `template< typename Fn, typename A0, ... > task( Fn fn, A0 a0, ...)`] +[variablelist +[[Effects:] [constructs a `boost::task::task< R >` from a function object and its arguments]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:operator `operator()()`] +[variablelist +[[Effects:] [executes task's internal function object]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:swap `swap( task< R > & other)`] +[variablelist +[[Effects:] [Exchanges the tasks associated with `*this` and `other`, so `*this` is associated with the task +associated with `other` prior to the call, and vice-versa.]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:unspec_operator `operator unspecified_bool_type() const`] +[variablelist +[[Returns:] [If `*this` refers to a task, the function returns true. Otherwise false.]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:not_operator `operator!()`] +[variablelist +[[Returns:] [If `*this` refers not to a task, the function returns true. Otherwise false.]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:non_member_make_task Non-member template function `make_task()`] + + #include + + template< typename Fn > + tasklet make_task( Fn fn); + + template< typename Fn, typename A0, ... > + tasklet make_task( Fn fn, A0 a0, ..., std::size_t stack_size); + +[variablelist +[[Effects:] [Creates a task.]] +] +[endsect] + +[endsect] + +[include this_task.qbk] + +[endsect] diff --git a/libs/task/doc/this_task.qbk b/libs/task/doc/this_task.qbk new file mode 100644 index 00000000..ae1309ee --- /dev/null +++ b/libs/task/doc/this_task.qbk @@ -0,0 +1,63 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + +[section:this_task Namespace `this_task`] + +[heading Synopsis] + +The non-member function in namespace `this_task` provide functionality to ['yield] the current task or +test if the task is executed inside a __thread_pool__. + +[section:block Non-member function `yield()`] + + #include + + namespace this_task + { + void yield() + } + +[variablelist +[[Effects:] [Blocks the current task and lets the worker-thread of the pool process another task. +The blocked task will be scheduled and return from this method.]] +[[Throws:] [nothing]] +] +[endsect] + +[section:runs_in_pool Non-member function `runs_in_pool()`] + + #include + + namespace this_task + { + bool runs_in_pool() + } + +[variablelist +[[Effects:] [Returns true if the current task is executed in a thread-pool.]] +[[Throws:] [nothing]] +[[Note:] [this function resides in namespace `boost::this_task`]] +] +[endsect] + + +[section:worker_id Non-member function `worker_id()`] + + #include + + namespace this_task + { + id worker_id() + } + +[variablelist +[[Effects:] [Returns returns the thread-id of the worker-thread form the thread-pool.]] +[[Throws:] [nothing]] +] +[endsect] + +[endsect] diff --git a/libs/task/doc/threadpool.qbk b/libs/task/doc/threadpool.qbk new file mode 100644 index 00000000..778412f0 --- /dev/null +++ b/libs/task/doc/threadpool.qbk @@ -0,0 +1,63 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:threadpool Execute in Thread-Pool] + +[heading Synopsis] + +Instead of creating a new thread and quickly throwing it away after the task is done, the overhead related to thread +creation and destruction can be avoided by running the __work_items__ on a __thread_pool__ (reusing an existing +__worker_thread__ instead). + +A __thread_pool__ maintains a global queue of __work_items__ to be processed, and a pool of __worker_threads__ which execute __work_items__ from the global queue. + +__boost_task__ provides __fn_async__ with support of executing an __task__ in __thread_pool__: + + + std::string echo( std::string const& msg) + { return msg; } + + void main() + { + // create a thread-pool with + // five worker-threads + // FIFO schduling of queued tasks + // and unlimited size of internal queue + boost::tasks::static_pool< boost::tasks::unbounded_fifo > pool( boost::tasks::poolsize( 5) ); + + // create task + boost::tasks::task< std::string > t( echo, "Hello World!"); + + // move task to executor + // let the task be executed by the thread-pool + boost::tasks::handle< std::string > h( + boost::tasks::async( + boost::move( t), + pool) ); + + // access the result + std::cout << h.get() << std::endl; + } + + +[important Tasks should not be too small (performance overhead dominates) and avoid blocking +tasks[footnote see [@http://www.ddj.com/go-parallel/article/showArticle.jhtml?articleID=216500409 +'Use Thread Pools Correctly'], Herb Sutter].] + + +[include static_pool.qbk] +[include meta_functions.qbk] +[include queue.qbk] +[include shutdown.qbk] +[include processor_binding.qbk] +[include work_stealing.qbk] +[include fork_join.qbk] + + +[endsect] + diff --git a/libs/task/doc/todo.qbk b/libs/task/doc/todo.qbk new file mode 100644 index 00000000..1faaa021 --- /dev/null +++ b/libs/task/doc/todo.qbk @@ -0,0 +1,22 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:todo Todo] + +* optimize memory ordering in atomic ops. + +* replace thread_specific_ptr by an specialiced implementation for `boost::tasks::detail::worker` + +* __dynamic_pool__ adds or removes __worker_threads__ from the __thread_pool__ + depending on the work-load (undersubscription/oversubscription). + +* task group defines a graph of interdependent tasks that can mostly be run in + parallel. The tasks in the group have dependencies or communicate with each + other. + +[endsect] diff --git a/libs/task/doc/work_stealing.qbk b/libs/task/doc/work_stealing.qbk new file mode 100644 index 00000000..e16d685d --- /dev/null +++ b/libs/task/doc/work_stealing.qbk @@ -0,0 +1,50 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + + +[section:workstealing Work-Stealing] + +Traditional __thread_pools__ do not scale because they use a single global-queue protected by a global-lock. The frequency at which +__worker_threads__ aquire the global-lock becomes a limiting factor for the throughput if: + +* the tasks become smaller + +* more processors are added + + +A __work_stealing__ algorithm can be used to solve this problem. It uses a special kind of queue which has two ends, and allows +lock-free pushes and pops from the ['private end] (accessed by the __worker_thread__ owning the queue), but requires synchronization +from the ['public end] (accessed by the other __worker_threads__). Synchronization is necessary when the queue is sufficiently small +that private and public operations could conflict. + +The pool contains one global-queue (__bounded_queue__ or __unbounded_queue__) protected by a global-lock and each __worker_thread__ +has its own private local worker-queue. If work is enqueued by a __worker_thread__ the __task__ is stored in the worker queue. If the +work is enqueued by a application thread it goes into the global queue. When __worker_threads__ are looking for work, they have +following search order: + +* look into the private worker-queue - tasks can be dequeued without locks + +* look in the global-queue - locks are used for synchronization + +* check other worker-queues ('stealing' tasks from private worker queues of other __worker_threads__) - requires locks + + +For a lot of recursively queued tasks (so called __sub_tasks__), the use of a worker-queue per thread substantially reduces the +synchronization necessary to complete the work. There are also fewer cache effects due to sharing of the global-queue information. + +Operations on the private worker queue are executed in LIFO order and operations on worker queues of other __worker_threads__ in +FIFO order (steals). + +* There are chances that memory is still hot in the cache, if the tasks are pushed in LIFO order into the private worker queue. + +* If a __worker_thread__ steals work in FIFO order, increases the chances that a larger 'chunk' of work will be stolen (the need +for other steals will be possibly reduced). Because the __sub_tasks__ are stored in LIFO order, the oldest items are closer to the +['public end] of the queue (forming a tree). Stealing such an older __task__ also steals a (probably) larger subtree of tasks +unfolded if the stolen work item get executed. Since a __sub_task__ is just part of a larger __task__, we don’t need to worry about +execution order. + +[endsect] diff --git a/libs/task/examples/Jamfile.v2 b/libs/task/examples/Jamfile.v2 new file mode 100644 index 00000000..28fa38c6 --- /dev/null +++ b/libs/task/examples/Jamfile.v2 @@ -0,0 +1,37 @@ +# Boost.Task Library Examples Jamfile + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For more information, see http://www.boost.org/ + +import os ; + +if [ os.name ] = SOLARIS +{ + lib socket ; +} + +project boost/task/example + : requirements + ../../tasklet/build//boost_tasklet + ../../thread/build//boost_thread + ../../system/build//boost_system + ../build//boost_task + static + multi + SOLARIS:socket + ; + +exe bind_to_processors : bind_to_processors.cpp ; +exe interrupt : interrupt.cpp ; +exe priority : priority.cpp ; +exe shutdown_now : shutdown_now.cpp ; +exe smart : smart.cpp ; +exe submit : submit.cpp ; +exe sub_tasks : sub_tasks.cpp ; +exe sync/fork_join_event : sync/fork_join_event.cpp ; +exe sync/message_passing : sync/message_passing.cpp ; +exe sync/ping_pong : sync/ping_pong.cpp ; diff --git a/libs/task/examples/bind_to_processors.cpp b/libs/task/examples/bind_to_processors.cpp new file mode 100644 index 00000000..e85aac08 --- /dev/null +++ b/libs/task/examples/bind_to_processors.cpp @@ -0,0 +1,116 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include +#include + +#include "boost/task.hpp" + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +typedef tsk::static_pool< tsk::unbounded_fifo > pool_type; + +int serial_fib( int n) +{ + if( n < 2) + return n; + else + return serial_fib( n - 1) + serial_fib( n - 2); +} + +int parallel_fib( int n, int cutof) +{ + if ( n < cutof) + { + return serial_fib( n); + } + else + { + BOOST_ASSERT( boost::this_task::runs_in_pool() ); + tsk::handle< int > h1( + tsk::async( + tsk::make_task( + parallel_fib, + n - 1, + cutof), + tsk::as_sub_task() ) ); + tsk::handle< int > h2( + tsk::async( + tsk::make_task( + parallel_fib, + n - 2, + cutof), + tsk::as_sub_task() ) ); + return h1.get() + h2.get(); + } +} + +int main( int argc, char *argv[]) +{ +# if defined(BOOST_HAS_PROCESSOR_BINDINGS) + try + { + pool_type pool( pool_type::bind_to_processors() ); + + std::vector< tsk::handle< int > > results; + results.reserve( 10); + + pt::ptime start = pt::microsec_clock::universal_time(); + +// for ( int i = 0; i < 15; ++i) +// results.push_back( +// tsk::async( +// tsk::make_task( +// parallel_fib, +// 10, +// 5), +// pool) ); +// + + results.push_back( + tsk::async( + tsk::make_task( + parallel_fib, + 25, + 5), + pool) ); + + tsk::waitfor_all( results.begin(), results.end() ); + + pt::ptime stop = pt::microsec_clock::universal_time(); + + pt::time_duration elapsed = stop - start; + std::cout << "total microseconds == " << elapsed.total_microseconds() << std::endl; + std::cout << "total milliseconds == " << elapsed.total_milliseconds() << std::endl; + + int k = 0; + std::vector< tsk::handle< int > >::iterator e( results.end() ); + for ( + std::vector< tsk::handle< int > >::iterator i( results.begin() ); + i != e; + ++i) + std::cout << "fibonacci(" << k++ << ") == " << i->get() << std::endl; + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch ( ... ) + { std::cerr << "unhandled" << std::endl; } + +# endif + + return EXIT_FAILURE; +} + + diff --git a/libs/task/examples/interrupt.cpp b/libs/task/examples/interrupt.cpp new file mode 100644 index 00000000..36590dcf --- /dev/null +++ b/libs/task/examples/interrupt.cpp @@ -0,0 +1,70 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include + +#include "boost/task.hpp" + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +typedef tsk::static_pool< tsk::unbounded_fifo > pool_type; + +inline +int fibonacci_fn( int n) +{ + if ( n == 0) return 0; + if ( n == 1) return 1; + int k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + boost::this_thread::interruption_point(); + int tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + boost::this_thread::interruption_point(); + return k1; +} + +inline +void long_running_fn() +{ boost::this_thread::sleep( pt::seconds( 1) ); } + +int main( int argc, char *argv[]) +{ + try + { + pool_type pool( tsk::poolsize( 5) ); + + tsk::async( + tsk::make_task( long_running_fn), + pool); + std::cout << "poolsize == " << pool.size() << std::endl; + tsk::handle< int > h( + tsk::async( + tsk::make_task( fibonacci_fn, 10), + pool) ); + h.interrupt(); + std::cout << h.get() << std::endl; + + return EXIT_SUCCESS; + } + catch ( tsk::task_interrupted const& ) + { std::cerr << "task_interrupted: task was interrupted" << std::endl; } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch ( ... ) + { std::cerr << "unhandled" << std::endl; } + + return EXIT_FAILURE; +} diff --git a/libs/task/examples/priority.cpp b/libs/task/examples/priority.cpp new file mode 100644 index 00000000..d83f4d06 --- /dev/null +++ b/libs/task/examples/priority.cpp @@ -0,0 +1,64 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include + +#include "boost/task.hpp" + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +inline +void print_fn( std::string const& msg) +{ printf("%s", msg.c_str() ); } + +inline +void long_running_fn() +{ boost::this_thread::sleep( pt::milliseconds( 250) ); } + +int main( int argc, char *argv[]) +{ + try + { + tsk::static_pool< tsk::unbounded_prio_queue< int > > pool( tsk::poolsize( 1) ); + + tsk::task< void > t1( long_running_fn); + tsk::task< void > t2( print_fn, "a text.\n"); + tsk::task< void > t3( print_fn, " is "); + tsk::task< void > t4( print_fn, "This"); + + tsk::async( + boost::move( t1), + 3, + pool); + tsk::async( + boost::move( t2), + 0, + pool); + tsk::async( + boost::move( t3), + 1, + pool); + tsk::async( + boost::move( t4), + 2, + pool); + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch ( ... ) + { std::cerr << "unhandled" << std::endl; } + + return EXIT_FAILURE; +} diff --git a/libs/task/examples/shutdown_now.cpp b/libs/task/examples/shutdown_now.cpp new file mode 100644 index 00000000..78429d27 --- /dev/null +++ b/libs/task/examples/shutdown_now.cpp @@ -0,0 +1,68 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include +#include + +#include "boost/task.hpp" + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +inline +int fibonacci_fn( int n) +{ + boost::this_thread::sleep( pt::milliseconds( 500) ); + if ( n == 0) return 0; + if ( n == 1) return 1; + int k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + boost::this_thread::interruption_point(); + int tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + boost::this_thread::interruption_point(); + return k1; +} + +int main( int argc, char *argv[]) +{ + try + { + tsk::static_pool< tsk::unbounded_fifo > pool( tsk::poolsize( 1) ); + + tsk::task< int > t( fibonacci_fn, 10); + + tsk::handle< int > h( + tsk::async( + boost::move( t), + pool) ); + + boost::this_thread::sleep( pt::milliseconds( 250) ); + + pool.shutdown_now(); + + std::cout << h.get() << std::endl; + + return EXIT_SUCCESS; + } + catch ( tsk::task_interrupted const& ) + { std::cerr << "task_interrupted: task was interrupted" << std::endl; } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch ( ... ) + { std::cerr << "unhandled" << std::endl; } + + return EXIT_FAILURE; +} diff --git a/libs/task/examples/smart.cpp b/libs/task/examples/smart.cpp new file mode 100644 index 00000000..3cd777e2 --- /dev/null +++ b/libs/task/examples/smart.cpp @@ -0,0 +1,88 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include + +#include "boost/task.hpp" + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +inline +void fibonacci_fn( int n) +{ + if ( n == 0) + { + printf("fibonacci(%d) == 0\n", n); + return; + } + if ( n == 1) + { + printf("fibonacci(%d) == 1\n", n); + return; + } + int k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + int tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + printf("fibonacci(%d) == %d\n", n, k1); +} + +inline +void long_running_fn() +{ boost::this_thread::sleep( pt::milliseconds( 500) ); } + +int main( int argc, char *argv[]) +{ + try + { + tsk::static_pool< + tsk::unbounded_smart_queue< + int, + std::less< int > + > + > pool( tsk::poolsize( 1) ); + + tsk::task< void > t1( long_running_fn); + tsk::task< void > t2( fibonacci_fn, 0); + tsk::task< void > t3( fibonacci_fn, 1); + tsk::task< void > t4( fibonacci_fn, 10); + + tsk::async( + boost::move( t1), + 0, + pool); + tsk::async( + boost::move( t2), + 1, + pool); + tsk::async( + boost::move( t3), + 2, + pool); + tsk::async( + boost::move( t4), + 2, + pool); + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch ( ... ) + { std::cerr << "unhandled" << std::endl; } + + return EXIT_FAILURE; +} diff --git a/libs/task/examples/sub_tasks.cpp b/libs/task/examples/sub_tasks.cpp new file mode 100755 index 00000000..c6bc9d88 --- /dev/null +++ b/libs/task/examples/sub_tasks.cpp @@ -0,0 +1,88 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include +#include + +#include "boost/task.hpp" + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +typedef tsk::static_pool< tsk::unbounded_fifo > pool_type; + +int serial_fib( int n) +{ + if( n < 2) + return n; + else + return serial_fib( n - 1) + serial_fib( n - 2); +} + +int parallel_fib( int n, int cutof) +{ + if ( n < cutof) + { + return serial_fib( n); + } + else + { + BOOST_ASSERT( boost::this_task::runs_in_pool() ); + + tsk::handle< int > h1( + tsk::async( + tsk::make_task( + parallel_fib, + n - 1, + cutof), + tsk::as_sub_task() ) ); + + tsk::handle< int > h2( + tsk::async( + tsk::make_task( + parallel_fib, + n - 2, + cutof), + tsk::as_sub_task() ) ); + + return h1.get() + h2.get(); + } +} + +int main( int argc, char *argv[]) +{ + try + { + pool_type pool( tsk::poolsize( 5) ); + + int n = 10; + tsk::handle< int > h( + tsk::async( + tsk::make_task( + parallel_fib, + n, + 5), + pool) ); + + std::cout << "fibonacci(" << n << ") == " << h.get() << std::endl; + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch ( ... ) + { std::cerr << "unhandled" << std::endl; } + + return EXIT_FAILURE; +} + + diff --git a/libs/task/examples/submit.cpp b/libs/task/examples/submit.cpp new file mode 100644 index 00000000..989b924a --- /dev/null +++ b/libs/task/examples/submit.cpp @@ -0,0 +1,75 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include + +#include "boost/task.hpp" + +namespace tsk = boost::tasks; + +inline +int fibonacci_fn( int n) +{ + if ( n == 0) return 0; + if ( n == 1) return 1; + int k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + boost::this_thread::interruption_point(); + int tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + boost::this_thread::interruption_point(); + return k1; +} + +int main( int argc, char *argv[]) +{ + try + { + tsk::static_pool< tsk::unbounded_prio_queue< int > > pool( tsk::poolsize( 3) ); + + tsk::task< int > t1( fibonacci_fn, 10); + tsk::task< int > t2( fibonacci_fn, 10); + tsk::task< int > t3( fibonacci_fn, 10); + tsk::task< int > t4( fibonacci_fn, 10); + + tsk::handle< int > h1( + tsk::async( boost::move( t1) ) ); + tsk::handle< int > h2( + tsk::async( + boost::move( t2), + tsk::new_thread() ) ); + tsk::handle< int > h3( + tsk::async( + boost::move( t3), + 2, + pool) ); + tsk::handle< int > h4( + tsk::async( + boost::move( t4), + 2, + pool) ); + + std::cout << h1.get() << std::endl; + std::cout << h2.get() << std::endl; + std::cout << h3.get() << std::endl; + std::cout << h4.get() << std::endl; + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch ( ... ) + { std::cerr << "unhandled" << std::endl; } + + return EXIT_FAILURE; +} diff --git a/libs/task/examples/sync/fork_join_event.cpp b/libs/task/examples/sync/fork_join_event.cpp new file mode 100755 index 00000000..29ac937b --- /dev/null +++ b/libs/task/examples/sync/fork_join_event.cpp @@ -0,0 +1,87 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "boost/task.hpp" + +namespace tsk = boost::tasks; + +typedef tsk::static_pool< tsk::unbounded_fifo > pool_type; + +void sub_task( int i, int n, tsk::spin::count_down_event & ev) +{ + BOOST_ASSERT( boost::this_task::runs_in_pool() ); + + fprintf( stderr, "t%d running ...\n", i); + + ev.set(); + + fprintf( stderr, "t%d finished ...\n", i); +} + +void main_task( + pool_type & pool, + int n, + tsk::spin::count_down_event & outer_ev) +{ + BOOST_ASSERT( boost::this_task::runs_in_pool() ); + + fprintf( stderr, "main-task running %d sub-tasks\n", n); + + tsk::spin::count_down_event inner_ev( n); + + for ( int i = 0; i < n; ++i) + tsk::async( + tsk::make_task( + & sub_task, + i, + n, + boost::ref( inner_ev) ), + tsk::as_sub_task() ); + + inner_ev.wait(); + outer_ev.set(); +} + +int main( int argc, char *argv[]) +{ + try + { + tsk::poolsize psize( boost::thread::hardware_concurrency() ); + pool_type pool( psize); + + int n = 32; + tsk::spin::count_down_event ev( 1); + tsk::async( + tsk::make_task( + & main_task, + boost::ref( pool), + n, + boost::ref( ev) ), + pool); + + fprintf( stderr, "main thread: waiting for t0 to finish\n"); + ev.wait(); + fprintf( stderr, "main thread: t0 finished\n"); + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch ( ... ) + { std::cerr << "unhandled" << std::endl; } + + return EXIT_FAILURE; +} diff --git a/libs/task/examples/sync/message_passing.cpp b/libs/task/examples/sync/message_passing.cpp new file mode 100755 index 00000000..ca03596f --- /dev/null +++ b/libs/task/examples/sync/message_passing.cpp @@ -0,0 +1,113 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "boost/task.hpp" + +namespace tsk = boost::tasks; + +typedef tsk::static_pool< tsk::unbounded_fifo > pool_type; + +int serial_fib( int n) +{ + if( n < 2) + return n; + else + return serial_fib( n - 1) + serial_fib( n - 2); +} + +int parallel_fib( int n, int cutof) +{ + if ( n < cutof) return serial_fib( n); + else + { + BOOST_ASSERT( boost::this_task::runs_in_pool() ); + tsk::task< int > t1( + parallel_fib, + n - 1, + cutof); + tsk::task< int > t2( + parallel_fib, + n - 2, + cutof); + tsk::handle< int > h1( + tsk::async( + boost::move( t1), + tsk::as_sub_task() ) ) ; + tsk::handle< int > h2( + tsk::async( + boost::move( t2), + tsk::as_sub_task() ) ); + return h1.get() + h2.get(); + } +} + +inline +void submit( + tsk::spin::unbounded_channel< int > & send, + tsk::spin::unbounded_channel< std::pair< int , int > > & recv, + int n) +{ + for ( int i = 0; i <= n; ++i) + send.put( i); + send.deactivate(); + boost::optional< std::pair< int , int > > r; + while ( recv.take( r) ) + { + BOOST_ASSERT( r); + printf("fib(%d) == %d\n", r->first, r->second); + } +} + +inline +void calculate( + tsk::spin::unbounded_channel< int > & recv, + tsk::spin::unbounded_channel< std::pair< int , int > > & send) +{ + boost::optional< int > n; + while ( recv.take( n) ) + { + BOOST_ASSERT( n); + int r = parallel_fib( * n, 5); + send.put( std::make_pair( * n, r) ); + } + send.deactivate(); +} + +int main( int argc, char *argv[]) +{ + try + { + pool_type pool( tsk::poolsize( 2) ); + + tsk::spin::unbounded_channel< int > buf1; + tsk::spin::unbounded_channel< std::pair< int , int > > buf2; + + tsk::async( + tsk::make_task( submit, buf1, buf2, 15), + pool); + tsk::async( + tsk::make_task( calculate, buf1, buf2), + pool); + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch ( ... ) + { std::cerr << "unhandled" << std::endl; } + + return EXIT_FAILURE; +} diff --git a/libs/task/examples/sync/ping_pong.cpp b/libs/task/examples/sync/ping_pong.cpp new file mode 100755 index 00000000..998730d5 --- /dev/null +++ b/libs/task/examples/sync/ping_pong.cpp @@ -0,0 +1,125 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +typedef boost::tasks::static_pool< boost::tasks::unbounded_fifo > pool_t; +typedef boost::tasks::spin::unbounded_channel< std::string > fifo_t; + +inline +void ping( + fifo_t & recv_buf, + fifo_t & send_buf, + boost::tasks::spin::barrier & b) +{ + boost::this_thread::sleep( boost::posix_time::seconds( 1) ); + b.wait(); + + std::stringstream tss; + tss << boost::this_thread::get_id(); + std::stringstream fss; + fss << boost::this_tasklet::get_id(); + + fprintf( stderr, "start tasklet task %s in thread %s\n", fss.str().c_str(), tss.str().c_str() ); + + boost::optional< std::string > value; + + send_buf.put("ping"); + BOOST_ASSERT( recv_buf.take( value) ); + fprintf( stderr, "ping tasklet %s in thread %s recevied %s\n", fss.str().c_str(), tss.str().c_str(), value->c_str()); + value.reset(); + + send_buf.put("ping"); + BOOST_ASSERT( recv_buf.take( value) ); + fprintf( stderr, "ping tasklet %s in thread %s recevied %s\n", fss.str().c_str(), tss.str().c_str(), value->c_str()); + value.reset(); + + send_buf.put("ping"); + BOOST_ASSERT( recv_buf.take( value) ); + fprintf( stderr, "ping tasklet %s in thread %s recevied %s\n", fss.str().c_str(), tss.str().c_str(), value->c_str()); + value.reset(); + + send_buf.deactivate(); + + fprintf( stderr, "end ping tasklet %s in thread %s\n", fss.str().c_str(), tss.str().c_str() ); +} + +inline +void pong( + fifo_t & recv_buf, + fifo_t & send_buf, + boost::tasks::spin::barrier & b) +{ + b.wait(); + std::stringstream tss; + tss << boost::this_thread::get_id(); + std::stringstream fss; + fss << boost::this_tasklet::get_id(); + + fprintf( stderr, "start pong tasklet %s in thread %s\n", fss.str().c_str(), tss.str().c_str() ); + + boost::optional< std::string > value; + + BOOST_ASSERT( recv_buf.take( value) ); + fprintf( stderr, "pong tasklet %s in thread %s recevied %s\n", fss.str().c_str(), tss.str().c_str(), value->c_str()); + value.reset(); + send_buf.put("pong"); + + BOOST_ASSERT( recv_buf.take( value) ); + fprintf( stderr, "pong tasklet %s in thread %s recevied %s\n", fss.str().c_str(), tss.str().c_str(), value->c_str()); + value.reset(); + send_buf.put("pong"); + + BOOST_ASSERT( recv_buf.take( value) ); + fprintf( stderr, "pong tasklet %s in thread %s recevied %s\n", fss.str().c_str(), tss.str().c_str(), value->c_str()); + value.reset(); + send_buf.put("pong"); + + send_buf.deactivate(); + + fprintf( stderr, "end pong tasklet %s in thread %s\n", fss.str().c_str(), tss.str().c_str() ); +} + +int main() +{ + try + { + fifo_t buf1; + fifo_t buf2; + boost::tasks::spin::barrier b( 2); + + std::cout << "start" << std::endl; + + pool_t pool( boost::tasks::poolsize( 2) ); + + boost::tasks::async( + boost::tasks::make_task( ping, buf1, buf2, boost::ref( b) ), + pool); + + boost::tasks::async( + boost::tasks::make_task( pong, buf2, buf1, boost::ref( b) ), + pool); + + pool.shutdown(); + + std::cout << "finish" << std::endl; + + return EXIT_SUCCESS; + } + catch ( boost::system::system_error const& e) + { std::cerr << "system_error: " << e.code().value() << std::endl; } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/task/src/callable.cpp b/libs/task/src/callable.cpp new file mode 100755 index 00000000..76737660 --- /dev/null +++ b/libs/task/src/callable.cpp @@ -0,0 +1,36 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/callable.hpp" + +namespace boost { +namespace tasks { + +callable::callable() : + base_() +{} + +void +callable::operator()() +{ base_->run(); } + +bool +callable::empty() const +{ return ! base_; } + +void +callable::clear() +{ base_.reset(); } + +void +callable::reset( shared_ptr< thread > const& thrd) +{ base_->reset( thrd); } + +void +callable::swap( callable & other) +{ base_.swap( other.base_); } + +}} diff --git a/libs/task/src/context.cpp b/libs/task/src/context.cpp new file mode 100755 index 00000000..f1583123 --- /dev/null +++ b/libs/task/src/context.cpp @@ -0,0 +1,84 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/context.hpp" + +#include + +namespace boost { +namespace tasks { +namespace detail { + +void +context_base::reset_( shared_ptr< thread > const& thrd) +{ + thrd_ = thrd; + BOOST_ASSERT( thrd_); + if ( requested_) + if ( thrd_) thrd_->interrupt(); +} + +void +context_base::interrupt_() +{ + if ( ! requested_) + { + requested_ = true; + if ( thrd_) thrd_->interrupt(); + } +} + +context_base::context_base() : + use_count_( 0), + requested_( false), + mtx_(), + thrd_() +{} + +void +context_base::reset( shared_ptr< thread > const& thrd) +{ + lock_guard< mutex > lk( mtx_); + reset_( thrd); +} + +void +context_base::interrupt() +{ + lock_guard< mutex > lk( mtx_); + interrupt_(); +} + +bool +context_base::interruption_requested() +{ + lock_guard< mutex > lk( mtx_); + return requested_; +} + +} + +context::context() : + base_( new detail::context_base() ) +{} + +void +context::reset( shared_ptr< thread > const& thrd) +{ base_->reset( thrd); } + +void +context::interrupt() +{ base_->interrupt(); } + +bool +context::interruption_requested() +{ return base_->interruption_requested(); } + +void +context::swap( context & other) +{ base_.swap( other.base_); } + +}} diff --git a/libs/task/src/detail/worker.cpp b/libs/task/src/detail/worker.cpp new file mode 100644 index 00000000..003a3e29 --- /dev/null +++ b/libs/task/src/detail/worker.cpp @@ -0,0 +1,51 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/detail/worker.hpp" + +namespace boost { +namespace tasks { +namespace detail { + +thread_specific_ptr< worker > worker::tss_; + +const thread::id +worker::get_id() const +{ return impl_->get_id(); } + +void +worker::join() const +{ impl_->join(); } + +void +worker::interrupt() const +{ impl_->interrupt(); } + +void +worker::put( callable const& ca) +{ impl_->put( ca); } + +bool +worker::try_steal( callable & ca) +{ return impl_->try_steal( ca); } + +void +worker::run() +{ + // FIXME: ugly + worker::tss_.reset( new worker( * this) ); + impl_->run(); +} + +void +worker::yield() +{ impl_->yield(); } + +worker * +worker::tss_get() +{ return worker::tss_.get(); } + +}}} diff --git a/libs/task/src/detail/worker_group.cpp b/libs/task/src/detail/worker_group.cpp new file mode 100644 index 00000000..f8d398d3 --- /dev/null +++ b/libs/task/src/detail/worker_group.cpp @@ -0,0 +1,88 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/detail/worker_group.hpp" + +#include +#include + +namespace boost { +namespace tasks { +namespace detail { + +worker_group::worker_group() : + cont_(), + id_idx_( cont_.get< id_idx_tag >() ), + rnd_idx_( cont_.get< rnd_idx_tag >() ) +{} + +worker_group::~worker_group() +{ + if ( ! empty() ) + join_all(); +} + +const worker +worker_group::operator[]( std::size_t pos) const +{ return rnd_idx_[pos]; } + +std::size_t +worker_group::size() const +{ return cont_.size(); } + +bool +worker_group::empty() const +{ return cont_.empty(); } + +const worker_group::iterator +worker_group::begin() +{ return id_idx_.begin(); } + +const worker_group::const_iterator +worker_group::begin() const +{ return id_idx_.begin(); } + +const worker_group::iterator +worker_group::end() +{ return id_idx_.end(); } + +const worker_group::const_iterator +worker_group::end() const +{ return id_idx_.end(); } + +const worker_group::const_iterator +worker_group::find( thread::id const& id) const +{ return id_idx_.find( id); } + +void +worker_group::insert( worker const& w) +{ cont_.insert( w); } + +worker_group::iterator +worker_group::erase( iterator const& i) +{ return id_idx_.erase( i); } + +void +worker_group::join_all() +{ + BOOST_FOREACH( worker w, cont_) + { + try + { w.join(); } + catch (...) + {} + } + cont_.clear(); +} + +void +worker_group::interrupt_all() +{ + BOOST_FOREACH( worker w, cont_) + { w.interrupt(); } +} + +}}} diff --git a/libs/task/src/detail/wsq.cpp b/libs/task/src/detail/wsq.cpp new file mode 100644 index 00000000..72a3784d --- /dev/null +++ b/libs/task/src/detail/wsq.cpp @@ -0,0 +1,120 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/detail/wsq.hpp" + +#include + +namespace boost { +namespace tasks { +namespace detail { + +wsq::wsq( fast_semaphore & fsem) : + initial_size_( 32), + array_( new callable[ initial_size_]), + capacity_( initial_size_), + mask_( initial_size_ - 1), + head_idx_( 0), + tail_idx_( 0), + mtx_(), + fsem_( fsem) +{} + +bool +wsq::empty() const +{ return head_idx_.load() >= tail_idx_.load(); } + +std::size_t +wsq::size() const +{ return tail_idx_.load() - head_idx_.load(); } + +void +wsq::put( callable const& ca) +{ + unsigned int tail( tail_idx_.load() ); + if ( tail <= head_idx_.load() + mask_) + { + array_[tail & mask_] = ca; + tail_idx_.fetch_add( 1); + } + else + { + lock_guard< recursive_mutex > lk( mtx_); + unsigned int head( head_idx_.load() ); + int count( size() ); + + if ( count >= mask_) + { + capacity_ <<= 1; + shared_array< callable > array( new callable[capacity_]); + for ( int i( 0); i != count; ++i) + array[i] = array_[(i + head) & mask_]; + array_.swap( array); + head_idx_.store( 0); + tail = count; + tail_idx_.store( tail); + mask_ = (mask_ << 1) | 1; + } + array_[tail & mask_] = ca; + tail_idx_.fetch_add( 1); + } + fsem_.post(); +} + +bool +wsq::try_take( callable & ca) +{ + unsigned int tail( tail_idx_.load() ); + if ( tail == 0) + return false; + tail -= 1; + tail_idx_.exchange( tail); + //tail_idx_.store( tail); + if ( head_idx_.load() <= tail) + { + ca.swap( array_[tail & mask_]); + return true; + } + else + { + lock_guard< recursive_mutex > lk( mtx_); + if ( head_idx_.load() <= tail) + { + ca.swap( array_[tail & mask_]); + return true; + } + else + { + tail_idx_.fetch_add( 1); + return false; + } + } +} + +bool +wsq::try_steal( callable & ca) +{ + recursive_mutex::scoped_try_lock lk( mtx_); + if ( lk.owns_lock() ) + { + unsigned int head( head_idx_.load() ); + head_idx_.exchange( head + 1); + //head_idx_.store( head + 1); + if ( head < tail_idx_.load() ) + { + ca.swap( array_[head & mask_]); + return true; + } + else + { + head_idx_.store( head); + return false; + } + } + return false; +} + +}}} diff --git a/libs/task/src/fast_semaphore.cpp b/libs/task/src/fast_semaphore.cpp new file mode 100755 index 00000000..d6a534f1 --- /dev/null +++ b/libs/task/src/fast_semaphore.cpp @@ -0,0 +1,74 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/fast_semaphore.hpp" + +#include +#include + +namespace boost { +namespace tasks { + +fast_semaphore::fast_semaphore( int sem_count, unsigned int spin_count) : + spin_count_( spin_count), + sem_count_( sem_count), + sem_active_( true), + sem_( sem_count) +{ + if ( 0 > sem_count_) + throw std::invalid_argument("count must not be negative"); +} + +fast_semaphore::~fast_semaphore() +{} + +void +fast_semaphore::post( int count) +{ + if ( 0 > count) + throw std::invalid_argument("count must not be negative"); + + int sem_count = sem_count_.fetch_add( count); + + if ( sem_count < 0) + sem_.post( ( std::min)( count, -sem_count) ); +} + +void +fast_semaphore::wait() +{ + unsigned int spin_count( spin_count_); + while ( 0 < spin_count--) + if ( try_wait() ) return; + + if ( 0 <= sem_count_.fetch_sub( 1) ) return; + + if ( ! sem_active_.load() ) return; + + sem_.wait(); +} + +bool +fast_semaphore::try_wait() +{ + int sem_count; + + do + { + sem_count = sem_count_.load(); + if ( ! sem_active_.load() ) return false; + } while ( sem_count > 0 && ! sem_count_.compare_exchange_strong( sem_count, sem_count - 1) ); + + return sem_count > 0; +} + +void +fast_semaphore::deactivate() { + sem_active_.store( false); + post( std::abs( sem_count_.load() ) ); +} + +}} diff --git a/libs/task/src/poolsize.cpp b/libs/task/src/poolsize.cpp new file mode 100755 index 00000000..7b19a561 --- /dev/null +++ b/libs/task/src/poolsize.cpp @@ -0,0 +1,22 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/poolsize.hpp" + +#include + +namespace boost { +namespace tasks { + +poolsize::poolsize( std::size_t value) : + value_( value) +{ if ( value <= 0) throw invalid_poolsize(); } + +poolsize::operator std::size_t () const +{ return value_; } + +}} + diff --git a/libs/task/src/semaphore_posix.cpp b/libs/task/src/semaphore_posix.cpp new file mode 100755 index 00000000..55bbca3a --- /dev/null +++ b/libs/task/src/semaphore_posix.cpp @@ -0,0 +1,100 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/semaphore.hpp" + +extern "C" { + +#include + +} + +#include +#include + +#include +#include +#include +#include + +namespace { + +#if ! defined(__FreeBSD__) +union semun { + int val; + semid_ds * buf; + ushort * array; +}; +#endif + +} + +namespace boost { +namespace tasks { + +semaphore::semaphore( int sem_count) : + handle_( -1) +{ + if ( 0 > sem_count) + throw std::invalid_argument("count must not be negative"); + + semun ctl; + ctl.val = sem_count; + BOOST_ASSERT( ctl.val == sem_count); + + if ( ( handle_ = ::semget( IPC_PRIVATE, 1, S_IRUSR | S_IWUSR) ) == -1) + throw system::system_error( errno, system::system_category() ); + + if ( ::semctl( handle_, 0, SETVAL, ctl) == -1) + throw system::system_error( errno, system::system_category() ); +} + +semaphore::~semaphore() +{ ::semctl( handle_, 0, IPC_RMID); } + +void +semaphore::post( int n) +{ + if ( 0 >= n) + throw std::invalid_argument("invalid post-argument"); + + sembuf op; + + op.sem_num = 0; + op.sem_op = n; + op.sem_flg = 0; + BOOST_ASSERT( op.sem_op == n); + + if ( ::semop( handle_, & op, 1) == -1) + throw system::system_error( errno, system::system_category() ); +} + +void +semaphore::wait() +{ + sembuf op; + + op.sem_num = 0; + op.sem_op = -1; + op.sem_flg = 0; + + if ( ::semop( handle_, & op, 1) == -1) + throw system::system_error( errno, system::system_category() ); +} + +bool +semaphore::try_wait() +{ + sembuf op; + + op.sem_num = 0; + op.sem_op = -1; + op.sem_flg = IPC_NOWAIT; // Don't wait if we can't lock it now + + return ::semop( handle_, & op, 1) == 0; +} + +}} diff --git a/libs/task/src/semaphore_windows.cpp b/libs/task/src/semaphore_windows.cpp new file mode 100755 index 00000000..3115850c --- /dev/null +++ b/libs/task/src/semaphore_windows.cpp @@ -0,0 +1,57 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/semaphore.hpp" + +#include +#include + +#include +#include + +namespace boost { +namespace tasks { + +semaphore::semaphore( int value) : + handle_() +{ + if ( ( handle_ = ::CreateSemaphore( 0, value, ( std::numeric_limits< int >::max)(), 0) ) == 0) + throw system::system_error( ::GetLastError(), system::system_category() ); +} + +semaphore::~semaphore() +{ ::CloseHandle( handle_); } + +void +semaphore::post( int n) +{ + if ( ! ::ReleaseSemaphore( handle_, n, 0) ) + throw system::system_error( ::GetLastError(), system::system_category() ); +} + +void +semaphore::wait() +{ + if ( ::WaitForSingleObject( handle_, INFINITE) != WAIT_OBJECT_0) + throw system::system_error( ::GetLastError(), system::system_category() ); +} + +bool +semaphore::try_wait() +{ + switch ( ::WaitForSingleObject( handle_, 0) ) + { + case WAIT_OBJECT_0: + return true; + case WAIT_TIMEOUT: + return false; + default: + throw system::system_error( ::GetLastError(), system::system_category() ); + } + return true; +} + +}} diff --git a/libs/task/src/spin/auto_reset_event.cpp b/libs/task/src/spin/auto_reset_event.cpp new file mode 100644 index 00000000..9c7c82a9 --- /dev/null +++ b/libs/task/src/spin/auto_reset_event.cpp @@ -0,0 +1,71 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/spin/auto_reset_event.hpp" + +#include + +#include + +namespace boost { +namespace tasks { +namespace spin { + +auto_reset_event::auto_reset_event( bool isset) : + state_( isset ? SET : RESET) +{} + +void +auto_reset_event::set() +{ state_.store( SET); } + +void +auto_reset_event::wait() +{ + state expected = SET; + while ( ! state_.compare_exchange_strong( expected, RESET) ) + { + this_thread::interruption_point(); + if ( this_task::runs_in_pool() ) + this_task::yield(); + else + this_thread::yield(); + this_thread::interruption_point(); + + expected = SET; + } +} + +bool +auto_reset_event::try_wait() +{ + state expected = SET; + return state_.compare_exchange_strong( expected, RESET); +} + +bool +auto_reset_event::timed_wait( system_time const& abs_time) +{ + if ( get_system_time() >= abs_time) return false; + + state expected = SET; + while ( ! state_.compare_exchange_strong( expected, RESET) ) + { + this_thread::interruption_point(); + if ( this_task::runs_in_pool() ) + this_task::yield(); + else + this_thread::yield(); + this_thread::interruption_point(); + + if ( get_system_time() >= abs_time) return false; + expected = SET; + } + + return true; +} + +}}} diff --git a/libs/task/src/spin/barrier.cpp b/libs/task/src/spin/barrier.cpp new file mode 100644 index 00000000..f2beb7a4 --- /dev/null +++ b/libs/task/src/spin/barrier.cpp @@ -0,0 +1,43 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/spin/barrier.hpp" + +#include + +namespace boost { +namespace tasks { +namespace spin { + +barrier::barrier( std::size_t initial) : + initial_( initial), + current_( initial_), + cycle_( true), + mtx_(), + cond_() +{ if ( initial == 0) throw std::invalid_argument("invalid barrier count"); } + +bool +barrier::wait() +{ + mutex::scoped_lock lk( mtx_); + bool cycle( cycle_); + if ( 0 == --current_) + { + cycle_ = ! cycle_; + current_ = initial_; + cond_.notify_all(); + return true; + } + else + { + while ( cycle == cycle_) + cond_.wait( lk); + } + return false; +} + +}}} diff --git a/libs/task/src/spin/condition.cpp b/libs/task/src/spin/condition.cpp new file mode 100644 index 00000000..32cdc66c --- /dev/null +++ b/libs/task/src/spin/condition.cpp @@ -0,0 +1,52 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/spin/condition.hpp" + +#include + +namespace boost { +namespace tasks { +namespace spin { + +void +condition::notify_( command cmd) +{ + enter_mtx_.lock(); + + if ( 0 == waiters_.load() ) + { + enter_mtx_.unlock(); + return; + } + + command expected = SLEEPING; + while ( ! cmd_.compare_exchange_strong( expected, cmd) ) + { + if ( this_tasklet::runs_as_tasklet() ) + this_tasklet::yield(); + else + this_thread::yield(); + expected = SLEEPING; + } +} + +condition::condition() : + cmd_( SLEEPING), + waiters_( 0), + enter_mtx_(), + check_mtx_() +{} + +void +condition::notify_one() +{ notify_( NOTIFY_ONE); } + +void +condition::notify_all() +{ notify_( NOTIFY_ALL); } + +}}} diff --git a/libs/task/src/spin/count_down_event.cpp b/libs/task/src/spin/count_down_event.cpp new file mode 100644 index 00000000..a4c425fe --- /dev/null +++ b/libs/task/src/spin/count_down_event.cpp @@ -0,0 +1,82 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/spin/count_down_event.hpp" + +#include + +#include +#include + +namespace boost { +namespace tasks { +namespace spin { + +count_down_event::count_down_event( std::size_t initial) : + initial_( initial), + current_( initial_) +{} + +std::size_t +count_down_event::initial() const +{ return initial_; } + +std::size_t +count_down_event::current() const +{ return current_.load(); } + +bool +count_down_event::is_set() const +{ return 0 == current_.load(); } + +void +count_down_event::set() +{ + for (;;) + { + if ( 0 == current_.load() ) + return; + std::size_t expected = current_.load(); + if ( current_.compare_exchange_strong( expected, expected - 1) ) + return; + } +} + +void +count_down_event::wait() +{ + while ( 0 != current_.load() ) + { + this_thread::interruption_point(); + if ( this_task::runs_in_pool() ) + this_task::yield(); + else + this_thread::yield(); + this_thread::interruption_point(); + } +} + +bool +count_down_event::timed_wait( system_time const& abs_time) +{ + if ( get_system_time() >= abs_time) return false; + + while ( 0 != current_.load() ) + { + this_thread::interruption_point(); + if ( this_task::runs_in_pool() ) + this_task::yield(); + else + this_thread::yield(); + this_thread::interruption_point(); + + if ( get_system_time() >= abs_time) return false; + } + + return true; +} + +}}} diff --git a/libs/task/src/spin/manual_reset_event.cpp b/libs/task/src/spin/manual_reset_event.cpp new file mode 100644 index 00000000..b1880def --- /dev/null +++ b/libs/task/src/spin/manual_reset_event.cpp @@ -0,0 +1,104 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/spin/manual_reset_event.hpp" + +#include +#include + +#include + +namespace boost { +namespace tasks { +namespace spin { + +manual_reset_event::manual_reset_event( bool isset) : + state_( isset ? SET : RESET), + waiters_( 0), + enter_mtx_() +{} + +void +manual_reset_event::set() +{ + enter_mtx_.lock(); + + state expected = RESET; + if ( ! state_.compare_exchange_strong( expected, SET) || + 0 == waiters_.load() ) + enter_mtx_.unlock(); +} + +void +manual_reset_event::reset() +{ + mutex::scoped_lock lk( enter_mtx_); + BOOST_ASSERT( lk); + + state_.store( RESET); +} + +void +manual_reset_event::wait() +{ + { + mutex::scoped_lock lk( enter_mtx_); + BOOST_ASSERT( lk); + waiters_.fetch_add( 1); + } + + while ( RESET == state_.load() ) + { + this_thread::interruption_point(); + if ( this_task::runs_in_pool() ) + this_task::yield(); + else + this_thread::yield(); + this_thread::interruption_point(); + } + + if ( 1 == waiters_.fetch_sub( 1) ) + enter_mtx_.unlock(); +} + +bool +manual_reset_event::timed_wait( system_time const& abs_time) +{ + if ( get_system_time() >= abs_time) return false; + + while ( RESET == state_.load() ) + { + this_thread::interruption_point(); + if ( this_task::runs_in_pool() ) + this_task::yield(); + else + this_thread::yield(); + this_thread::interruption_point(); + + if ( get_system_time() >= abs_time) return false; + } + + return true; +} + +bool +manual_reset_event::try_wait() +{ + { + mutex::scoped_lock lk( enter_mtx_); + BOOST_ASSERT( lk); + waiters_.fetch_add( 1); + } + + bool result = SET == state_.load(); + + if ( 1 == waiters_.fetch_sub( 1) ) + enter_mtx_.unlock(); + + return result; +} + +}}} diff --git a/libs/task/src/spin/mutex.cpp b/libs/task/src/spin/mutex.cpp new file mode 100644 index 00000000..387d4fcb --- /dev/null +++ b/libs/task/src/spin/mutex.cpp @@ -0,0 +1,77 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include + +#include + +namespace boost { +namespace tasks { +namespace spin { + +mutex::mutex() : + state_( UNLOCKED) +{} + +void +mutex::lock() +{ + for (;;) + { + state expected = UNLOCKED; + if ( state_.compare_exchange_strong( expected, LOCKED) ) + break; + if ( this_tasklet::runs_as_tasklet() ) + this_tasklet::yield(); + else + this_thread::yield(); + } +} + +bool +mutex::timed_lock( system_time const& abs_time) +{ + if ( abs_time.is_infinity() ) + { + lock(); + return true; + } + + if ( get_system_time() >= abs_time) + return false; + + for (;;) + { + if ( try_lock() ) break; + + if ( get_system_time() >= abs_time) + return false; + + this_thread::interruption_point(); + if ( this_task::runs_in_pool() ) + this_task::yield(); + else + this_thread::yield(); + this_thread::interruption_point(); + } + + return true; +} + +bool +mutex::try_lock() +{ + state expected = UNLOCKED; + return state_.compare_exchange_strong( expected, LOCKED); +} + +void +mutex::unlock() +{ state_.store( UNLOCKED); } + +}}} diff --git a/libs/task/src/stacksize.cpp b/libs/task/src/stacksize.cpp new file mode 100755 index 00000000..3c6548b0 --- /dev/null +++ b/libs/task/src/stacksize.cpp @@ -0,0 +1,21 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/stacksize.hpp" + +#include + +namespace boost { +namespace tasks { + +stacksize::stacksize( std::size_t value) : + value_( value) +{ if ( value <= 0) throw invalid_stacksize(); } + +stacksize::operator std::size_t () const +{ return value_; } + +}} diff --git a/libs/task/src/watermark.cpp b/libs/task/src/watermark.cpp new file mode 100755 index 00000000..fe2fc24b --- /dev/null +++ b/libs/task/src/watermark.cpp @@ -0,0 +1,34 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/task/watermark.hpp" + +#include + +namespace boost { +namespace tasks { + +high_watermark::high_watermark( std::size_t value) : + value_( value) +{ + if ( value <= 0) + throw invalid_watermark(); +} + +high_watermark::operator std::size_t () const +{ return value_; } + +low_watermark::low_watermark( std::size_t value) +: value_( value) +{ + if ( value < 0) + throw invalid_watermark(); +} + +low_watermark::operator std::size_t () const +{ return value_; } + +}} diff --git a/libs/task/test/Jamfile.v2 b/libs/task/test/Jamfile.v2 new file mode 100644 index 00000000..1aa4d0a2 --- /dev/null +++ b/libs/task/test/Jamfile.v2 @@ -0,0 +1,46 @@ +# Boost.Task Library Tests Jamfile + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import testing ; + +project boost/task/test + : requirements + ../../test/build//boost_unit_test_framework + ../../tasklet/build//boost_tasklet + ../../thread/build//boost_thread + ../../system/build//boost_system + ../build//boost_task + static + multi + ; + +rule task-test ( source ) +{ + return + [ run $(source).cpp ] + ; +} + +test-suite task : + [ task-test test_task ] + [ task-test test_own_thread ] + [ task-test test_tasklet ] + [ task-test test_new_thread ] + [ task-test test_unbounded_pool ] + [ task-test test_bounded_pool ] + [ task-test test_as_sub_task ] + [ task-test test_spin_mutex ] + [ task-test test_spin_condition ] + [ task-test test_spin_condition_notify_all ] + [ task-test test_spin_condition_notify_one ] + [ task-test test_spin_condition_timed_wait_times_out ] + [ task-test test_spin_count_down_event ] + [ task-test test_spin_auto_reset_event ] + [ task-test test_spin_manual_reset_event ] + [ task-test test_spin_unbounded_channel ] + [ task-test test_spin_bounded_channel ] + ; diff --git a/libs/task/test/condition_test_common.hpp b/libs/task/test/condition_test_common.hpp new file mode 100644 index 00000000..10b84d0c --- /dev/null +++ b/libs/task/test/condition_test_common.hpp @@ -0,0 +1,99 @@ +#ifndef TASK_CONDITION_TEST_COMMON_HPP +#define TASK_CONDITION_TEST_COMMON_HPP +// Copyright (C) 2007 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include + +namespace tsk = boost::tasks; + +unsigned const timeout_seconds=5; + +struct wait_for_flag : private boost::noncopyable +{ + tsk::spin::mutex mutex; + tsk::spin::condition cond_var; + bool flag; + unsigned woken; + + wait_for_flag(): + flag(false),woken(0) + {} + + struct check_flag + { + bool const& flag; + + check_flag(bool const& flag_): + flag(flag_) + {} + + bool operator()() const + { + return flag; + } + private: + void operator=(check_flag&); + }; + + + void wait_without_predicate() + { + tsk::spin::mutex::scoped_lock lock(mutex); + while(!flag) + { + cond_var.wait(lock); + } + ++woken; + } + + void wait_with_predicate() + { + tsk::spin::mutex::scoped_lock lock(mutex); + cond_var.wait(lock,check_flag(flag)); + if(flag) + { + ++woken; + } + } + + void timed_wait_without_predicate() + { + boost::system_time const timeout=boost::get_system_time()+boost::posix_time::seconds(timeout_seconds); + + tsk::spin::mutex::scoped_lock lock(mutex); + while(!flag) + { + if(!cond_var.timed_wait(lock,timeout)) + { + return; + } + } + ++woken; + } + + void timed_wait_with_predicate() + { + boost::system_time const timeout=boost::get_system_time()+boost::posix_time::seconds(timeout_seconds); + tsk::spin::mutex::scoped_lock lock(mutex); + if(cond_var.timed_wait(lock,timeout,check_flag(flag)) && flag) + { + ++woken; + } + } + void relative_timed_wait_with_predicate() + { + tsk::spin::mutex::scoped_lock lock(mutex); + if(cond_var.timed_wait(lock,boost::posix_time::seconds(timeout_seconds),check_flag(flag)) && flag) + { + ++woken; + } + } +}; + +#endif diff --git a/libs/task/test/test_as_sub_task.cpp b/libs/task/test/test_as_sub_task.cpp new file mode 100755 index 00000000..f6ea7dfb --- /dev/null +++ b/libs/task/test/test_as_sub_task.cpp @@ -0,0 +1,70 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "test_functions.hpp" + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +namespace { + +bool exec_sub_task() +{ + tsk::handle< bool > h( + tsk::async( + tsk::make_task( runs_in_pool_fn), + tsk::as_sub_task() ) ); + return h.get(); +} + +void test_runs_not_in_pool() +{ + tsk::task< bool > t( runs_in_pool_fn); + tsk::handle< bool > h( + tsk::async( boost::move( t), tsk::as_sub_task() ) ); + BOOST_CHECK_EQUAL( h.get(), false); +} + +void test_runs_in_pool() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 1) ); + tsk::handle< bool > h( + tsk::async( + tsk::make_task( exec_sub_task), + pool) ); + BOOST_CHECK_EQUAL( h.get(), true); +} + +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test_framework::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: as-sub-task test suite"); + + test->add( BOOST_TEST_CASE( & test_runs_in_pool) ); + test->add( BOOST_TEST_CASE( & test_runs_not_in_pool) ); + + return test; +} diff --git a/libs/task/test/test_bounded_pool.cpp b/libs/task/test/test_bounded_pool.cpp new file mode 100644 index 00000000..32397263 --- /dev/null +++ b/libs/task/test/test_bounded_pool.cpp @@ -0,0 +1,509 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "test_functions.hpp" + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +// check size and move op +void test_case_1() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool1( + tsk::poolsize( 3), + tsk::high_watermark( 10), + tsk::low_watermark( 5) ); + BOOST_CHECK( pool1); + BOOST_CHECK_EQUAL( pool1.size(), std::size_t( 3) ); + BOOST_CHECK_EQUAL( pool1.upper_bound(), std::size_t( 10) ); + BOOST_CHECK_EQUAL( pool1.lower_bound(), std::size_t( 5) ); + + tsk::static_pool< + tsk::bounded_fifo + > pool2; + BOOST_CHECK( ! pool2); + BOOST_CHECK_THROW( pool2.size(), tsk::pool_moved); + BOOST_CHECK_THROW( pool2.upper_bound(), tsk::pool_moved); + BOOST_CHECK_THROW( pool2.lower_bound(), tsk::pool_moved); + + pool2 = boost::move( pool1); + + BOOST_CHECK( ! pool1); + BOOST_CHECK_THROW( pool1.size(), tsk::pool_moved); + BOOST_CHECK_THROW( pool1.upper_bound(), tsk::pool_moved); + BOOST_CHECK_THROW( pool1.lower_bound(), tsk::pool_moved); + + BOOST_CHECK( pool2); + BOOST_CHECK_EQUAL( pool2.size(), std::size_t( 3) ); + BOOST_CHECK_EQUAL( pool2.upper_bound(), std::size_t( 10) ); + BOOST_CHECK_EQUAL( pool2.lower_bound(), std::size_t( 5) ); + + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h( + tsk::async( boost::move( t), pool2) ); + BOOST_CHECK_EQUAL( h.get(), 55); +} + +// check submit +void test_case_2() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 1), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK_EQUAL( h.get(), 55); +} + +// check assignment +void test_case_3() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h1; + tsk::handle< int > h2( + tsk::async( boost::move( t), pool) ); + h1 = h2; + BOOST_CHECK_EQUAL( h1.get(), 55); + BOOST_CHECK_EQUAL( h2.get(), 55); +} + +// check swap +void test_case_4() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + tsk::task< int > t1( fibonacci_fn, 5); + tsk::task< int > t2( fibonacci_fn, 10); + tsk::handle< int > h1( + tsk::async( boost::move( t1), pool) ); + tsk::handle< int > h2( + tsk::async( boost::move( t2), pool) ); + BOOST_CHECK_EQUAL( h1.get(), 5); + BOOST_CHECK_EQUAL( h2.get(), 55); + BOOST_CHECK_NO_THROW( h1.swap( h2) ); + BOOST_CHECK_EQUAL( h1.get(), 55); + BOOST_CHECK_EQUAL( h2.get(), 5); +} + +// check runs in pool +void test_case_5() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 1), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + tsk::task< bool > t( runs_in_pool_fn); + tsk::handle< bool > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK_EQUAL( h.get(), true); +} + +// check shutdown +void test_case_6() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 1), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h( + tsk::async( boost::move( t), pool) ); + pool.shutdown(); + BOOST_CHECK( pool.closed() ); + BOOST_CHECK_EQUAL( h.get(), 55); +} + +// check runtime_error throw inside task +void test_case_7() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 1), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + tsk::task< void > t( throwing_fn); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + pool.shutdown(); + BOOST_CHECK_THROW( h.get(), std::runtime_error); +} + +// check shutdown with task_rejected exception +void test_case_8() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 1), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + tsk::task< int > t( fibonacci_fn, 10); + pool.shutdown(); + BOOST_CHECK( pool.closed() ); + BOOST_CHECK_THROW( + tsk::async( boost::move( t), pool), + tsk::task_rejected); +} + +// check shutdown_now with thread_interrupted exception +void test_case_9() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 1), + tsk::high_watermark( 1), + tsk::low_watermark( 1) ); + tsk::task< void > t( delay_fn, pt::millisec( 500) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK_EQUAL( pool.size(), std::size_t( 1) ); + pool.shutdown_now(); + BOOST_CHECK( pool.closed() ); + BOOST_CHECK_EQUAL( pool.size(), std::size_t( 0) ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check wait +void test_case_10() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 1), + tsk::low_watermark( 1) ); + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h( + tsk::async( boost::move( t), pool) ); + h.wait(); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); + BOOST_CHECK_EQUAL( h.get(), 55); +} + +// check wait_for +void test_case_11() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 1), + tsk::low_watermark( 1) ); + tsk::task< void > t( delay_fn, pt::seconds( 1) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( h.wait_for( pt::seconds( 3) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_for +void test_case_12() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 1), + tsk::low_watermark( 1) ); + tsk::task< void > t( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( ! h.wait_for( pt::seconds( 1) ) ); + BOOST_CHECK( ! h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_for +void test_case_13() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 1), + tsk::low_watermark( 1) ); + tsk::task< void > t( delay_fn, pt::seconds( 1) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( h.wait_until( boost::get_system_time() + pt::seconds( 3) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_for +void test_case_14() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 1), + tsk::low_watermark( 1) ); + tsk::task< void > t( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( ! h.wait_until( boost::get_system_time() + pt::seconds( 1) ) ); + BOOST_CHECK( ! h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check interrupt +void test_case_15() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + tsk::task< void > t( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + h.interrupt(); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check interrupt_all_worker +void test_case_16() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + tsk::task< void > t1( delay_fn, pt::seconds( 3) ); + tsk::task< void > t2( delay_fn, pt::seconds( 3) ); + tsk::task< void > t3( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h1( + tsk::async( boost::move( t1), pool) ); + tsk::handle< void > h2( + tsk::async( boost::move( t2), pool) ); + tsk::handle< void > h3( + tsk::async( boost::move( t3), pool) ); + boost::this_thread::sleep( pt::millisec( 250) ); + pool.interrupt_all_worker(); + BOOST_CHECK( ! h1.interruption_requested() ); + BOOST_CHECK( ! h2.interruption_requested() ); + BOOST_CHECK( ! h3.interruption_requested() ); + BOOST_CHECK_THROW( h1.get(), tsk::task_interrupted); + BOOST_CHECK_THROW( h2.get(), tsk::task_interrupted); + BOOST_CHECK_THROW( h3.get(), tsk::task_interrupted); + BOOST_CHECK_EQUAL( pool.size(), std::size_t( 5) ); +} + +// check interrupt_and_wait +void test_case_17() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + bool finished( false); + tsk::task< void > t( interrupt_fn, pt::seconds( 1), boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + h.interrupt_and_wait(); + BOOST_CHECK( finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( h.has_exception() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check interrupt_and_wait_for +void test_case_18() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + bool finished( false); + tsk::task< void > t( interrupt_fn, pt::seconds( 1), boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( h.interrupt_and_wait_for( pt::seconds( 3) ) ); + BOOST_CHECK( finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( h.has_exception() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check interrupt_and_wait_for +void test_case_19() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + tsk::task< void > t( non_interrupt_fn, 3); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( ! h.interrupt_and_wait_for( pt::seconds( 1) ) ); +} + +// check interrupt_and_wait_until +void test_case_20() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + bool finished( false); + tsk::task< void > t( + interrupt_fn, + pt::seconds( 1), + boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( h.interrupt_and_wait_until( boost::get_system_time() + pt::seconds( 3) ) ); + BOOST_CHECK( finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( h.has_exception() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check interrupt_and_wait_until +void test_case_21() +{ + tsk::static_pool< + tsk::bounded_fifo + > pool( + tsk::poolsize( 5), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + tsk::task< void > t( non_interrupt_fn, 3); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( ! h.interrupt_and_wait_until( boost::get_system_time() + pt::seconds( 1) ) ); +} + +// check fifo scheduling +void test_case_22() +{ + typedef tsk::static_pool< + tsk::bounded_fifo + > pool_type; + BOOST_CHECK( ! tsk::has_attribute< pool_type >::value); + pool_type pool( + tsk::poolsize( 1), + tsk::high_watermark( 10), + tsk::low_watermark( 10) ); + boost::barrier b( 2); + std::vector< int > buffer; + tsk::task< void > t1( barrier_fn, boost::ref( b) ); + tsk::task< void > t2( + buffer_fibonacci_fn, + boost::ref( buffer), + 10); + tsk::task< void > t3( + buffer_fibonacci_fn, + boost::ref( buffer), + 0); + tsk::async( boost::move( t1), pool); + boost::this_thread::sleep( pt::millisec( 250) ); + tsk::async( boost::move( t2), pool); + tsk::async( boost::move( t3), pool); + b.wait(); + pool.shutdown(); + BOOST_CHECK_EQUAL( buffer[0], 55); + BOOST_CHECK_EQUAL( buffer[1], 0); + BOOST_CHECK_EQUAL( buffer.size(), std::size_t( 2) ); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: bounded-pool test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + test->add( BOOST_TEST_CASE( & test_case_5) ); + test->add( BOOST_TEST_CASE( & test_case_6) ); + test->add( BOOST_TEST_CASE( & test_case_7) ); + test->add( BOOST_TEST_CASE( & test_case_8) ); + test->add( BOOST_TEST_CASE( & test_case_9) ); + test->add( BOOST_TEST_CASE( & test_case_10) ); + test->add( BOOST_TEST_CASE( & test_case_11) ); + test->add( BOOST_TEST_CASE( & test_case_12) ); + test->add( BOOST_TEST_CASE( & test_case_13) ); + test->add( BOOST_TEST_CASE( & test_case_14) ); + test->add( BOOST_TEST_CASE( & test_case_15) ); + test->add( BOOST_TEST_CASE( & test_case_16) ); + test->add( BOOST_TEST_CASE( & test_case_17) ); + test->add( BOOST_TEST_CASE( & test_case_18) ); + test->add( BOOST_TEST_CASE( & test_case_19) ); + test->add( BOOST_TEST_CASE( & test_case_20) ); + test->add( BOOST_TEST_CASE( & test_case_21) ); + test->add( BOOST_TEST_CASE( & test_case_22) ); + + return test; +} + diff --git a/libs/task/test/test_functions.hpp b/libs/task/test/test_functions.hpp new file mode 100644 index 00000000..46d29e29 --- /dev/null +++ b/libs/task/test/test_functions.hpp @@ -0,0 +1,99 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TP_TEST_FUNCTIONS_H +#define BOOST_TP_TEST_FUNCTIONS_H + +#include +#include +#include +#include +#include + +#include + +extern "C" +{ +#if defined( BOOST_POSIX_API) +#include +# endif +# if defined( BOOST_WINDOWS_API) +#include +# endif +} + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +void barrier_fn( + boost::barrier & b) +{ b.wait(); } + +void delay_fn( pt::time_duration const& td) +{ boost::this_thread::sleep( td); } + +void non_interrupt_fn( int sec) +{ +# if defined( BOOST_WINDOWS_API) + ::Sleep( sec * 1000); +# else + ::sleep( sec); +# endif +} + +void interrupt_fn( pt::time_duration const& td, bool & finished) +{ + try + { boost::this_thread::sleep( td); } + catch (...) + { + finished = true; + throw; + } +} + +inline +int fibonacci_fn( int n) +{ + if ( n < 2) return n; + int k1( 1), k2( 0); + for ( int i( 2); i <= n; ++i) + { + boost::this_thread::interruption_point(); + int tmp( k1); + k1 = k1 + k2; + k2 = tmp; + } + boost::this_thread::interruption_point(); + return k1; +} + +inline +void buffer_fibonacci_fn( + std::vector< int > & buffer, + int n) +{ buffer.push_back( fibonacci_fn( n) ); } + +inline +bool runs_in_pool_fn() +{ return boost::this_task::runs_in_pool(); } + +inline +void throwing_fn() +{ throw std::runtime_error("exception thrown"); } + +#endif // BOOST_TP_TEST_FUNCTIONS_H diff --git a/libs/task/test/test_new_thread.cpp b/libs/task/test/test_new_thread.cpp new file mode 100644 index 00000000..e5fd30b6 --- /dev/null +++ b/libs/task/test/test_new_thread.cpp @@ -0,0 +1,284 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "test_functions.hpp" + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +// check assignment +void test_case_1() +{ + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h1; + tsk::handle< int > h2( + tsk::async( boost::move( t), tsk::new_thread() ) ); + h1 = h2; + BOOST_CHECK_EQUAL( h1.get(), 55); + BOOST_CHECK_EQUAL( h2.get(), 55); +} + +// check swap +void test_case_2() +{ + tsk::task< int > t1( fibonacci_fn, 5); + tsk::task< int > t2( fibonacci_fn, 10); + tsk::handle< int > h1( + tsk::async( boost::move( t1), tsk::new_thread() ) ); + tsk::handle< int > h2( + tsk::async( boost::move( t2), tsk::new_thread() ) ); + BOOST_CHECK_EQUAL( h1.get(), 5); + BOOST_CHECK_EQUAL( h2.get(), 55); + BOOST_CHECK_NO_THROW( h1.swap( h2) ); + BOOST_CHECK_EQUAL( h1.get(), 55); + BOOST_CHECK_EQUAL( h2.get(), 5); +} + +// check runs not in pool +void test_case_3() +{ + tsk::task< bool > t( runs_in_pool_fn); + tsk::handle< bool > h( + tsk::async( boost::move( t), tsk::new_thread() ) ); + BOOST_CHECK_EQUAL( h.get(), false); +} + +// check runtime_error throw inside task +void test_case_4() +{ + tsk::task< void > t( throwing_fn); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::new_thread() ) ); + BOOST_CHECK_THROW( h.get(), std::runtime_error); +} + +// check wait +void test_case_5() +{ + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h( + tsk::async( boost::move( t), tsk::new_thread() ) ); + h.wait(); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); + BOOST_CHECK_EQUAL( h.get(), 55); +} + +// check wait_for +void test_case_6() +{ + tsk::task< void > t( delay_fn, pt::seconds( 1) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::new_thread() ) ); + BOOST_CHECK( h.wait_for( pt::seconds( 3) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_for +void test_case_7() +{ + tsk::task< void > t( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::new_thread() ) ); + BOOST_CHECK( ! h.wait_for( pt::seconds( 1) ) ); + BOOST_CHECK( ! h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_for +void test_case_8() +{ + tsk::task< void > t( delay_fn, pt::seconds( 1) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::new_thread() ) ); + BOOST_CHECK( h.wait_until( boost::get_system_time() + pt::seconds( 3) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_for +void test_case_9() +{ + tsk::task< void > t( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::new_thread() ) ); + BOOST_CHECK( ! h.wait_until( boost::get_system_time() + pt::seconds( 1) ) ); + BOOST_CHECK( ! h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check interrupt +void test_case_10() +{ + tsk::task< void > t( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::new_thread() ) ); + h.interrupt(); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check interrupt_and_wait +void test_case_11() +{ + bool finished( false); + tsk::task< void > t( + interrupt_fn, + pt::seconds( 1), + boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::new_thread() ) ); + h.interrupt_and_wait(); + BOOST_CHECK( finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( h.has_exception() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check interrupt_and_wait_for +void test_case_12() +{ + bool finished( false); + tsk::task< void > t( + interrupt_fn, + pt::seconds( 1), + boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::new_thread() ) ); + BOOST_CHECK( h.interrupt_and_wait_for( pt::seconds( 3) ) ); + BOOST_CHECK( finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( h.has_exception() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check interrupt_and_wait_for +void test_case_13() +{ + tsk::task< void > t( non_interrupt_fn, 3); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::new_thread() ) ); + BOOST_CHECK( ! h.interrupt_and_wait_for( pt::seconds( 1) ) ); +} + +// check interrupt_and_wait_until +void test_case_14() +{ + bool finished( false); + tsk::task< void > t( + interrupt_fn, + pt::seconds( 1), + boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::new_thread() ) ); + BOOST_CHECK( h.interrupt_and_wait_until( boost::get_system_time() + pt::seconds( 3) ) ); + BOOST_CHECK( finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( h.has_exception() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check interrupt_and_wait_until +void test_case_15() +{ + tsk::task< void > t( non_interrupt_fn, 3); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::new_thread() ) ); + BOOST_CHECK( ! h.interrupt_and_wait_until( boost::get_system_time() + pt::seconds( 1) ) ); +} + +// check waitfor_all() +void test_case_16() +{ + std::vector< tsk::handle< int > > vec; + for ( int i = 0; i <= 5; ++i) + { + tsk::task< int > t( fibonacci_fn, i); + vec.push_back( + tsk::async( boost::move( t), tsk::new_thread() ) ); + } + tsk::waitfor_all( vec.begin(), vec.end() ); + BOOST_CHECK( vec[0].is_ready() ); + BOOST_CHECK( vec[1].is_ready() ); + BOOST_CHECK( vec[2].is_ready() ); + BOOST_CHECK( vec[3].is_ready() ); + BOOST_CHECK( vec[4].is_ready() ); + BOOST_CHECK( vec[5].is_ready() ); + BOOST_CHECK_EQUAL( vec[0].get(), 0); + BOOST_CHECK_EQUAL( vec[1].get(), 1); + BOOST_CHECK_EQUAL( vec[2].get(), 1); + BOOST_CHECK_EQUAL( vec[3].get(), 2); + BOOST_CHECK_EQUAL( vec[4].get(), 3); + BOOST_CHECK_EQUAL( vec[5].get(), 5); +} +/* +// check waitfor_any() +void test_case_17() +{ + tsk::task< void > t1( delay_fn, pt::seconds( 2) ); + tsk::task< void > t2( delay_fn, pt::seconds( 1) ); + tsk::handle< void > h1( + tsk::async( boost::move( t1), tsk::new_thread() ) ); + tsk::handle< void > h2( + tsk::async( boost::move( t2), tsk::new_thread() ) ); + tsk::waitfor_any( h1, h2); + BOOST_CHECK( ! h1.is_ready() ); + BOOST_CHECK( h2.is_ready() ); +} +*/ +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: new-thread test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + test->add( BOOST_TEST_CASE( & test_case_5) ); + test->add( BOOST_TEST_CASE( & test_case_6) ); + test->add( BOOST_TEST_CASE( & test_case_7) ); + test->add( BOOST_TEST_CASE( & test_case_8) ); + test->add( BOOST_TEST_CASE( & test_case_9) ); + test->add( BOOST_TEST_CASE( & test_case_10) ); + test->add( BOOST_TEST_CASE( & test_case_11) ); + test->add( BOOST_TEST_CASE( & test_case_12) ); + test->add( BOOST_TEST_CASE( & test_case_13) ); + test->add( BOOST_TEST_CASE( & test_case_14) ); + test->add( BOOST_TEST_CASE( & test_case_15) ); + test->add( BOOST_TEST_CASE( & test_case_16) ); +// test->add( BOOST_TEST_CASE( & test_case_17) ); + + return test; +} diff --git a/libs/task/test/test_own_thread.cpp b/libs/task/test/test_own_thread.cpp new file mode 100644 index 00000000..63c275ef --- /dev/null +++ b/libs/task/test/test_own_thread.cpp @@ -0,0 +1,312 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "test_functions.hpp" + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +// check assignment +void test_case_1() +{ + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h1; + tsk::handle< int > h2( + tsk::async( boost::move( t), tsk::own_thread() ) ); + h1 = h2; + BOOST_CHECK_EQUAL( h1.get(), 55); + BOOST_CHECK_EQUAL( h2.get(), 55); +} + +// check swap +void test_case_2() +{ + tsk::task< int > t1( fibonacci_fn, 5); + tsk::task< int > t2( fibonacci_fn, 10); + tsk::handle< int > h1( + tsk::async( boost::move( t1), tsk::own_thread() ) ); + tsk::handle< int > h2( + tsk::async( boost::move( t2), tsk::own_thread() ) ); + BOOST_CHECK_EQUAL( h1.get(), 5); + BOOST_CHECK_EQUAL( h2.get(), 55); + BOOST_CHECK_NO_THROW( h1.swap( h2) ); + BOOST_CHECK_EQUAL( h1.get(), 55); + BOOST_CHECK_EQUAL( h2.get(), 5); +} + +// check runs not in pool +void test_case_3() +{ + tsk::task< bool > t( runs_in_pool_fn); + tsk::handle< bool > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + BOOST_CHECK_EQUAL( h.get(), false); +} + +// check runtime_error throw inside task +void test_case_4() +{ + tsk::task< void > t( throwing_fn); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + BOOST_CHECK_THROW( h.get(), std::runtime_error); +} + +// check task_uninitialized +void test_case_5() +{ + tsk::handle< int > h; + BOOST_CHECK_THROW( h.get(), tsk::task_uninitialized); + BOOST_CHECK_THROW( h.wait(), tsk::task_uninitialized); + BOOST_CHECK_THROW( h.wait_for( pt::seconds( 1) ), tsk::task_uninitialized); + BOOST_CHECK_THROW( + h.wait_until( boost::get_system_time() + pt::seconds( 1) ), + tsk::task_uninitialized); + BOOST_CHECK( ! h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait +void test_case_6() +{ + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + h.wait(); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); + BOOST_CHECK_EQUAL( h.get(), 55); +} + +// check wait_for +void test_case_7() +{ + tsk::task< void > t( delay_fn, pt::seconds( 1) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + BOOST_CHECK( h.wait_for( pt::seconds( 2) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_for +void test_case_8() +{ + tsk::task< void > t( delay_fn, pt::seconds( 2) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + BOOST_CHECK( h.wait_for( pt::seconds( 1) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_for +void test_case_9() +{ + tsk::task< void > t( delay_fn, pt::seconds( 1) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + BOOST_CHECK( h.wait_until( boost::get_system_time() + pt::seconds( 3) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_for +void test_case_10() +{ + tsk::task< void > t( delay_fn, pt::seconds( 2) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + BOOST_CHECK( h.wait_until( boost::get_system_time() + pt::seconds( 1) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check interrupt +void test_case_11() +{ + tsk::task< void > t( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + h.interrupt(); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +// check interrupt_and_wait +void test_case_12() +{ + bool finished( false); + tsk::task< void > t( + interrupt_fn, + pt::seconds( 3), + boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + h.interrupt_and_wait(); + BOOST_CHECK( ! finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +// check interrupt_and_wait_for +void test_case_13() +{ + bool finished( false); + tsk::task< void > t( + interrupt_fn, + pt::seconds( 1), + boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + BOOST_CHECK( h.interrupt_and_wait_for( pt::seconds( 2) ) ); + BOOST_CHECK( ! finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +// check interrupt_and_wait_for +void test_case_14() +{ + tsk::task< void > t( non_interrupt_fn, 2); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + BOOST_CHECK( h.interrupt_and_wait_for( pt::seconds( 1) ) ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +// check interrupt_and_wait_until +void test_case_15() +{ + bool finished( false); + tsk::task< void > t( + interrupt_fn, + pt::seconds( 1), + boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + BOOST_CHECK( h.interrupt_and_wait_until( boost::get_system_time() + pt::seconds( 2) ) ); + BOOST_CHECK( ! finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +// check interrupt_and_wait_until +void test_case_16() +{ + tsk::task< void > t( non_interrupt_fn, 2); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + BOOST_CHECK( h.interrupt_and_wait_until( boost::get_system_time() + pt::seconds( 1) ) ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +// check waitfor_all() +void test_case_17() +{ + std::vector< tsk::handle< int > > vec; + for ( int i = 0; i <= 5; ++i) + { + tsk::task< int > t( fibonacci_fn, i); + vec.push_back( + tsk::async( boost::move( t), tsk::own_thread() ) ); + } + tsk::waitfor_all( vec.begin(), vec.end() ); + BOOST_CHECK( vec[0].is_ready() ); + BOOST_CHECK( vec[1].is_ready() ); + BOOST_CHECK( vec[2].is_ready() ); + BOOST_CHECK( vec[3].is_ready() ); + BOOST_CHECK( vec[4].is_ready() ); + BOOST_CHECK( vec[5].is_ready() ); + BOOST_CHECK_EQUAL( vec[0].get(), 0); + BOOST_CHECK_EQUAL( vec[1].get(), 1); + BOOST_CHECK_EQUAL( vec[2].get(), 1); + BOOST_CHECK_EQUAL( vec[3].get(), 2); + BOOST_CHECK_EQUAL( vec[4].get(), 3); + BOOST_CHECK_EQUAL( vec[5].get(), 5); +} +/* +// check waitfor_any() +void test_case_18() +{ + tsk::task< void > t1( delay_fn, pt::seconds( 2) ); + tsk::task< void > t2( delay_fn, pt::seconds( 1) ); + tsk::handle< void > h1( + tsk::async( boost::move( t1), tsk::own_thread() ) ); + tsk::handle< void > h2( + tsk::async( boost::move( t2), tsk::own_thread() ) ); + tsk::waitfor_any( h1, h2); + BOOST_CHECK( h1.is_ready() ); + BOOST_CHECK( h2.is_ready() ); +} +*/ +// check interrupt + wait +void test_case_19() +{ + tsk::task< void > t( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h( + tsk::async( boost::move( t), tsk::own_thread() ) ); + h.interrupt(); + BOOST_CHECK_NO_THROW( h.wait() ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: own-thread test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + test->add( BOOST_TEST_CASE( & test_case_5) ); + test->add( BOOST_TEST_CASE( & test_case_6) ); + test->add( BOOST_TEST_CASE( & test_case_7) ); + test->add( BOOST_TEST_CASE( & test_case_8) ); + test->add( BOOST_TEST_CASE( & test_case_9) ); + test->add( BOOST_TEST_CASE( & test_case_10) ); + test->add( BOOST_TEST_CASE( & test_case_11) ); + test->add( BOOST_TEST_CASE( & test_case_12) ); + test->add( BOOST_TEST_CASE( & test_case_13) ); + test->add( BOOST_TEST_CASE( & test_case_14) ); + test->add( BOOST_TEST_CASE( & test_case_15) ); + test->add( BOOST_TEST_CASE( & test_case_16) ); + test->add( BOOST_TEST_CASE( & test_case_17) ); +// test->add( BOOST_TEST_CASE( & test_case_18) ); + test->add( BOOST_TEST_CASE( & test_case_19) ); + + return test; +} diff --git a/libs/task/test/test_spin_auto_reset_event.cpp b/libs/task/test/test_spin_auto_reset_event.cpp new file mode 100755 index 00000000..363edd72 --- /dev/null +++ b/libs/task/test/test_spin_auto_reset_event.cpp @@ -0,0 +1,210 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +boost::uint32_t wait_fn( boost::uint32_t n, tsk::spin::auto_reset_event & ev) +{ + ev.wait(); + return n; +} + +// check wait in new thread +void test_case_1() +{ + boost::uint32_t n = 3; + tsk::spin::auto_reset_event ev; + + tsk::handle< boost::uint32_t > h1( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + tsk::handle< boost::uint32_t > h2( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( ! h1.is_ready() ); + BOOST_CHECK( ! h2.is_ready() ); + + ev.set(); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h1.is_ready() || h2.is_ready() ); + if ( h1.is_ready() ) + { + BOOST_CHECK_EQUAL( h1.get(), n); + BOOST_CHECK( ! h2.is_ready() ); + + ev.set(); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h2.is_ready() ); + BOOST_CHECK_EQUAL( h2.get(), n); + } + else + { + BOOST_CHECK( h2.is_ready() ); + BOOST_CHECK_EQUAL( h2.get(), n); + BOOST_CHECK( ! h1.is_ready() ); + + ev.set(); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h1.is_ready() ); + BOOST_CHECK_EQUAL( h1.get(), n); + + } +} + +// check wait in pool +void test_case_2() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + + boost::uint32_t n = 3; + tsk::spin::auto_reset_event ev; + + tsk::handle< boost::uint32_t > h1( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + pool) ); + tsk::handle< boost::uint32_t > h2( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + pool) ); + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( ! h1.is_ready() ); + BOOST_CHECK( ! h2.is_ready() ); + + ev.set(); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h1.is_ready() || h2.is_ready() ); + if ( h1.is_ready() ) + { + BOOST_CHECK_EQUAL( h1.get(), n); + BOOST_CHECK( ! h2.is_ready() ); + + ev.set(); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h2.is_ready() ); + BOOST_CHECK_EQUAL( h2.get(), n); + } + else + { + BOOST_CHECK( h2.is_ready() ); + BOOST_CHECK_EQUAL( h2.get(), n); + BOOST_CHECK( ! h1.is_ready() ); + + ev.set(); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h1.is_ready() ); + BOOST_CHECK_EQUAL( h1.get(), n); + + } +} + +void test_case_3() +{ + boost::uint32_t n = 3; + tsk::spin::auto_reset_event ev( true); + + tsk::handle< boost::uint32_t > h1( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + tsk::handle< boost::uint32_t > h2( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h1.is_ready() || h2.is_ready() ); + if ( h1.is_ready() ) + { + BOOST_CHECK_EQUAL( h1.get(), n); + BOOST_CHECK( ! h2.is_ready() ); + + ev.set(); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h2.is_ready() ); + BOOST_CHECK_EQUAL( h2.get(), n); + } + else + { + BOOST_CHECK( h2.is_ready() ); + BOOST_CHECK_EQUAL( h2.get(), n); + BOOST_CHECK( ! h1.is_ready() ); + + ev.set(); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h1.is_ready() ); + BOOST_CHECK_EQUAL( h1.get(), n); + + } +} + +void test_case_4() +{ + tsk::spin::auto_reset_event ev; + + BOOST_CHECK_EQUAL( false, ev.try_wait() ); + + ev.set(); + + BOOST_CHECK_EQUAL( true, ev.try_wait() ); + BOOST_CHECK_EQUAL( false, ev.try_wait() ); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: spin-auto-reset-event test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + + return test; +} diff --git a/libs/task/test/test_spin_bounded_channel.cpp b/libs/task/test/test_spin_bounded_channel.cpp new file mode 100644 index 00000000..1662af0a --- /dev/null +++ b/libs/task/test/test_spin_bounded_channel.cpp @@ -0,0 +1,171 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +struct send_data +{ + tsk::spin::bounded_channel< int > & buf; + + send_data( tsk::spin::bounded_channel< int > & buf_) : + buf( buf_) + {} + + void operator()( int value) + { buf.put( value); } +}; + +struct recv_data +{ + tsk::spin::bounded_channel< int > & buf; + int value; + + recv_data( tsk::spin::bounded_channel< int > & buf_) : + buf( buf_), value( 0) + {} + + void operator()( ) + { + boost::optional< int > res; + if ( buf.take( res) ) + value = * res; + } +}; + +void test_case_1() +{ + tsk::spin::bounded_channel< int > buf( tsk::high_watermark( 10), tsk::low_watermark( 10) ); + BOOST_CHECK_EQUAL( true, buf.empty() ); + BOOST_CHECK_EQUAL( true, buf.active() ); + int n = 1; + buf.put( n); + BOOST_CHECK_EQUAL( false, buf.empty() ); + boost::optional< int > res; + BOOST_CHECK_EQUAL( true, buf.take( res) ); + BOOST_CHECK( res); + BOOST_CHECK_EQUAL( n, res.get() ); + buf.deactivate(); + BOOST_CHECK_EQUAL( false, buf.active() ); + BOOST_CHECK_THROW( buf.put( 1), std::runtime_error); +} + +void test_case_2() +{ + tsk::spin::bounded_channel< int > buf( tsk::high_watermark( 10), tsk::low_watermark( 10) ); + BOOST_CHECK_EQUAL( true, buf.empty() ); + BOOST_CHECK_EQUAL( true, buf.active() ); + int n = 1; + buf.put( n); + BOOST_CHECK_EQUAL( false, buf.empty() ); + boost::optional< int > res; + BOOST_CHECK_EQUAL( true, buf.try_take( res) ); + BOOST_CHECK( res); + BOOST_CHECK_EQUAL( n, res.get() ); + BOOST_CHECK_EQUAL( false, buf.try_take( res) ); + BOOST_CHECK_EQUAL( false, buf.take( res, pt::milliseconds( 10) ) ); +} + +void test_case_3() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 2) ); + + tsk::spin::bounded_channel< int > buf( tsk::high_watermark( 10), tsk::low_watermark( 10) ); + + int n = 37; + + recv_data receiver( buf); + BOOST_CHECK_EQUAL( 0, receiver.value); + tsk::handle< void > h = + tsk::async( + tsk::make_task( + & recv_data::operator(), + boost::ref( receiver) ), + pool); + + boost::this_thread::sleep( + pt::milliseconds( 250) ); + + BOOST_CHECK_EQUAL( false, h.is_ready() ); + buf.put( n); + + h.wait(); + + BOOST_CHECK_EQUAL( n, receiver.value); +} + +void test_case_4() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 2) ); + + tsk::spin::bounded_channel< int > buf( tsk::high_watermark( 10), tsk::low_watermark( 10) ); + + int n = 37; + + send_data sender( buf); + recv_data receiver( buf); + BOOST_CHECK_EQUAL( 0, receiver.value); + + tsk::handle< void > h1 = + tsk::async( + tsk::make_task( + & recv_data::operator(), + boost::ref( receiver) ), + pool); + + boost::this_thread::sleep( + pt::milliseconds( 250) ); + BOOST_CHECK_EQUAL( false, h1.is_ready() ); + + tsk::handle< void > h2 = + tsk::async( + tsk::make_task( + & send_data::operator(), + boost::ref( sender), + n), + pool); + + h2.wait(); + BOOST_CHECK_EQUAL( true, h2.is_ready() ); + h1.wait(); + BOOST_CHECK_EQUAL( true, h1.is_ready() ); + + BOOST_CHECK_EQUAL( n, receiver.value); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: bounded-buffer test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + + return test; +} diff --git a/libs/task/test/test_spin_condition.cpp b/libs/task/test/test_spin_condition.cpp new file mode 100644 index 00000000..eeeb7121 --- /dev/null +++ b/libs/task/test/test_spin_condition.cpp @@ -0,0 +1,191 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// Copyright (C) 2007 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include + +#include + +namespace tsk = boost::tasks; +namespace pt = boost::posix_time; + +struct condition_test_data +{ + condition_test_data() : notified(0), awoken(0) { } + + tsk::spin::mutex mutex; + tsk::spin::condition condition; + int notified; + int awoken; +}; + +void condition_test_thread(condition_test_data* data) +{ + tsk::spin::mutex::scoped_lock lock(data->mutex); + BOOST_CHECK(lock ? true : false); + while (!(data->notified > 0)) + data->condition.wait(lock); + BOOST_CHECK(lock ? true : false); + data->awoken++; +} + +struct cond_predicate +{ + cond_predicate(int& var, int val) : _var(var), _val(val) { } + + bool operator()() { return _var == _val; } + + int& _var; + int _val; +private: + void operator=(cond_predicate&); + +}; + +void condition_test_waits(condition_test_data* data) +{ + tsk::spin::mutex::scoped_lock lock(data->mutex); + BOOST_CHECK(lock ? true : false); + + // Test wait. + while (data->notified != 1) + data->condition.wait(lock); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data->notified, 1); + data->awoken++; + data->condition.notify_one(); + + // Test predicate wait. + data->condition.wait(lock, cond_predicate(data->notified, 2)); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data->notified, 2); + data->awoken++; + data->condition.notify_one(); + + // Test timed_wait. + pt::time_duration xt = pt::seconds(10); + while (data->notified != 3) + data->condition.timed_wait(lock, xt); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data->notified, 3); + data->awoken++; + data->condition.notify_one(); + + // Test predicate timed_wait. + xt = pt::seconds(10); + cond_predicate pred(data->notified, 4); + BOOST_CHECK(data->condition.timed_wait(lock, xt, pred)); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK(pred()); + BOOST_CHECK_EQUAL(data->notified, 4); + data->awoken++; + data->condition.notify_one(); + + // Test predicate timed_wait with relative timeout + cond_predicate pred_rel(data->notified, 5); + BOOST_CHECK(data->condition.timed_wait(lock, boost::posix_time::seconds(10), pred_rel)); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK(pred_rel()); + BOOST_CHECK_EQUAL(data->notified, 5); + data->awoken++; + data->condition.notify_one(); +} + +void do_test_condition_waits() +{ + condition_test_data data; + + boost::thread thread(bind(&condition_test_waits, &data)); + + { + tsk::spin::mutex::scoped_lock lock(data.mutex); + BOOST_CHECK(lock ? true : false); + + boost::this_thread::sleep(pt::seconds(1)); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 1) + data.condition.wait(lock); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data.awoken, 1); + + boost::this_thread::sleep(pt::seconds(1)); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 2) + data.condition.wait(lock); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data.awoken, 2); + + boost::this_thread::sleep(pt::seconds(1)); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 3) + data.condition.wait(lock); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data.awoken, 3); + + boost::this_thread::sleep(pt::seconds(1)); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 4) + data.condition.wait(lock); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data.awoken, 4); + + + boost::this_thread::sleep(pt::seconds(1)); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 5) + data.condition.wait(lock); + BOOST_CHECK(lock ? true : false); + BOOST_CHECK_EQUAL(data.awoken, 5); + } + + thread.join(); + BOOST_CHECK_EQUAL(data.awoken, 5); +} + +void test_condition_waits() +{ + // We should have already tested notify_one here, so + // a timed test with the default execution_monitor::use_condition + // should be OK, and gives the fastest performance + timed_test(&do_test_condition_waits, 12); +} + +void do_test_condition_wait_is_a_interruption_point() +{ + condition_test_data data; + + boost::thread thread(bind(&condition_test_thread, &data)); + + thread.interrupt(); + thread.join(); + BOOST_CHECK_EQUAL(data.awoken,0); +} + + +void test_condition_wait_is_a_interruption_point() +{ + timed_test(&do_test_condition_wait_is_a_interruption_point, 1); +} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Task: condition test suite"); + + test->add(BOOST_TEST_CASE(&test_condition_waits)); + test->add(BOOST_TEST_CASE(&test_condition_wait_is_a_interruption_point)); + + return test; +} diff --git a/libs/task/test/test_spin_condition_notify_all.cpp b/libs/task/test/test_spin_condition_notify_all.cpp new file mode 100644 index 00000000..f34b996a --- /dev/null +++ b/libs/task/test/test_spin_condition_notify_all.cpp @@ -0,0 +1,225 @@ +// Copyright (C) 2007 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include + +#include + +#include + +#include "condition_test_common.hpp" + +namespace tsk = boost::tasks; + +unsigned const number_of_test_threads=5; + +void do_test_condition_notify_all_wakes_from_wait() +{ + wait_for_flag data; + + boost::thread_group group; + + try + { + for(unsigned i=0;iadd(BOOST_TEST_CASE(&test_condition_notify_all)); + + return test; +} diff --git a/libs/task/test/test_spin_condition_notify_one.cpp b/libs/task/test/test_spin_condition_notify_one.cpp new file mode 100644 index 00000000..9986e1b1 --- /dev/null +++ b/libs/task/test/test_spin_condition_notify_one.cpp @@ -0,0 +1,159 @@ +// Copyright (C) 2007 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include + +#include + +#include + +#include "condition_test_common.hpp" + +namespace tsk = boost::tasks; + +void do_test_condition_notify_one_wakes_from_wait() +{ + wait_for_flag data; + + boost::thread thread(boost::bind(&wait_for_flag::wait_without_predicate, boost::ref(data))); + + { + tsk::spin::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void do_test_condition_notify_one_wakes_from_wait_with_predicate() +{ + wait_for_flag data; + + boost::thread thread(boost::bind(&wait_for_flag::wait_with_predicate, boost::ref(data))); + + { + tsk::spin::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void do_test_condition_notify_one_wakes_from_timed_wait() +{ + wait_for_flag data; + + boost::thread thread(boost::bind(&wait_for_flag::timed_wait_without_predicate, boost::ref(data))); + + { + tsk::spin::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void do_test_condition_notify_one_wakes_from_timed_wait_with_predicate() +{ + wait_for_flag data; + + boost::thread thread(boost::bind(&wait_for_flag::timed_wait_with_predicate, boost::ref(data))); + + { + tsk::spin::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +void do_test_condition_notify_one_wakes_from_relative_timed_wait_with_predicate() +{ + wait_for_flag data; + + boost::thread thread(boost::bind(&wait_for_flag::relative_timed_wait_with_predicate, boost::ref( data))); + + { + tsk::spin::mutex::scoped_lock lock(data.mutex); + data.flag=true; + data.cond_var.notify_one(); + } + + thread.join(); + BOOST_CHECK(data.woken); +} + +namespace +{ + tsk::spin::mutex multiple_wake_mutex; + tsk::spin::condition multiple_wake_cond; + unsigned multiple_wake_count=0; + + void wait_for_condvar_and_increase_count() + { + tsk::spin::mutex::scoped_lock lk(multiple_wake_mutex); + multiple_wake_cond.wait(lk); + ++multiple_wake_count; + } + +} + + +void do_test_multiple_notify_one_calls_wakes_multiple_threads() +{ + boost::thread thread1(wait_for_condvar_and_increase_count); + boost::thread thread2(wait_for_condvar_and_increase_count); + + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + multiple_wake_cond.notify_one(); + + boost::thread thread3(wait_for_condvar_and_increase_count); + + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + multiple_wake_cond.notify_one(); + multiple_wake_cond.notify_one(); + boost::this_thread::sleep(boost::posix_time::milliseconds(200)); + + { + tsk::spin::mutex::scoped_lock lk(multiple_wake_mutex); + BOOST_CHECK(multiple_wake_count==3); + } + + thread1.join(); + thread2.join(); + thread3.join(); +} + +void test_condition_notify_one() +{ + timed_test(&do_test_condition_notify_one_wakes_from_wait, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_condition_notify_one_wakes_from_wait_with_predicate, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_condition_notify_one_wakes_from_timed_wait, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_condition_notify_one_wakes_from_timed_wait_with_predicate, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_condition_notify_one_wakes_from_relative_timed_wait_with_predicate, timeout_seconds, execution_monitor::use_mutex); + timed_test(&do_test_multiple_notify_one_calls_wakes_multiple_threads, timeout_seconds, execution_monitor::use_mutex); +} + + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Task: condition test suite"); + + test->add(BOOST_TEST_CASE(&test_condition_notify_one)); + + return test; +} diff --git a/libs/task/test/test_spin_condition_timed_wait_times_out.cpp b/libs/task/test/test_spin_condition_timed_wait_times_out.cpp new file mode 100644 index 00000000..6e9d069b --- /dev/null +++ b/libs/task/test/test_spin_condition_timed_wait_times_out.cpp @@ -0,0 +1,175 @@ +// Copyright (C) 2007-8 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include + +#include + +#include + +namespace tsk = boost::tasks; + +bool fake_predicate() +{ + return false; +} + +unsigned const timeout_seconds=2; +unsigned const timeout_grace=1; +boost::posix_time::milliseconds const timeout_resolution(100); + + +void do_test_timed_wait_times_out() +{ + tsk::spin::condition cond; + tsk::spin::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + tsk::spin::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+delay; + + while(cond.timed_wait(lock,timeout)); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_timed_wait_with_predicate_times_out() +{ + tsk::spin::condition cond; + tsk::spin::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + tsk::spin::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+delay; + + bool const res=cond.timed_wait(lock,timeout,fake_predicate); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK(!res); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_relative_timed_wait_with_predicate_times_out() +{ + tsk::spin::condition cond; + tsk::spin::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + tsk::spin::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + + bool const res=cond.timed_wait(lock,delay,fake_predicate); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK(!res); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_timed_wait_relative_times_out() +{ + tsk::spin::condition cond; + tsk::spin::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + tsk::spin::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + + while(cond.timed_wait(lock,delay)); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_cv_any_timed_wait_times_out() +{ + tsk::spin::condition cond; + tsk::spin::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + tsk::spin::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+delay; + + while(cond.timed_wait(lock,timeout)); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_cv_any_timed_wait_with_predicate_times_out() +{ + tsk::spin::condition cond; + tsk::spin::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + tsk::spin::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + boost::system_time const timeout=start+delay; + + bool const res=cond.timed_wait(lock,timeout,fake_predicate); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK(!res); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_cv_any_relative_timed_wait_with_predicate_times_out() +{ + tsk::spin::condition cond; + tsk::spin::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + tsk::spin::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + + bool const res=cond.timed_wait(lock,delay,fake_predicate); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK(!res); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + +void do_test_cv_any_timed_wait_relative_times_out() +{ + tsk::spin::condition cond; + tsk::spin::mutex m; + + boost::posix_time::seconds const delay(timeout_seconds); + tsk::spin::mutex::scoped_lock lock(m); + boost::system_time const start=boost::get_system_time(); + + while(cond.timed_wait(lock,delay)); + + boost::system_time const end=boost::get_system_time(); + BOOST_CHECK((delay-timeout_resolution)<=(end-start)); +} + + +void test_timed_wait_times_out() +{ + timed_test(&do_test_timed_wait_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_relative_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_timed_wait_relative_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_cv_any_timed_wait_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_cv_any_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_cv_any_relative_timed_wait_with_predicate_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); + timed_test(&do_test_cv_any_timed_wait_relative_times_out, timeout_seconds+timeout_grace, execution_monitor::use_mutex); +} + +boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[]) +{ + boost::unit_test_framework::test_suite* test = + BOOST_TEST_SUITE("Boost.Task: condition test suite"); + + test->add(BOOST_TEST_CASE(&test_timed_wait_times_out)); + + return test; +} diff --git a/libs/task/test/test_spin_count_down_event.cpp b/libs/task/test/test_spin_count_down_event.cpp new file mode 100755 index 00000000..6f0994af --- /dev/null +++ b/libs/task/test/test_spin_count_down_event.cpp @@ -0,0 +1,122 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +boost::uint32_t wait_fn( boost::uint32_t n, tsk::spin::count_down_event & ev) +{ + ev.wait(); + return n; +} + +// check initial + current +void test_case_1() +{ + boost::uint32_t n = 3; + tsk::spin::count_down_event ev( n); + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), n); + + ev.set(); + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), static_cast< boost::uint32_t >( 2) ); + + ev.set(); + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), static_cast< boost::uint32_t >( 1) ); + + ev.set(); + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), static_cast< boost::uint32_t >( 0) ); + + ev.set(); + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), static_cast< boost::uint32_t >( 0) ); +} + +// check wait in new thread +void test_case_2() +{ + boost::uint32_t n = 3; + tsk::spin::count_down_event ev( n); + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), n); + + tsk::handle< boost::uint32_t > h( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + BOOST_CHECK( ! h.is_ready() ); + for ( boost::uint32_t i = 0; i < n; ++i) + { + ev.set(); + BOOST_CHECK( ! h.is_ready() ); + } + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), static_cast< boost::uint32_t >( 0) ); + BOOST_CHECK_EQUAL( h.get(), n); +} + +// check wait in pool +void test_case_3() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + + boost::uint32_t n = 3; + tsk::spin::count_down_event ev( n); + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), n); + + tsk::handle< boost::uint32_t > h( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + pool) ); + BOOST_CHECK( ! h.is_ready() ); + for ( boost::uint32_t i = 0; i < n; ++i) + { + ev.set(); + BOOST_CHECK( ! h.is_ready() ); + } + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), static_cast< boost::uint32_t >( 0) ); + BOOST_CHECK_EQUAL( h.get(), n); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: spin-count-down-event test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + + return test; +} diff --git a/libs/task/test/test_spin_manual_reset_event.cpp b/libs/task/test/test_spin_manual_reset_event.cpp new file mode 100755 index 00000000..848e9064 --- /dev/null +++ b/libs/task/test/test_spin_manual_reset_event.cpp @@ -0,0 +1,234 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +boost::uint32_t wait_fn( boost::uint32_t n, tsk::spin::manual_reset_event & ev) +{ + ev.wait(); + return n; +} + +// check wait in new thread +void test_case_1() +{ + boost::uint32_t n = 3; + tsk::spin::manual_reset_event ev; + + tsk::handle< boost::uint32_t > h1( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + tsk::handle< boost::uint32_t > h2( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( ! h1.is_ready() ); + BOOST_CHECK( ! h2.is_ready() ); + + ev.set(); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h1.is_ready() ); + BOOST_CHECK( h2.is_ready() ); + BOOST_CHECK_EQUAL( h1.get(), n); + BOOST_CHECK_EQUAL( h2.get(), n); + + ev.reset(); + + tsk::handle< boost::uint32_t > h3( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + tsk::handle< boost::uint32_t > h4( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( ! h3.is_ready() ); + BOOST_CHECK( ! h4.is_ready() ); + + ev.set(); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h3.is_ready() ); + BOOST_CHECK( h4.is_ready() ); + BOOST_CHECK_EQUAL( h3.get(), n); + BOOST_CHECK_EQUAL( h4.get(), n); +} + +// check wait in pool +void test_case_2() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + + boost::uint32_t n = 3; + tsk::spin::manual_reset_event ev; + + tsk::handle< boost::uint32_t > h1( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + pool) ); + tsk::handle< boost::uint32_t > h2( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + pool) ); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( ! h1.is_ready() ); + BOOST_CHECK( ! h2.is_ready() ); + + ev.set(); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h1.is_ready() ); + BOOST_CHECK( h2.is_ready() ); + BOOST_CHECK_EQUAL( h1.get(), n); + BOOST_CHECK_EQUAL( h2.get(), n); + + ev.reset(); + + tsk::handle< boost::uint32_t > h3( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + tsk::handle< boost::uint32_t > h4( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( ! h3.is_ready() ); + BOOST_CHECK( ! h4.is_ready() ); + + ev.set(); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h3.is_ready() ); + BOOST_CHECK( h4.is_ready() ); + BOOST_CHECK_EQUAL( h3.get(), n); + BOOST_CHECK_EQUAL( h4.get(), n); +} + +void test_case_3() +{ + boost::uint32_t n = 3; + tsk::spin::manual_reset_event ev( true); + + tsk::handle< boost::uint32_t > h1( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + tsk::handle< boost::uint32_t > h2( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h1.is_ready() ); + BOOST_CHECK( h2.is_ready() ); + BOOST_CHECK_EQUAL( h1.get(), n); + BOOST_CHECK_EQUAL( h2.get(), n); + + ev.reset(); + + tsk::handle< boost::uint32_t > h3( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + tsk::handle< boost::uint32_t > h4( + tsk::async( + tsk::make_task( + wait_fn, + n, boost::ref( ev) ), + tsk::new_thread() ) ); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( ! h3.is_ready() ); + BOOST_CHECK( ! h4.is_ready() ); + + ev.set(); + + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK( h3.is_ready() ); + BOOST_CHECK( h4.is_ready() ); + BOOST_CHECK_EQUAL( h3.get(), n); + BOOST_CHECK_EQUAL( h4.get(), n); +} + +void test_case_4() +{ + tsk::spin::manual_reset_event ev; + + BOOST_CHECK_EQUAL( false, ev.try_wait() ); + + ev.set(); + + BOOST_CHECK_EQUAL( true, ev.try_wait() ); + BOOST_CHECK_EQUAL( true, ev.try_wait() ); + ev.wait(); + BOOST_CHECK_EQUAL( true, ev.try_wait() ); + + ev.reset(); + BOOST_CHECK_EQUAL( false, ev.try_wait() ); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: spin-manual-reset-event test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + + return test; +} diff --git a/libs/task/test/test_spin_mutex.cpp b/libs/task/test/test_spin_mutex.cpp new file mode 100644 index 00000000..a90ad0fc --- /dev/null +++ b/libs/task/test/test_spin_mutex.cpp @@ -0,0 +1,85 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// This test is based on the tests of Boost.Thread + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +template< typename M > +struct test_lock +{ + typedef M mutex_type; + typedef typename M::scoped_lock lock_type; + + void operator()() + { + mutex_type mutex; + tsk::spin::condition condition; + + // Test the lock's constructors. + { + lock_type lock(mutex, boost::defer_lock); + BOOST_CHECK(!lock); + } + lock_type lock(mutex); + BOOST_CHECK(lock ? true : false); + + // Construct and initialize an xtime for a fast time out. + pt::time_duration xt = pt::milliseconds( 100); + + // Test the lock and the mutex with condition variables. + // No one is going to notify this condition variable. We expect to + // time out. + BOOST_CHECK(!condition.timed_wait(lock, xt)); + BOOST_CHECK(lock ? true : false); + + // Test the lock and unlock methods. + lock.unlock(); + BOOST_CHECK(!lock); + lock.lock(); + BOOST_CHECK(lock ? true : false); + } +}; + +void do_test_mutex() +{ + test_lock< tsk::spin::mutex >()(); +} + +void test_mutex() +{ + timed_test(&do_test_mutex, 3); +} + + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test_framework::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: spin-mutex test suite"); + + test->add(BOOST_TEST_CASE(&test_mutex)); + + return test; +} diff --git a/libs/task/test/test_spin_unbounded_channel.cpp b/libs/task/test/test_spin_unbounded_channel.cpp new file mode 100644 index 00000000..1871a85f --- /dev/null +++ b/libs/task/test/test_spin_unbounded_channel.cpp @@ -0,0 +1,171 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +struct send_data +{ + tsk::spin::unbounded_channel< int > & buf; + + send_data( tsk::spin::unbounded_channel< int > & buf_) : + buf( buf_) + {} + + void operator()( int value) + { buf.put( value); } +}; + +struct recv_data +{ + tsk::spin::unbounded_channel< int > & buf; + int value; + + recv_data( tsk::spin::unbounded_channel< int > & buf_) : + buf( buf_), value( 0) + {} + + void operator()( ) + { + boost::optional< int > res; + if ( buf.take( res) ) + value = * res; + } +}; + +void test_case_1() +{ + tsk::spin::unbounded_channel< int > buf; + BOOST_CHECK_EQUAL( true, buf.empty() ); + BOOST_CHECK_EQUAL( true, buf.active() ); + int n = 1; + buf.put( n); + BOOST_CHECK_EQUAL( false, buf.empty() ); + boost::optional< int > res; + BOOST_CHECK_EQUAL( true, buf.take( res) ); + BOOST_CHECK( res); + BOOST_CHECK_EQUAL( n, res.get() ); + buf.deactivate(); + BOOST_CHECK_EQUAL( false, buf.active() ); + BOOST_CHECK_THROW( buf.put( 1), std::runtime_error); +} + +void test_case_2() +{ + tsk::spin::unbounded_channel< int > buf; + BOOST_CHECK_EQUAL( true, buf.empty() ); + BOOST_CHECK_EQUAL( true, buf.active() ); + int n = 1; + buf.put( n); + BOOST_CHECK_EQUAL( false, buf.empty() ); + boost::optional< int > res; + BOOST_CHECK_EQUAL( true, buf.try_take( res) ); + BOOST_CHECK( res); + BOOST_CHECK_EQUAL( n, res.get() ); + BOOST_CHECK_EQUAL( false, buf.try_take( res) ); + BOOST_CHECK_EQUAL( false, buf.take( res, pt::milliseconds( 10) ) ); +} + +void test_case_3() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 2) ); + + tsk::spin::unbounded_channel< int > buf; + + int n = 37; + + recv_data receiver( buf); + BOOST_CHECK_EQUAL( 0, receiver.value); + tsk::handle< void > h = + tsk::async( + tsk::make_task( + & recv_data::operator(), + boost::ref( receiver) ), + pool); + + boost::this_thread::sleep( + pt::milliseconds( 250) ); + + BOOST_CHECK_EQUAL( false, h.is_ready() ); + buf.put( n); + + h.wait(); + + BOOST_CHECK_EQUAL( n, receiver.value); +} + +void test_case_4() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 2) ); + + tsk::spin::unbounded_channel< int > buf; + + int n = 37; + + send_data sender( buf); + recv_data receiver( buf); + BOOST_CHECK_EQUAL( 0, receiver.value); + + tsk::handle< void > h1 = + tsk::async( + tsk::make_task( + & recv_data::operator(), + boost::ref( receiver) ), + pool); + + boost::this_thread::sleep( + pt::milliseconds( 250) ); + BOOST_CHECK_EQUAL( false, h1.is_ready() ); + + tsk::handle< void > h2 = + tsk::async( + tsk::make_task( + & send_data::operator(), + boost::ref( sender), + n), + pool); + + h2.wait(); + BOOST_CHECK_EQUAL( true, h2.is_ready() ); + h1.wait(); + BOOST_CHECK_EQUAL( true, h1.is_ready() ); + + BOOST_CHECK_EQUAL( n, receiver.value); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: unbounded-buffer test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + + return test; +} diff --git a/libs/task/test/test_task.cpp b/libs/task/test/test_task.cpp new file mode 100755 index 00000000..0d66a8c9 --- /dev/null +++ b/libs/task/test/test_task.cpp @@ -0,0 +1,99 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "test_functions.hpp" + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +void zero_args_fn() {} +int one_arg_fn( int i) { return i; } +int two_args_fn( int i, std::string const& s) { return i; } + +// check vaild task +void test_case_1() +{ + tsk::task< int > t1( fibonacci_fn, 10); + tsk::task< int > t2; + BOOST_CHECK( t1); + BOOST_CHECK( ! t2); +} + +// check make_task +void test_case_2() +{ + tsk::task< void > t1; + BOOST_CHECK( ! t1); + t1 = tsk::make_task( zero_args_fn); + BOOST_CHECK( t1); + tsk::task< int > t2 = tsk::make_task( one_arg_fn, 1); + BOOST_CHECK( t2); + tsk::task< int > t3; + BOOST_CHECK( ! t3); + t3 = tsk::make_task( two_args_fn, 1, "abc"); + BOOST_CHECK( t3); +} + +// check moved task +void test_case_3() +{ + tsk::task< int > t1( fibonacci_fn, 10); + BOOST_CHECK( t1); + tsk::task< int > t2( boost::move( t1) ); + BOOST_CHECK( ! t1); + BOOST_CHECK_THROW( t1(), tsk::task_moved); + BOOST_CHECK_NO_THROW( t2() ); +} + +// check execute twice +void test_case_4() +{ + tsk::task< int > t1( fibonacci_fn, 10); + BOOST_CHECK_NO_THROW( t1() ); + BOOST_CHECK_THROW( t1(), tsk::task_already_executed); +} + +// check swap +void test_case_5() +{ + tsk::task< int > t1( fibonacci_fn, 10); + tsk::task< int > t2; + BOOST_CHECK_NO_THROW( t1() ); + BOOST_CHECK_THROW( t2(), tsk::task_moved); + t1.swap( t2); + BOOST_CHECK_THROW( t1(), tsk::task_moved); + BOOST_CHECK_THROW( t2(), tsk::task_already_executed); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: task test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + test->add( BOOST_TEST_CASE( & test_case_5) ); + + return test; +} diff --git a/libs/task/test/test_tasklet.cpp b/libs/task/test/test_tasklet.cpp new file mode 100644 index 00000000..1ed98077 --- /dev/null +++ b/libs/task/test/test_tasklet.cpp @@ -0,0 +1,425 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "test_functions.hpp" + +namespace fi = boost::tasklets; +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +// check assignment +void test_case_1() +{ + fi::scheduler<> sched; + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h1; + tsk::handle< int > h2( + tsk::async( boost::move( t), sched) ); + h1 = h2; + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK_EQUAL( h1.get(), 55); + BOOST_CHECK_EQUAL( h2.get(), 55); +} + +// check swap +void test_case_2() +{ + fi::scheduler<> sched; + tsk::task< int > t1( fibonacci_fn, 5); + tsk::task< int > t2( fibonacci_fn, 10); + tsk::handle< int > h1( + tsk::async( boost::move( t1), sched) ); + tsk::handle< int > h2( + tsk::async( boost::move( t2), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK_EQUAL( h1.get(), 5); + BOOST_CHECK_EQUAL( h2.get(), 55); + BOOST_CHECK_NO_THROW( h1.swap( h2) ); + BOOST_CHECK_EQUAL( h1.get(), 55); + BOOST_CHECK_EQUAL( h2.get(), 5); +} + +// check runs not in pool +void test_case_3() +{ + fi::scheduler<> sched; + tsk::task< bool > t( runs_in_pool_fn); + tsk::handle< bool > h( + tsk::async( boost::move( t), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK_EQUAL( h.get(), false); +} + +// check runtime_error throw inside task +void test_case_4() +{ + fi::scheduler<> sched; + tsk::task< void > t( throwing_fn); + tsk::handle< void > h( + tsk::async( boost::move( t), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK_THROW( h.get(), std::runtime_error); +} + +// check task_uninitialized +void test_case_5() +{ + tsk::handle< int > h; + BOOST_CHECK_THROW( h.get(), tsk::task_uninitialized); + BOOST_CHECK_THROW( h.wait(), tsk::task_uninitialized); + BOOST_CHECK_THROW( h.wait_for( pt::seconds( 1) ), tsk::task_uninitialized); + BOOST_CHECK_THROW( + h.wait_until( boost::get_system_time() + pt::seconds( 1) ), + tsk::task_uninitialized); + BOOST_CHECK( ! h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait +void test_case_6() +{ + fi::scheduler<> sched; + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h( + tsk::async( boost::move( t), sched) ); + BOOST_CHECK( ! h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + h.wait(); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); + BOOST_CHECK_EQUAL( h.get(), 55); +} + +// check wait_for +void test_case_7() +{ + fi::scheduler<> sched; + tsk::task< void > t( delay_fn, pt::seconds( 1) ); + tsk::handle< void > h( + tsk::async( boost::move( t), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK( h.wait_for( pt::seconds( 2) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_for +void test_case_8() +{ + fi::scheduler<> sched; + tsk::task< void > t( delay_fn, pt::seconds( 2) ); + tsk::handle< void > h( + tsk::async( boost::move( t), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK( h.wait_for( pt::seconds( 1) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_for +void test_case_9() +{ + fi::scheduler<> sched; + tsk::task< void > t( delay_fn, pt::seconds( 1) ); + tsk::handle< void > h( + tsk::async( boost::move( t), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK( h.wait_until( boost::get_system_time() + pt::seconds( 3) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_for +void test_case_10() +{ + fi::scheduler<> sched; + tsk::task< void > t( delay_fn, pt::seconds( 2) ); + tsk::handle< void > h( + tsk::async( boost::move( t), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK( h.wait_until( boost::get_system_time() + pt::seconds( 1) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check interrupt +void test_case_11() +{ + fi::scheduler<> sched; + tsk::task< void > t( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h( + tsk::async( boost::move( t), sched) ); + h.interrupt(); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +// check interrupt_and_wait +void test_case_12() +{ + fi::scheduler<> sched; + bool finished( false); + tsk::task< void > t( + interrupt_fn, + pt::seconds( 3), + boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + h.interrupt_and_wait(); + BOOST_CHECK( ! finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +// check interrupt_and_wait_for +void test_case_13() +{ + fi::scheduler<> sched; + bool finished( false); + tsk::task< void > t( + interrupt_fn, + pt::seconds( 1), + boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK( h.interrupt_and_wait_for( pt::seconds( 2) ) ); + BOOST_CHECK( ! finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +// check interrupt_and_wait_for +void test_case_14() +{ + fi::scheduler<> sched; + tsk::task< void > t( non_interrupt_fn, 2); + tsk::handle< void > h( + tsk::async( boost::move( t), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK( h.interrupt_and_wait_for( pt::seconds( 1) ) ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +// check interrupt_and_wait_until +void test_case_15() +{ + fi::scheduler<> sched; + bool finished( false); + tsk::task< void > t( + interrupt_fn, + pt::seconds( 1), + boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK( h.interrupt_and_wait_until( boost::get_system_time() + pt::seconds( 2) ) ); + BOOST_CHECK( ! finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +// check interrupt_and_wait_until +void test_case_16() +{ + fi::scheduler<> sched; + tsk::task< void > t( non_interrupt_fn, 2); + tsk::handle< void > h( + tsk::async( boost::move( t), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK( h.interrupt_and_wait_until( boost::get_system_time() + pt::seconds( 1) ) ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +// check waitfor_all() +void test_case_17() +{ + fi::scheduler<> sched; + std::vector< tsk::handle< int > > vec; + for ( int i = 0; i <= 5; ++i) + { + tsk::task< int > t( fibonacci_fn, i); + vec.push_back( + tsk::async( boost::move( t), sched) ); + } + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + tsk::waitfor_all( vec.begin(), vec.end() ); + BOOST_CHECK( vec[0].is_ready() ); + BOOST_CHECK( vec[1].is_ready() ); + BOOST_CHECK( vec[2].is_ready() ); + BOOST_CHECK( vec[3].is_ready() ); + BOOST_CHECK( vec[4].is_ready() ); + BOOST_CHECK( vec[5].is_ready() ); + BOOST_CHECK_EQUAL( vec[0].get(), 0); + BOOST_CHECK_EQUAL( vec[1].get(), 1); + BOOST_CHECK_EQUAL( vec[2].get(), 1); + BOOST_CHECK_EQUAL( vec[3].get(), 2); + BOOST_CHECK_EQUAL( vec[4].get(), 3); + BOOST_CHECK_EQUAL( vec[5].get(), 5); +} +/* +// check waitfor_any() +void test_case_18() +{ + fi::scheduler<> sched; + tsk::task< void > t1( delay_fn, pt::seconds( 2) ); + tsk::task< void > t2( delay_fn, pt::seconds( 1) ); + tsk::handle< void > h1( + tsk::async( boost::move( t1), sched) ); + tsk::handle< void > h2( + tsk::async( boost::move( t2), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + tsk::waitfor_any( h1, h2); + BOOST_CHECK( h1.is_ready() ); + BOOST_CHECK( h2.is_ready() ); +} +*/ +// check interrupt + wait +void test_case_19() +{ + fi::scheduler<> sched; + tsk::task< void > t( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h( + tsk::async( boost::move( t), sched) ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + h.interrupt(); + BOOST_CHECK_NO_THROW( h.wait() ); + BOOST_CHECK_NO_THROW( h.get() ); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: tasklet test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + test->add( BOOST_TEST_CASE( & test_case_5) ); + test->add( BOOST_TEST_CASE( & test_case_6) ); + test->add( BOOST_TEST_CASE( & test_case_7) ); + test->add( BOOST_TEST_CASE( & test_case_8) ); + test->add( BOOST_TEST_CASE( & test_case_9) ); + test->add( BOOST_TEST_CASE( & test_case_10) ); + test->add( BOOST_TEST_CASE( & test_case_11) ); + test->add( BOOST_TEST_CASE( & test_case_12) ); + test->add( BOOST_TEST_CASE( & test_case_13) ); + test->add( BOOST_TEST_CASE( & test_case_14) ); + test->add( BOOST_TEST_CASE( & test_case_15) ); + test->add( BOOST_TEST_CASE( & test_case_16) ); + test->add( BOOST_TEST_CASE( & test_case_17) ); +// test->add( BOOST_TEST_CASE( & test_case_18) ); + test->add( BOOST_TEST_CASE( & test_case_19) ); + + return test; +} diff --git a/libs/task/test/test_unbounded_pool.cpp b/libs/task/test/test_unbounded_pool.cpp new file mode 100644 index 00000000..9e1c346e --- /dev/null +++ b/libs/task/test/test_unbounded_pool.cpp @@ -0,0 +1,440 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "test_functions.hpp" + +namespace pt = boost::posix_time; +namespace tsk = boost::tasks; + +// check size and move op +void test_case_1() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool1( tsk::poolsize( 3) ); + BOOST_CHECK( pool1); + BOOST_CHECK_EQUAL( pool1.size(), std::size_t( 3) ); + + tsk::static_pool< + tsk::unbounded_fifo + > pool2; + BOOST_CHECK( ! pool2); + BOOST_CHECK_THROW( pool2.size(), tsk::pool_moved); + + pool2 = boost::move( pool1); + + BOOST_CHECK( ! pool1); + BOOST_CHECK_THROW( pool1.size(), tsk::pool_moved); + + BOOST_CHECK( pool2); + BOOST_CHECK_EQUAL( pool2.size(), std::size_t( 3) ); + + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h( + tsk::async( boost::move( t), pool2) ); + BOOST_CHECK_EQUAL( h.get(), 55); +} + +// check submit +void test_case_2() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK_EQUAL( h.get(), 55); +} + +// check assignment +void test_case_3() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h1; + tsk::handle< int > h2( + tsk::async( boost::move( t), pool) ); + h1 = h2; + BOOST_CHECK_EQUAL( h1.get(), 55); + BOOST_CHECK_EQUAL( h2.get(), 55); +} + +// check swap +void test_case_4() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + tsk::task< int > t1( fibonacci_fn, 5); + tsk::task< int > t2( fibonacci_fn, 10); + tsk::handle< int > h1( + tsk::async( boost::move( t1), pool) ); + tsk::handle< int > h2( + tsk::async( boost::move( t2), pool) ); + BOOST_CHECK_EQUAL( h1.get(), 5); + BOOST_CHECK_EQUAL( h2.get(), 55); + BOOST_CHECK_NO_THROW( h1.swap( h2) ); + BOOST_CHECK_EQUAL( h1.get(), 55); + BOOST_CHECK_EQUAL( h2.get(), 5); +} + +// check runs in pool +void test_case_5() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 1) ); + tsk::task< bool > t( runs_in_pool_fn); + tsk::handle< bool > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK_EQUAL( h.get(), true); +} + +// check shutdown +void test_case_6() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 1) ); + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h( + tsk::async( boost::move( t), pool) ); + pool.shutdown(); + BOOST_CHECK( pool.closed() ); + BOOST_CHECK_EQUAL( h.get(), 55); +} + +// check runtime_error throw inside task +void test_case_7() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 1) ); + tsk::task< void > t( throwing_fn); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + pool.shutdown(); + BOOST_CHECK_THROW( h.get(), std::runtime_error); +} + +// check shutdown with task_rejected exception +void test_case_8() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 1) ); + tsk::task< int > t( fibonacci_fn, 10); + pool.shutdown(); + BOOST_CHECK( pool.closed() ); + BOOST_CHECK_THROW( + tsk::async( boost::move( t), pool), + tsk::task_rejected); +} + +// check shutdown_now with thread_interrupted exception +void test_case_9() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 1) ); + tsk::task< void > t( delay_fn, pt::millisec( 500) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + boost::this_thread::sleep( pt::millisec( 250) ); + BOOST_CHECK_EQUAL( pool.size(), std::size_t( 1) ); + pool.shutdown_now(); + BOOST_CHECK( pool.closed() ); + BOOST_CHECK_EQUAL( pool.size(), std::size_t( 0) ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check wait +void test_case_10() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + tsk::task< int > t( fibonacci_fn, 10); + tsk::handle< int > h( + tsk::async( boost::move( t), pool) ); + h.wait(); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); + BOOST_CHECK_EQUAL( h.get(), 55); +} + +// check wait_for +void test_case_11() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + tsk::task< void > t( delay_fn, pt::seconds( 1) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( h.wait_for( pt::seconds( 3) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_for +void test_case_12() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + tsk::task< void > t( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( ! h.wait_for( pt::seconds( 1) ) ); + BOOST_CHECK( ! h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_until +void test_case_13() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + tsk::task< void > t( delay_fn, pt::seconds( 1) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( h.wait_until( boost::get_system_time() + pt::seconds( 3) ) ); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check wait_until +void test_case_14() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + tsk::task< void > t( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( ! h.wait_until( boost::get_system_time() + pt::seconds( 1) ) ); + BOOST_CHECK( ! h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( ! h.has_exception() ); +} + +// check interrupt +void test_case_15() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + tsk::task< void > t( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + h.interrupt(); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check interrupt_all_worker +void test_case_16() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 5) ); + tsk::task< void > t1( delay_fn, pt::seconds( 3) ); + tsk::task< void > t2( delay_fn, pt::seconds( 3) ); + tsk::task< void > t3( delay_fn, pt::seconds( 3) ); + tsk::handle< void > h1( + tsk::async( boost::move( t1), pool) ); + tsk::handle< void > h2( + tsk::async( boost::move( t2), pool) ); + tsk::handle< void > h3( + tsk::async( boost::move( t3), pool) ); + boost::this_thread::sleep( pt::millisec( 250) ); + pool.interrupt_all_worker(); + BOOST_CHECK( ! h1.interruption_requested() ); + BOOST_CHECK( ! h2.interruption_requested() ); + BOOST_CHECK( ! h3.interruption_requested() ); + BOOST_CHECK_THROW( h1.get(), tsk::task_interrupted); + BOOST_CHECK_THROW( h2.get(), tsk::task_interrupted); + BOOST_CHECK_THROW( h3.get(), tsk::task_interrupted); + BOOST_CHECK_EQUAL( pool.size(), std::size_t( 5) ); +} + +// check interrupt_and_wait +void test_case_17() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + bool finished( false); + tsk::task< void > t( + interrupt_fn, + pt::seconds( 1), + boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + h.interrupt_and_wait(); + BOOST_CHECK( finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( h.has_exception() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check interrupt_and_wait_for +void test_case_18() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + bool finished( false); + tsk::task< void > t( + interrupt_fn, + pt::seconds( 1), + boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( h.interrupt_and_wait_for( pt::seconds( 3) ) ); + BOOST_CHECK( finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( h.has_exception() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check interrupt_and_wait_for +void test_case_19() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + tsk::task< void > t( non_interrupt_fn, 3); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( ! h.interrupt_and_wait_for( pt::seconds( 1) ) ); +} + +// check interrupt_and_wait_until +void test_case_20() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + bool finished( false); + tsk::task< void > t( + interrupt_fn, + pt::seconds( 1), + boost::ref( finished) ); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( h.interrupt_and_wait_until( boost::get_system_time() + pt::seconds( 3) ) ); + BOOST_CHECK( finished); + BOOST_CHECK( h.is_ready() ); + BOOST_CHECK( ! h.has_value() ); + BOOST_CHECK( h.has_exception() ); + BOOST_CHECK( h.interruption_requested() ); + BOOST_CHECK_THROW( h.get(), tsk::task_interrupted); +} + +// check interrupt_and_wait_until +void test_case_21() +{ + tsk::static_pool< + tsk::unbounded_fifo + > pool( tsk::poolsize( 3) ); + tsk::task< void > t( non_interrupt_fn, 3); + tsk::handle< void > h( + tsk::async( boost::move( t), pool) ); + BOOST_CHECK( ! h.interrupt_and_wait_until( boost::get_system_time() + pt::seconds( 1) ) ); +} + +// check fifo scheduling +void test_case_22() +{ + typedef tsk::static_pool< + tsk::unbounded_fifo + > pool_type; + BOOST_CHECK( ! tsk::has_attribute< pool_type >::value); + pool_type pool( tsk::poolsize( 1) ); + boost::barrier b( 2); + std::vector< int > buffer; + tsk::task< void > t1( barrier_fn, boost::ref( b) ); + tsk::task< void > t2( + buffer_fibonacci_fn, + boost::ref( buffer), + 10); + tsk::task< void > t3( + buffer_fibonacci_fn, + boost::ref( buffer), + 0); + tsk::async( boost::move( t1), pool); + boost::this_thread::sleep( pt::millisec( 250) ); + tsk::async( boost::move( t2), pool); + tsk::async( boost::move( t3), pool); + b.wait(); + pool.shutdown(); + BOOST_CHECK_EQUAL( buffer[0], 55); + BOOST_CHECK_EQUAL( buffer[1], 0); + BOOST_CHECK_EQUAL( buffer.size(), std::size_t( 2) ); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: unbounded-pool test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + test->add( BOOST_TEST_CASE( & test_case_5) ); + test->add( BOOST_TEST_CASE( & test_case_6) ); + test->add( BOOST_TEST_CASE( & test_case_7) ); + test->add( BOOST_TEST_CASE( & test_case_8) ); + test->add( BOOST_TEST_CASE( & test_case_9) ); + test->add( BOOST_TEST_CASE( & test_case_10) ); + test->add( BOOST_TEST_CASE( & test_case_11) ); + test->add( BOOST_TEST_CASE( & test_case_12) ); + test->add( BOOST_TEST_CASE( & test_case_13) ); + test->add( BOOST_TEST_CASE( & test_case_14) ); + test->add( BOOST_TEST_CASE( & test_case_15) ); + test->add( BOOST_TEST_CASE( & test_case_16) ); + test->add( BOOST_TEST_CASE( & test_case_17) ); + test->add( BOOST_TEST_CASE( & test_case_18) ); + test->add( BOOST_TEST_CASE( & test_case_19) ); + test->add( BOOST_TEST_CASE( & test_case_20) ); + test->add( BOOST_TEST_CASE( & test_case_21) ); + test->add( BOOST_TEST_CASE( & test_case_22) ); + + return test; +} diff --git a/libs/task/test/util.ipp b/libs/task/test/util.ipp new file mode 100644 index 00000000..7bcf96d9 --- /dev/null +++ b/libs/task/test/util.ipp @@ -0,0 +1,159 @@ +// Copyright (C) 2001-2003 +// William E. Kempf +// Copyright (C) 2007-8 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#if !defined(UTIL_INL_WEK01242003) +#define UTIL_INL_WEK01242003 + +#include + +#include +#include +#include + +#ifndef DEFAULT_EXECUTION_MONITOR_TYPE +# define DEFAULT_EXECUTION_MONITOR_TYPE execution_monitor::use_condition +#endif + +// boostinspect:nounnamed +// + +namespace pt = boost::posix_time; + +namespace { + +class execution_monitor +{ +public: + enum wait_type { use_sleep_only, use_mutex, use_condition }; + + execution_monitor(wait_type type, int secs) + : done(false), type(type), secs(secs) { } + void start() + { + if (type != use_sleep_only) { + boost::mutex::scoped_lock lock(mutex); done = false; + } else { + done = false; + } + } + void finish() + { + if (type != use_sleep_only) { + boost::mutex::scoped_lock lock(mutex); + done = true; + if (type == use_condition) + cond.notify_one(); + } else { + done = true; + } + } + bool wait() + { + pt::time_duration xt = pt::seconds(secs); + if (type != use_condition) + boost::this_thread::sleep(xt); + if (type != use_sleep_only) { + boost::mutex::scoped_lock lock(mutex); + while (type == use_condition && !done) { + if (!cond.timed_wait(lock, xt)) + break; + } + return done; + } + return done; + } + +private: + boost::mutex mutex; + boost::condition cond; + bool done; + wait_type type; + int secs; +}; + +template +class indirect_adapter +{ +public: + indirect_adapter(F func, execution_monitor& monitor) + : func(func), monitor(monitor) { } + void operator()() const + { + try + { + boost::thread thrd(func); + thrd.join(); + } + catch (...) + { + monitor.finish(); + throw; + } + monitor.finish(); + } + +private: + F func; + execution_monitor& monitor; + void operator=(indirect_adapter&); +}; + +template +void timed_test(F func, int secs, + execution_monitor::wait_type type=DEFAULT_EXECUTION_MONITOR_TYPE) +{ + execution_monitor monitor(type, secs); + indirect_adapter ifunc(func, monitor); + monitor.start(); + boost::thread thrd(ifunc); + BOOST_REQUIRE_MESSAGE(monitor.wait(), + "Timed test didn't complete in time, possible deadlock."); +} + +template +class thread_binder +{ +public: + thread_binder(const F& func, const T& param) + : func(func), param(param) { } + void operator()() const { func(param); } + +private: + F func; + T param; +}; + +template +thread_binder bind(const F& func, const T& param) +{ + return thread_binder(func, param); +} + +template +class thread_member_binder +{ +public: + thread_member_binder(R (T::*func)(), T& param) + : func(func), param(param) { } + void operator()() const { (param.*func)(); } + +private: + void operator=(thread_member_binder&); + + R (T::*func)(); + T& param; +}; + + +template +thread_member_binder bind(R (T::*func)(), T& param) +{ + return thread_member_binder(func, param); +} +} // namespace + +#endif diff --git a/libs/tasklet/build/Jamfile.v2 b/libs/tasklet/build/Jamfile.v2 new file mode 100644 index 00000000..3b51a78f --- /dev/null +++ b/libs/tasklet/build/Jamfile.v2 @@ -0,0 +1,39 @@ +# Boost.tasklet Library Build Jamfile + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import feature ; +import modules ; +import toolset ; + +project boost/tasklet + : source-location + ../src + : usage-requirements + shared:BOOST_TASKLET_DYN_LINK=1 + ; + +lib boost_tasklet + : auto_reset_event.cpp + barrier.cpp + condition.cpp + count_down_event.cpp + detail/tasklet_base.cpp + tasklet.cpp + manual_reset_event.cpp + mutex.cpp + round_robin.cpp + spin_condition.cpp + spin_mutex.cpp + strategy.cpp + ../../fiber/build//boost_fiber + ../../thread/build//boost_thread + : shared:BOOST_TASKLET_DYN_LINK=1 + : + : shared:../../fiber/build//boost_fiber + ; + +boost-install boost_tasklet ; diff --git a/libs/tasklet/doc/Jamfile.v2 b/libs/tasklet/doc/Jamfile.v2 new file mode 100644 index 00000000..d6039250 --- /dev/null +++ b/libs/tasklet/doc/Jamfile.v2 @@ -0,0 +1,33 @@ +# (C) Copyright 2008 Anthony Williams +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +path-constant boost-images : ../../../doc/src/images ; + +xml tasklet : tasklet.qbk ; + +boostbook standalone + : + tasklet + : + # HTML options first: + # Use graphics not text for navigation: + navig.graphics=1 + # How far down we chunk nested sections, basically all of them: + chunk.section.depth=3 + # Don't put the first section on the same page as the TOC: + chunk.first.sections=1 + # How far down sections get TOC's + toc.section.depth=10 + # Max depth in each TOC: + toc.max.depth=3 + # How far down we go with TOC's + generate.section.toc.level=10 + # Path for links to Boost: + boost.root=../../../.. + # Path for libraries index: + boost.libraries=../../../../libs/libraries.htm + # Use the main Boost stylesheet: + html.stylesheet=../../../../doc/html/boostbook.css + ; diff --git a/libs/tasklet/doc/barrier.qbk b/libs/tasklet/doc/barrier.qbk new file mode 100644 index 00000000..457d6840 --- /dev/null +++ b/libs/tasklet/doc/barrier.qbk @@ -0,0 +1,52 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] + +[section:barriers Barriers] + +A barrier is a concept also known as a __rendezvous__, it is a synchronization +point between multiple contexts of execution (thread or tasklet). The barrier is +configured for a particular number of tasklets (`n`), and as tasklets reach the +barrier they must wait until all `n` tasklets have arrived. Once the `n`-th +tasklet has reached the barrier, all the waiting tasklets can proceed, and the +barrier is reset. + +[section:barrier Class `barrier`] + + #include + + class barrier + { + public: + barrier( unsigned int initial); + + bool wait(); + }; + + +Instances of __barrier__ are not copyable or movable. + +[section:constructor `barrier( unsigned int initial)`] +[variablelist +[[Effects:] [Construct a barrier for `initial` tasklets.]] +[[Throws:] [__invalid_argument__]] +] +[endsect] + +[section:wait `bool wait()`] +[variablelist +[[Effects:] [Block until `initial` tasklets have called `wait` on `*this`. When +the `initial`-th tasklet calls `wait`, all waiting tasklets are unblocked, and +the barrier is reset. ]] +[[Returns:] [`true` for exactly one tasklet from each batch of waiting tasklets, +`false` otherwise.]] +[[Throws:] [__tasklet_error__]] +] +[endsect] + +[endsect] + +[endsect] diff --git a/libs/tasklet/doc/channel.qbk b/libs/tasklet/doc/channel.qbk new file mode 100644 index 00000000..a3e5ca33 --- /dev/null +++ b/libs/tasklet/doc/channel.qbk @@ -0,0 +1,193 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + +[section:channels (Un)Bounded channels] + +__boost_tasklet__ provides a bounded and a unbounded channel suitable to +synchonize tasklets via message passing. + + typedef boost::tasklets::unbounded_channel< int > channel_t; + + void send( channel_t & channel) + { + for ( int i = 0; i < 5; ++i) + channel.put( i); + channel.deactivate(); + } + + void recv( channel_t & channel) + { + boost::optional< int > value; + while ( channel.take( value) ) + { std::cout << "received " << * value << std::endl; } + } + + boost::tasklets::scheduler<> sched; + channel_t channel; + sched.make_tasklet( send, ref( channel) ); + sched.make_tasklet( recv, ref( channel) ); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + +[section:unbounded_channel Template `template< typename T > unbounded_channel`] + + #include + + template< typename T > + class unbounded_channel : private noncopyable + { + public: + void deactivate(); + + bool empty(); + + void put( T const& t); + + bool take( boost::optional< T > & va); + + bool try_take( boost::optional< T > & va); + }; + +[section:deactivate `void deactivate()`] +[variablelist +[[Effects:] [Deactivates the channel. No values can be put after calling +`this->deactivate`. Tasklets blocked in +`this->take()` will be return.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:empty `bool empty()`] +[variablelist +[[Effects:] [Returns `true` if the channel currently contains no data.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:put `void put( T const& t)`] +[variablelist +[[Effects:] [Enqueues the value in the channel and wakes up a tasklet waiting +for new data available from the +channel.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:take `bool take( boost::optional< T > & va)`] +[variablelist +[[Effects:] [Dequeues a value from the channel. If no data is available from the +channel the tasklet gets suspended until new data are enqueued (return value +`true` and va contains dequeued value) or the channel gets deactiveted and the +function returns `false`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:try_take `bool try_take( boost::optional< T > & va)`] +[variablelist +[[Effects:] [Dequeues a value from the channel. If no data is available from the +channel the function returns `false`. Otherwise it returns `true` and `va` +contains the dequed value.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[endsect] + + +[section:bounded_channel Template `template< typename T > bounded_channel`] + + #include + + template< typename T > + class bounded_channel : private noncopyable + { + public: + bounded_channel( std::size_t wm); + + bounded_channel( std::size_t hwm, std::size_t lwm); + + void deactivate(); + + bool empty(); + + void put( T const& t); + + bool take( boost::optional< T > & va); + + bool try_take( boost::optional< T > & va); + }; + +[section:constructor `bounded_channel( std::size_t wm)`] +[variablelist +[[Effects:] [Constructs an object of class `bounded_channel` which will contain +a maximum of `wm` items.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:constructor2 `bounded_channel( std::size_t hwm, std::size_t lwm)`] +[variablelist +[[Effects:] [Constructs an object of class `bounded_channel` which will contain +a high-watermark of `hwm` +and a low-watermark of `lwm` items.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:deactivate `void deactivate()`] +[variablelist +[[Effects:] [Deactivates the channel. No values can be put after calling +`this->deactivate`. Tasklets blocked in `this->take()` will be return.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:empty `bool empty()`] +[variablelist +[[Effects:] [Returns `true` if the channel currently contains no data.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:put `void put( T const& t)`] +[variablelist +[[Effects:] [Enqueues the value in the channel and wakes up a tasklet waiting +for new data available from the channel. If the watermark has reached the +tasklet putting the value will be supended until at least one item was +dequeued.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:take `bool take( boost::optional< T > & va)`] +[variablelist +[[Effects:] [Dequeues a value from the channel. If no data is available from the +channel the tasklet gets suspended until new data are enqueued (return value +`true` and va contains dequeued value) or the channel gets deactiveted and the +function returns `false`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:try_take `bool try_take( boost::optional< T > & va)`] +[variablelist +[[Effects:] [Dequeues a value from the channel. If no data is available from the +channel the function returns `false`. Otherwise it returns `true` and `va` +contains the dequed value.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[endsect] + +[endsect] diff --git a/libs/tasklet/doc/condition_variables.qbk b/libs/tasklet/doc/condition_variables.qbk new file mode 100644 index 00000000..f3007a7f --- /dev/null +++ b/libs/tasklet/doc/condition_variables.qbk @@ -0,0 +1,267 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] + +[section:conditions Condition Variables] + +[heading Synopsis] + +The class `condition` provides a mechanism for one tasklet to wait for +notification `condition`. When the tasklet is woken from the wait, then it +checks to see if the appropriate condition is now true, and continues if so. If +the condition is not true, then the tasklet then calls `wait` again to resume +waiting. In the simplest case, this condition is just a boolean variable: + + boost::tasklets::condition cond; + boost::tasklets::mutex mtx; + bool data_ready; + + void process_data(); + + void wait_for_data_to_process() + { + boost::unique_lock< boost::tasklets::mutex > lk( mtx); + while ( ! data_ready) + { + cond.wait( lk); + } + process_data(); + } + +Notice that the `lk` is passed to `wait`: `wait` will atomically add the tasklet +to the set of tasklets waiting on the condition variable, and unlock the mutex. +When the tasklet is woken, the mutex will be locked again before the call to +`wait` returns. This allows other tasklets to acquire the mutex in order to +update the shared data, and ensures that the data associated with the condition +is correctly synchronized. + +In the mean time, another tasklet sets the condition to `true`, and then calls +either `notify_one` or `notify_all` on the condition variable to wake one +waiting tasklet or all the waiting tasklets respectively. + + void retrieve_data(); + void prepare_data(); + + void prepare_data_for_processing() + { + retrieve_data(); + prepare_data(); + { + boost::lock_guard< boost::tasklets::mutex > lk( mtx); + data_ready = true; + } + cond.notify_one(); + } + +Note that the same mutex is locked before the shared data is updated, but that +the mutex does not have to be locked across the call to `notify_one`. + +[section:condition Class `condition`] + + #include + + class condition + { + public: + void notify_one(); + void notify_all(); + + void wait( boost::unique_lock< boost::tasklets::mutex > & lk); + + template< typename Pred > + void wait( boost::unique_lock< boost::tasklets::mutex > & lk, Pred pred); + + template< typename LockType > + void wait( LockType & lk); + + template< typename LockType, typename Pred > + void wait( LockType & lk, Pred predicate); + }; + +[section:notify_one `void notify_one()`] +[variablelist +[[Effects:] [If any tasklets are currently __blocked__ waiting on `*this` in a +call to `wait`, unblocks one of those tasklets.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:notify_all `void notify_all()`] +[variablelist +[[Effects:] [If any tasklets are currently __blocked__ waiting on `*this` in a +call to `wait`, unblocks all of those tasklets.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:wait `void wait( boost::unique_lock< boost::tasklets::mutex > & lk)`] +[variablelist +[[Precondition:] [`lk` is locked by the current tasklet, and either no other +tasklet is currently waiting on `*this`, or the execution of the `mutex()` +member function on the `lk` objects supplied in the calls to `wait` in all the +tasklets currently waiting on `*this` would return the same value as +`lk->mutex()` for this call to `wait`.]] +[[Effects:] [Atomically call `lk.unlock()` and blocks the current tasklet. The +tasklet will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, or spuriously. When the tasklet is unblocked (for whatever +reason), the lock is reacquired by invoking `lk.lock()` before the call to +`wait` returns. The lock is also reacquired by invoking `lk.lock()` if the +function exits with an exception.]] +[[Postcondition:] [`lk` is locked by the current tasklet.]] +[[Throws:] [__tasklet_error__ if an error +occurs. __tasklet_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __tasklet__ object associated with the current tasklet of +execution.]] +] +[endsect] + +[section:wait_predicate `template< typename Pred > void wait( boost::unique_lock< boost::tasklets::mutex > & lk, Pred pred)`] +[variablelist +[[Effects:] [As-if `` +while ( ! pred()) +{ + wait( lk); +} +``]] + +] +[endsect] + +[section:wait_t `template< typename LockType > void wait( LockType & lk)`] +[variablelist +[[Effects:] [Atomically call `lk.unlock()` and blocks the current tasklet. The +tasklet will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, or spuriously. When the tasklet is unblocked (for whatever +reason), the lock is reacquired by invoking `lk.lock()` before the call to +`wait` returns. The lock is also reacquired by invoking `lk.lock()` if the +function exits with an exception.]] +[[Postcondition:] [`lk` is locked by the current tasklet.]] +[[Throws:] [__tasklet_error__ if an error +occurs. __tasklet_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __tasklet__ object associated with the current tasklet of +execution.]] +] +[endsect] + +[section:wait_predicate_t `template< typename LockType, typename Pred > void wait( LockType & lk, Pred pred)`] +[variablelist +[[Effects:] [As-if `` +while ( ! pred()) +{ + wait( lock); +} +``]] + +] +[endsect] + +[endsect] + + +[section:spin_condition Class `spin_condition`] + + #include + + class spin_condition + { + public: + void notify_one(); + void notify_all(); + + void wait( boost::unique_lock< boost::tasklets::spin_mutex > & lk); + + template< typename Pred > + void wait( boost::unique_lock< boost::tasklets::spin_mutex > & lk, Pred pred); + + template< typename LockType > + void wait( LockType & lk); + + template< typename LockType, typename Pred > + void wait( LockType & lk, Pred predicate); + }; + +[section:notify_one `void notify_one()`] +[variablelist +[[Effects:] [If any tasklets are currently __blocked__ waiting on `*this` in a +call to `wait`, unblocks one of +those tasklets.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:notify_all `void notify_all()`] +[variablelist +[[Effects:] [If any tasklets are currently __blocked__ waiting on `*this` in a +call to `wait`, unblocks all of +those tasklets.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:wait `void wait( boost::unique_lock< boost::tasklets::spin_mutex > & lk)`] +[variablelist +[[Precondition:] [`lk` is locked by the current tasklet, and either no other +tasklet is currently waiting on `*this`, or the execution of the `spin_mutex()` +member function on the `lk` objects supplied in the calls to `wait` in all the +tasklets currently waiting on `*this` would return the same value as +`lk->mutex()` for this call to `wait`.]] +[[Effects:] [Atomically call `lk.unlock()` and blocks the current tasklet. The +tasklet will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, or spuriously. When the tasklet is unblocked (for whatever +reason), the lock is reacquired by invoking `lk.lock()` before the call to +`wait` returns. The lock is also reacquired by invoking `lk.lock()` if the +function exits with an exception.]] +[[Postcondition:] [`lk` is locked by the current tasklet.]] +[[Throws:] [__tasklet_error__ if an error +occurs. __tasklet_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __tasklet__ object associated with the current tasklet of +execution.]] +] +[endsect] + +[section:wait_predicate `template< typename Pred > void wait( boost::unique_lock< boost::tasklets::spin_mutex > & lk, Pred pred)`] +[variablelist +[[Effects:] [As-if `` +while ( ! pred()) +{ + wait( lk); +} +``]] + +] +[endsect] + +[section:wait_t `template< typename LockType > void wait( LockType & lk)`] +[variablelist +[[Effects:] [Atomically call `lk.unlock()` and blocks the current tasklet. The +tasklet will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, or spuriously. When the tasklet is unblocked (for whatever +reason), the lock is reacquired by invoking `lk.lock()` before the call to +`wait` returns. The lock is also reacquired by invoking `lk.lock()` if the +function exits with an exception.]] +[[Postcondition:] [`lk` is locked by the current tasklet.]] +[[Throws:] [__tasklet_error__ if an error +occurs. __tasklet_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __tasklet__ object associated with the current tasklet of +execution.]] +] +[endsect] + +[section:wait_predicate_t `template< typename LockType, typename Pred > void wait( LockType & lk, Pred pred)`] +[variablelist +[[Effects:] [As-if `` +while ( ! pred()) +{ + wait( lock); +} +``]] + +] +[endsect] + +[endsect] + +[endsect] diff --git a/libs/tasklet/doc/event_variables.qbk b/libs/tasklet/doc/event_variables.qbk new file mode 100644 index 00000000..216b8bb2 --- /dev/null +++ b/libs/tasklet/doc/event_variables.qbk @@ -0,0 +1,283 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] + +[section:eventvar_ref Event Variables] + +[heading Synopsis] + +__boost_tasklet__ provides event variables to facilitate coordination between +tasklets. A event-variable has two states `set` (`signaled`) or `reset` +(`nonsignaled`). + + boost::tasklets::auto_reset_event ev; + + void process_data(); + + void wait_for_data_to_process() + { + ev.wait(); + process_data(); + } + +`wait` will atomically add the tasklet to the set of tasklets waiting on the +event variable. When the tasklet is woken, the event variable will be reset +again. + +In the mean time, another tasklet signals the event variable by calling `set` +on the event variable to wake one waiting tasklet. + + void retrieve_data(); + void prepare_data(); + + void prepare_data_for_processing() + { + retrieve_data(); + prepare_data(); + ev.set(); + } + + +[section:auto_reset_event Class `auto_reset_event`] + +[heading Synopsis] + +When the ['auto_reset_event] gets signaled, any one tasklet will see this +particular signal. When a tasklet observes the signal by waiting on the event, +it is automatically transitioned back to non-signaled state. Any tasklets can +subsequently set the event. + + #include + + class auto_reset_event : private boost::noncopyable + { + public: + auto_reset_event( bool isset = false); + + ~auto_reset_event(); + + void set(); + + void wait(); + + bool try_wait(); + }; + +[section:constructor `auto_reset_event( bool isset = false)`] +[variablelist +[[Effects:] [Constructs an object of class `auto_reset_event`. If isset is +`true` the variable is set.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:destructor `~auto_reset_event()`] +[variablelist +[[Precondition:] [All tasklets waiting on `*this` have been notified by a call +to `set` (though the respective calls to `wait` need not have returned).]] +[[Effects:] [Destroys the object.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:set `void set()`] +[variablelist +[[Effects:] [If any tasklets are currently __blocked__ waiting on `*this` in a +call to `wait`, unblocks one of those tasklets.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:wait `void wait()`] +[variablelist +[[Effects:] [Blocks the current tasklet. The tasklet will unblock when notified +by a call to `this->set()`. When the tasklet is unblocked, the variable is reset +before `wait` returns.]] +[[Throws:] [__tasklet_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __tasklet__ object associated with the current tasklet of +execution.]] +] +[endsect] + +[section:try_wait `bool try_wait()`] +[variablelist +[[Effects:] [Returns `true` if the event variable is set otherwise `false`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[endsect] + + +[section:manual_reset_event Class `manual_reset_event`] + +[heading Synopsis] + +The ['manual_reset_event] remains signaled until it is manually reset. Multiple +tasklets wait on the same event and observe the same signal. + + #include + + class manual_reset_event : private boost::noncopyable + { + public: + manual_reset_event( bool isset = false); + + ~manual_reset_event(); + + void set(); + + void reset(); + + void wait(); + + bool try_wait(); + }; + +[section:constructor `manual_reset_event( bool isset = false)`] +[variablelist +[[Effects:] [Constructs an object of class `manual_reset_event`. If isset is +`true` the variable is set.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:destructor `~manual_reset_event()`] +[variablelist +[[Precondition:] [All tasklets waiting on `*this` have been notified by a call +to `set` (though the respective calls to `wait` need not have returned).]] +[[Effects:] [Destroys the object.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:set `void set()`] +[variablelist +[[Effects:] [If any tasklets are currently __blocked__ waiting on `*this` in a +call to `wait`, unblocks those tasklets. The variable remains signaled until +`this->reset()` gets called.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:reset `void reset()`] +[variablelist +[[Effects:] [The event variable gets nonsignaled and tasklets calling +`this->wait()` will block.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:wait `void wait()`] +[variablelist +[[Effects:] [Blocks the current tasklet. The tasklet will unblock when notified +by a call to `this->set()`. When the tasklet is unblocked, the variable remains +set.]] +[[Throws:] [__tasklet_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __tasklet__ object associated with the current tasklet of +execution.]] +] +[endsect] + +[section:trywait `boo try_wait()`] +[variablelist +[[Effects:] [Returns `true` if the event variable is set otherwise `false`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[endsect] + + +[section:count_down_event Class `count_down_event`] + +[heading Synopsis] + +The ['count_down_event] decrements an internal counter (set in the constructor) +and all waiting tasklets are blocked until the count reaches zero. + + #include + + class count_down_event : private boost::noncopyable + { + public: + count_down_event( unsigned int initial); + + ~count_down_event(); + + unsigned int initial() const; + + unsigned int current() const; + + bool is_set() const; + + void set(); + + void wait(); + }; + +[section:constructor `count_down_event( unsigned int initial)`] +[variablelist +[[Effects:] [Constructs an object of class `count_down_event` with initial +value.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:destructor `~count_down_event()`] +[variablelist +[[Precondition:] [All tasklets waiting on `*this` have been notified by a call +to `set` (though the respective calls to `wait` need not have returned).]] +[[Effects:] [Destroys the object.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:initial `unsigned int initial()`] +[variablelist +[[Effects:] [Returns the initial value the event variable was initialized +with.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:current `unsigned int current()`] +[variablelist +[[Effects:] [Returns the value the variable currently holds.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:is_set `bool is_set()`] +[variablelist +[[Effects:] [Returns `true` if the varaible has reached zero.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:set `void set()`] +[variablelist +[[Effects:] [Decrements the current count. If the count reaches zero and any +tasklets are currently __blocked__ waiting on `*this` in a call to `wait`, +unblocks those tasklets. The variable remains signaled.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:wait `void wait()`] +[variablelist +[[Effects:] [Blocks the current tasklet. The tasklet will unblock when notified +by a call to `this->set()` and the count of the event variable reaches zero. +When the tasklet is unblocked, the variable remains set.]] +[[Throws:] [__tasklet_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __tasklet__ object associated with the current tasklet of +execution.]] +] +[endsect] + +[endsect] + +[endsect] diff --git a/libs/tasklet/doc/mutexes.qbk b/libs/tasklet/doc/mutexes.qbk new file mode 100644 index 00000000..8e75a337 --- /dev/null +++ b/libs/tasklet/doc/mutexes.qbk @@ -0,0 +1,67 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] + +[section:mutex_types Mutex Types] + +[section:mutex Class `mutex`] + + #include + + class mutex : private boost::noncopyable + { + public: + mutex(); + + ~mutex(); + + void lock(); + bool try_lock(); + void unlock(); + + typedef unique_lock< mutex > scoped_lock; + }; + +__mutex__ implements the __lockable_concept__ to provide an exclusive-ownership +mutex. At most one tasklet can own the lock on a given instance of __mutex__ at +any time. Multiple concurrent calls to __lock__, __try_lock__ and __unlock__ +shall be permitted. + +The tasklets blocked in __lock__ are suspended in the scheduler until get +signaled. + +[endsect] + +[section:spin_mutex Class `spin_mutex`] + + #include + + class spin_mutex : private boost::noncopyable + { + public: + spin_mutex(); + + ~spin_mutex(); + + void lock(); + bool try_lock(); + void unlock(); + + typedef unique_lock< spin_mutex > scoped_lock; + }; + +__spin_mutex__ implements the __lockable_concept__ to provide an +exclusive-ownership mutex. At most one tasklet can own the lock on a given +instance of __mutex__ at any time. Multiple concurrent calls to __lock__, +__try_lock__ and __unlock__ shall be permitted. + +The tasklets and threads blocked in __lock__ are spin waiting until the lock get +released, e.g. try to get an calling __tasklet_yield__/__thread_yield__. + + +[endsect] + +[endsect] diff --git a/libs/tasklet/doc/overview.qbk b/libs/tasklet/doc/overview.qbk new file mode 100644 index 00000000..edd7db41 --- /dev/null +++ b/libs/tasklet/doc/overview.qbk @@ -0,0 +1,54 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + +[section:overview Overview] + +__boost_tasklet__ executes micro-tasks (tasklets) in a thread and assigns each +small task with a micro-thread/fiber - so tasklets are cooperativly scheduled. +The API contains classes and functions to manage and synchronize tasklets (even +if the tasklets are running on different threads). + +The fiber associated with the tasklet is able to store the current execution +state, including all registers and CPU flags, the instruction pointer, and the +stack pointer and later restore this state. The idea is to have multiple +tasks/execution paths running on a single thread using a sort of cooperative +scheduling (threads are preemptively scheduled) - the running tasklet decides +explicitly when its yields to allow another tasklet to run (context switching). +A context switch between threads costs usally thousends of CPU cycles on x86 +compared to a tasklet switch with few hundreds of cycles. +A tasklet can only run on a single thread at any point in time but may be +migrated between threads. + +In order to use the classes and functions described here, you can either include +the specific headers specified by the descriptions of each class or function, or +include the master library header: + + #include + +which includes all the other headers in turn. + +Used namespaces are: + + namespace boost::tasklets + namespace boost::this_tasklet + + +[warning This library is ['not] an official Boost library] + +__boost_tasklet__ depends uppon __boost_atomic__, __boost_fiber__ and +__boost_move__. + + +[heading How to build and install] + +* download the sources from +[@http://www.boost-consulting.com/vault/index.php?directory=Concurrent%20Programming Boost Vault] +* extract the archive into the boost-source directory +* call ['bjam] (consider installation options of __boost_fiber__) + + +[endsect] diff --git a/libs/tasklet/doc/spin_condition_variables.qbk b/libs/tasklet/doc/spin_condition_variables.qbk new file mode 100644 index 00000000..7763c773 --- /dev/null +++ b/libs/tasklet/doc/spin_condition_variables.qbk @@ -0,0 +1,163 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] + +[section:spin_conditions Spinning Condition Variables] + +[heading Synopsis] + +The class `condition` provides a mechanism for one tasklet to wait for +notification `condition`. When the tasklet is woken from the wait, then it +checks to see if the appropriate condition is now true, and continues if so. If +the condition is not true, then the tasklet then calls `wait` again to resume +waiting. In the simplest case, this condition is just a boolean variable: + + boost::tasklets::condition cond; + boost::tasklets::mutex mtx; + bool data_ready; + + void process_data(); + + void wait_for_data_to_process() + { + boost::unique_lock< boost::tasklets::mutex > lk( mtx); + while ( ! data_ready) + { + cond.wait( lk); + } + process_data(); + } + +Notice that the `lk` is passed to `wait`: `wait` will atomically add the tasklet +to the set of tasklets waiting on the condition variable, and unlock the mutex. +When the tasklet is woken, the mutex will be locked again before the call to +`wait` returns. This allows other tasklets to acquire the mutex in order to +update the shared data, and ensures that the data associated with the condition +is correctly synchronized. + +In the mean time, another tasklet sets the condition to `true`, and then calls +either `notify_one` or `notify_all` on the condition variable to wake one +waiting tasklet or all the waiting tasklets respectively. + + void retrieve_data(); + void prepare_data(); + + void prepare_data_for_processing() + { + retrieve_data(); + prepare_data(); + { + boost::lock_guard< boost::tasklets::mutex > lk( mtx); + data_ready = true; + } + cond.notify_one(); + } + +Note that the same mutex is locked before the shared data is updated, but that +the mutex does not have to be locked across the call to `notify_one`. + +[section:condition Class `condition`] + + #include + + class condition + { + public: + void notify_one(); + void notify_all(); + + void wait( boost::unique_lock< boost::tasklets::mutex > & lk); + + template< typename Pred > + void wait( boost::unique_lock< boost::tasklets::mutex > & lk, Pred pred); + + template< typename LockType > + void wait( LockType & lk); + + template< typename LockType, typename Pred > + void wait( LockType & lk, Pred predicate); + }; + +[section:notify_one `void notify_one()`] +[variablelist +[[Effects:] [If any tasklets are currently __blocked__ waiting on `*this` in a +call to `wait`, unblocks one of those tasklets.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:notify_all `void notify_all()`] +[variablelist +[[Effects:] [If any tasklets are currently __blocked__ waiting on `*this` in a +call to `wait`, unblocks all of those tasklets.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:wait `void wait( boost::unique_lock< boost::tasklets::mutex > & lk)`] +[variablelist +[[Precondition:] [`lk` is locked by the current tasklet, and either no other +tasklet is currently waiting on `*this`, or the execution of the `mutex()` +member function on the `lk` objects supplied in the calls to `wait` in all the +tasklets currently waiting on `*this` would return the same value as +`lk->mutex()` for this call to `wait`.]] +[[Effects:] [Atomically call `lk.unlock()` and blocks the current tasklet. The +tasklet will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, or spuriously. When the tasklet is unblocked (for whatever +reason), the lock is reacquired by invoking `lk.lock()` before the call to +`wait` returns. The lock is also reacquired by invoking `lk.lock()` if the +function exits with an exception.]] +[[Postcondition:] [`lk` is locked by the current tasklet.]] +[[Throws:] [__tasklet_error__ if an error +occurs. __tasklet_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __tasklet__ object associated with the current tasklet of +execution.]] +] +[endsect] + +[section:wait_predicate `template< typename Pred > void wait( boost::unique_lock< boost::tasklets::mutex > & lk, Pred pred)`] +[variablelist +[[Effects:] [As-if `` +while ( ! pred()) +{ + wait( lk); +} +``]] + +] +[endsect] + +[section:wait_t `template< typename LockType > void wait( LockType & lk)`] +[variablelist +[[Effects:] [Atomically call `lk.unlock()` and blocks the current tasklet. The +tasklet will unblock when notified by a call to `this->notify_one()` or +`this->notify_all()`, or spuriously. When the tasklet is unblocked (for whatever +reason), the lock is reacquired by invoking `lk.lock()` before the call to +`wait` returns. The lock is also reacquired by invoking `lk.lock()` if the +function exits with an exception.]] +[[Postcondition:] [`lk` is locked by the current tasklet.]] +[[Throws:] [__tasklet_error__ if an error +occurs. __tasklet_interrupted__ if the wait was interrupted by a call to +__interrupt__ on the __tasklet__ object associated with the current tasklet of +execution.]] +] +[endsect] + +[section:wait_predicate_t `template< typename LockType, typename Pred > void wait( LockType & lk, Pred pred)`] +[variablelist +[[Effects:] [As-if `` +while ( ! pred()) +{ + wait( lock); +} +``]] + +] +[endsect] + +[endsect] + +[endsect] diff --git a/libs/tasklet/doc/spin_mutext.qbk b/libs/tasklet/doc/spin_mutext.qbk new file mode 100644 index 00000000..44590a7a --- /dev/null +++ b/libs/tasklet/doc/spin_mutext.qbk @@ -0,0 +1,38 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] + +[section:spin_mutex_types Spinning Mutex Types] + +[section:spin_mutex Class `spin_mutex`] + + #include + + class spin_mutex : private boost::noncopyable + { + public: + spin_mutex(); + + ~spin_mutex(); + + void lock(); + bool try_lock(); + void unlock(); + + typedef unique_lock< spin_mutex > scoped_lock; + }; + +__spin_mutex__ implements the __lockable_concept__ to provide an +exclusive-ownership mutex. At most one tasklet can own the lock on a given +instance of __mutex__ at any time. Multiple concurrent calls to __lock__, +__try_lock__ and __unlock__ shall be permitted. + +The tasklets and threads blocked in __lock__ are spin waiting until the lock get +released, e.g. try to get an calling __tasklet_yield__/__thread_yield__. + +[endsect] + +[endsect] diff --git a/libs/tasklet/doc/synchronization.qbk b/libs/tasklet/doc/synchronization.qbk new file mode 100644 index 00000000..07639c08 --- /dev/null +++ b/libs/tasklet/doc/synchronization.qbk @@ -0,0 +1,14 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + +[section:synchronization Synchronization] +[include mutexes.qbk] +[include condition_variables.qbk] +[include barrier.qbk] +[include event_variables.qbk] +[include channel.qbk] +[endsect] diff --git a/libs/tasklet/doc/tasklet.qbk b/libs/tasklet/doc/tasklet.qbk new file mode 100644 index 00000000..ccb529fd --- /dev/null +++ b/libs/tasklet/doc/tasklet.qbk @@ -0,0 +1,98 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + +[article Tasklet + [quickbook 1.4] + [authors [Kowalke, Oliver]] + [copyright 2009 Oliver Kowalke] + [purpose C++ Library for cooperativly schedule and synchronize multiple tasklets (micro-tasks) in one thread] + [category text] + [license + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + [@http://www.boost.org/LICENSE_1_0.txt]) + ] +] + + +[def __boost_atomic__ [@http://www.chaoticmind.net/~hcb/projects/boost.atomic [*Boost.Atomic]]] +[def __boost_fiber__ [*Boost.Fiber]] +[def __boost_tasklet__ [*Boost.Tasklet]] +[def __boost_move__ [@http://svn.boost.org/svn/boost/sandbox/move [*Boost.Move]]] +[def __boost_thread__ [@http://www.boost.org/libs/thread [*Boost.Thread]]] + +[template barrier_link[link_text] [link tasklet.synchronization.barriers [link_text]]] +[template condition_link[link_text] [link tasklet.synchronization.conditions.condition [link_text]]] +[template tasklet_id_link[link_text] [link tasklet.tasklet_management.tasklet.id [link_text]]] +[template tasklet_link[link_text] [link tasklet.tasklet_management.tasklet [link_text]]] +[template mutex_link[link_text] [link tasklet.synchronization.mutex_types.mutex [link_text]]] +[template round_robin_link[link_text] [link tasklet.tasklet_management.strategy.round_robin [link_text]]] +[template scheduler_link[link_text] [link tasklet.tasklet_management.scheduler [link_text]]] +[template spin_condition_link[link_text] [link tasklet.synchronization.conditions.spin_condition [link_text]]] +[template spin_mutex_link[link_text] [link tasklet.synchronization.mutex_types.spin_mutex [link_text]]] +[template strategy_link[link_text] [link tasklet.tasklet_management.strategy [link_text]]] + +[template auto_reset_wait_link[link_text] [link tasklet.synchronization.eventvar_ref.auto_reset_event.wait [link_text]]] +[template manual_reset_wait_link[link_text] [link tasklet.synchronization.eventvar_ref.manual_reset_event.wait [link_text]]] +[template count_down_wait_link[link_text] [link tasklet.synchronization.eventvar_ref.count_down_event.wait [link_text]]] +[template cond_wait_link[link_text] [link tasklet.synchronization.conditions.condition.wait [link_text]]] +[template interrupt_link[link_text] [link tasklet.tasklet_management.tasklet.interrupt [link_text]]] +[template join_link[link_text] [link tasklet.tasklet_management.tasklet.join [link_text]]] + +[template interr_enabled_link[link_text] [link tasklet.tasklet_management.this_tasklet.interruption_enabled [link_text]]] +[template interr_requested_link[link_text] [link tasklet.tasklet_management.this_tasklet.interruption_requested [link_text]]] +[template interr_point_link[link_text] [link tasklet.tasklet_management.this_tasklet.interruption_point [link_text]]] +[template disable_interr_link[link_text] [link tasklet.tasklet_management.this_tasklet.disable_interruption [link_text]]] +[template restore_interr_link[link_text] [link tasklet.tasklet_management.this_tasklet.restore_interruption [link_text]]] +[template interr_points_link[link_text] [link tasklet.tasklet_management.predefined_interruption_points [link_text]]] +[template yield_link[link_text] [link tasklet.tasklet_management.this_tasklet.yield [link_text]]] + +[def __blocked__ ['blocked]] +[def __not_a_tasklet__ ['not-a-tasklet]] +[def __lockable_concept__ ['lockable concept]] +[def __rendezvous__ ['rendezvous]] + +[def __barrier__ [barrier_link ['barrier]]] +[def __condition__ [condition_link ['condition]]] +[def __tasklet__ [tasklet_link ['tasklet]]] +[def __tasklet_id__ [tasklet_id_link ['tasklet-id]]] +[def __interruption_points__ [interr_points_link ['interruption points]]] +[def __mutex__ [mutex_link ['mutex]]] +[def __round_robin__ [round_robin_link ['round_robin]]] +[def __scheduler__ [scheduler_link ['scheduler]]] +[def __spin_condition__ [spin_condition_link ['spin_condition]]] +[def __spin_mutex__ [spin_mutex_link ['spin_mutex]]] +[def __strategy__ [strategy_link ['strategy]]] + +[def __cond_wait__ [cond_wait_link `wait()`]] +[def __interrupt__ [interrupt_link `interrupt()`]] +[def __join__ [join_link `join()`]] +[def __lock__ `lock()`] +[def __try_lock__ `try_lock()`] +[def __unlock__ `unlock()`] + +[def __interruption_enabled__ [interr_enabled_link `boost::this_tasklet::interruption_enabled()`]] +[def __interruption_requested__ [interr_requested_link `boost::this_tasklet::interruption_requested()`]] +[def __interruption_point__ [interr_point_link `boost::this_tasklet::interruption_point()`]] +[def __disable_interruption__ [disable_interr_link `boost::this_tasklet::disable_interruption`]] +[def __restore_interruption__ [restore_interr_link `boost::this_tasklet::restore_interruption`]] + +[def __tasklet_yield__ [yield_link `boost::this_tasklet::yield()`]] +[def __thread_yield__ `boost::this_thread::yield()`] + +[def __tasklet_error__ `boost::tasklets::tasklet_error`] +[def __tasklet_interrupted__ `boost::tasklets::tasklet_interrupted`] +[def __tasklet_moved__ `boost::tasklets::tasklet_moved`] +[def __invalid_argument__ `std::invalid_argument`] +[def __lock_error__ `boost::tasklets::lock_error`] + + + +[include overview.qbk] +[include tasklet_ref.qbk] +[include synchronization.qbk] +[include todo.qbk] diff --git a/libs/tasklet/doc/tasklet_ref.qbk b/libs/tasklet/doc/tasklet_ref.qbk new file mode 100644 index 00000000..d18a0a57 --- /dev/null +++ b/libs/tasklet/doc/tasklet_ref.qbk @@ -0,0 +1,1264 @@ +[/ + (C) Copyright 2007-8 Anthony Williams. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt). +] + +[section:tasklet_management Tasklet Management] + +[heading Synopsis] + +Each __tasklet__ class represents a micro-task which will be launched and +managed by the __scheduler__ class. Objects of type __tasklet__ are copy- and +moveable. + + boost::tasklet f; // not-a-tasklet + + void f() + { + boost::tasklet f1( boost::tasklets::make_tasklet( some_fn, boost::tasklet::default_stacksize) ); + boost::tasklet f2( boost::tasklets::make_tasklet( another_fn, boost::tasklet::default_stacksize) ); + + boost::tasklets::scheduler<> s; + s.submit( f1); // f1 gets copied + s.submit( boost::move( f2) ); // f2 gets moved + + std::cout << f1.get_id() << std::endl; + std::cout << f2.get_id() << std::endl; + } + + +[note If tasklets are migrated between threads the code called by a tasklet must +not use thread-local-storage.] + + +[heading Launching] + +A new tasklet is launched by passing an object of a callable type that can be +invoked with no parameters to the constructor and submiting the tasklet (copy- +or move-op.) to an instance of __scheduler__. The object is then copied into +internal storage, and invoked on the newly-created tasklet. If the object must +not (or cannot) be copied, then `boost::ref` can be used to pass in a reference +to the function object. In this case, the user of __boost_tasklet__ must ensure +that the referred-to object outlives the newly-created tasklet. + + struct callable + { void operator()(); }; + + boost::tasklet copies_are_safe() + { + callable x; + return boost::tasklet( x); + } // x is destroyed, but the newly-created tasklet has a copy, so this is OK + + boost::tasklet oops() + { + callable x; + return boost::tasklet( boost::ref( x), boost::tasklet::default_stacksize); + } // x is destroyed, but the newly-created tasklet still has a reference + // this leads to undefined behaviour + +If you wish to construct an instance of __tasklet__ with a function or callable +object that requires arguments to be supplied, this can be done by passing +additional arguments to the __tasklet__ constructor: + + void find_the_question( int the_answer); + + boost::tasklet deep_thought_2( find_the_question, 42, boost::tasklet::default_stacksize); + +The arguments are ['copied] into the internal tasklet structure: if a reference +is required, use `boost::ref`, just as for references to callable functions. + +For convinience `boost::tasklets::make_tasklet()` is provided: + + boost::tasklet f( boost::tasklets::make_tasklet( new_fn, arg1, arg2, arg3, boost::tasklet::default_stacksize) ); + +Another alternative is to use `scheduler<>::make_tasklet()` which creates and +stores the new tasklet inside the scheduler. + + boost::tasklets::scheduler<> sched; + sched.make_tasklet( new_fn, arg1, arg2, arg3, boost::tasklet_default_stacksize); + + +[heading Exceptions] + +Exceptions thrown by the function or callable object passed to the __tasklet__ +constructor are consumed by the framework (if it required to know which +exceptions was thrown use __future__ and __packaged_task__). + +[warning Don't use the sjlj exception model. Frame-unwind-tables instead of +setjmp/longjmp based exception handling should be used.] + +[heading Joining] + +In order to wait for a tasklet to finish, the __join__ member functions of the +__tasklet__ object can be used. __join__ will block the calling tasklet until +the tasklet represented by the __tasklet__ object has completed. +If the tasklet has already completed, or the __tasklet__ object represents +__not_a_tasklet__, then __join__ returns immediately. + + void some_fn() + {} + + void joining_tasklet_fn( boost::tasklet f) + { f.join(); } + + boost:.tasklets::scheduler<> sched; + boost::tasklet f( some_fn, boost::tasklet_default_stacksize); + sched.submit_tasklet( f); + sched.make_tasklet( joining_tasklet, f, boost::tasklet_default_stacksize); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + +[heading Interruption] + +A running tasklet can be ['interrupted] by invoking the __interrupt__ member +function. When the interrupted tasklet next executes one of the specified +__interruption_points__ (or if it is currently __blocked__ whilst executing one) +with interruption enabled, then a __tasklet_interrupted__ exception will be +thrown in the interrupted tasklet. If not caught, this will cause the execution +of the interrupted tasklet to terminate. + +If a tasklet wishes to avoid being interrupted, it can create an instance of +__disable_interruption__. Objects of this class disable interruption for the +tasklet that created them on construction, and restore the interruption state to +whatever it was before on destruction: + + void f() + { + // interruption enabled here + { + boost::this_tasklet::disable_interruption disabler1; + // interruption disabled + { + boost::this_tasklet::disable_interruption disabler2; + // interruption still disabled + } // disabler2 destroyed, interruption state restored + // interruption still disabled + } // disabler1 destroyed, interruption state restored + // interruption now enabled + } + +The effects of an instance of __disable_interruption__ can be temporarily +reversed by constructing an instance of __restore_interruption__, passing in the +__disable_interruption__ object in question. This will restore the interruption +state to what it was when the __disable_interruption__ object was constructed, +and then disable interruption again when the __restore_interruption__ object is +destroyed. + + void g() + { + // interruption enabled here + { + boost::this_tasklet::disable_interruption disabler; + // interruption disabled + { + boost::this_tasklet::restore_interruption restorer( disabler); + // interruption now enabled + } // restorer destroyed, interruption disable again + } // disabler destroyed, interruption state restored + // interruption now enabled + } + +At any point, the interruption state for the current tasklet can be queried by +calling __interruption_enabled__. + + +[heading Predefined Interruption Points] + +The following functions are ['interruption points], which will throw +__tasklet_interrupted__ if interruption is enabled for the current tasklet, and +interruption is requested for the current tasklet: + +* [join_link `boost::tasklet::join()`] +* [cond_wait_link `boost::tasklets::condition::wait()`] +* [auto_reset_wait_link `boost::tasklets::auto_reset_event::wait()`] +* [manual_reset_wait_link `boost::tasklets::manual_reset_event::wait()`] +* [count_down_wait_link `boost::tasklets::count_down_event::wait()`] +* __interruption_point__ + + +[heading Tasklet IDs] + +Objects of class __tasklet_id__ can be used to identify tasklets. Each running +tasklet has a unique ID obtainable from the corresponding __tasklet__ by calling +the `get_id()` member function, or by calling `boost::this_tasklet::get_id()` +from within the tasklet. Objects of class __tasklet_id__ can be copied, and used +as keys in associative containers: the full range of comparison operators is +provided. Tasklet IDs can also be written to an output stream using the stream +insertion operator, though the output format is unspecified. + +Each instance of __tasklet_id__ either refers to some tasklet, or +__not_a_tasklet__. Instances that refer to __not_a_tasklet__ compare equal to +each other, but not equal to any instances that refer to an actual tasklet. The +comparison operators on __tasklet_id__ yield a total order for every non-equal +tasklet ID. + + +[section:tasklet Class `tasklet`] + + #include + + class tasklet + { + public: + tasklet(); + ~tasklet(); + + template< typename Fn > + explicit tasklet( Fn fn, std::size_t stack_size); + + template< typename Fn, typename A1, typename A2,... > + tasklet( Fn fn, A1 a1, A2 a2,..., std::size_t stack_size); + + template< typename Fn > + explicit tasklet( Fn && fn, std::size_t stack_size); + + // move support + tasklet( tasklet && x); + tasklet & operator=( tasklet && x); + + operator unspecified-bool-type() const; + + bool operator!() const; + + void swap( tasklet & other); + + id get_id() const; + + bool operator==( tasklet const&) const; + bool operator!=( tasklet const&) const; + + bool is_alive() const; + + int priority() const; + + void priority( int); + + void interrupt(); + bool interruption_requested() const; + + void cancel(); + + void join(); + }; + + void swap( tasklet & lhs, tasklet & rhs); + + template< typename Fn > + tasklet make_tasklet( Fn fn, std::size_t stack_size); + + template< typename Fn, typename A1, typename A2,..., typename A10 > + tasklet make_tasklet( Fn fn, A1 a1, A2 a2,..., A10 a10, std::size_t stack_size); + + +[section:default_constructor `tasklet()`] +[variablelist +[[Effects:] [Constructs a __tasklet__ instance that refers to +__not_a_tasklet__.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:callable_constructor_stack `template< typename Fn > tasklet( Fn fn, std::size_t stack_size)`] +[variablelist +[[Preconditions:] [`Callable` must by copyable.]] +[[Effects:] [`fn` is copied into storage managed internally by the tasklet +library, and that copy is invoked on a newly-created tasklet. Second version +sets the stack-size of the tasklet.]] +[[Postconditions:] [`*this` refers to the newly created tasklet.]] +[[Throws:] [__bad_alloc__]] +] +[endsect] + +[section:multiple_argument_constructor `template< typename Fn, typename A1, typename A2,... > tasklet( Fn fn, A1 a1, A2 a2,..., std::size_t stack_size)`] +[variablelist +[[Preconditions:] [`Fn` and each `A`n must by copyable or movable.]] +[[Effects:] [As if `tasklet( boost::bind( fn, a1, a2,...) )`. Consequently, +`fn` and each `a`n are copied into internal storage for access by the new +tasklet. Second version additionaly sets the stack-size used by the tasklet.]] +[[Postconditions:] [`*this` refers to the newly created tasklet.]] +[[Throws:] [__bad_alloc__]] +[[Note:] [Currently up to nine additional arguments `a1` to `a9` can be +specified in addition to the function `fn`.]] +] +[endsect] + +[section:destructor `~tasklet()`] +[variablelist +[[Effects:] [Destroys `*this`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:get_id `tasklet::id get_id() const`] +[variablelist +[[Returns:] [If `*this` refers to a tasklet, an instance of __tasklet_id__ that +represents that tasklet. Otherwise returns a default-constructed +__tasklet_id__.]] +[[Throws:] [Nothing.]] +] + +[endsect] + +[section:join `void join()`] +[variablelist +[[Effects:] [Waits for `*this` to complete.]] +[[Throws:] [__tasklet_interrupted__ if the current tasklet is interrupted.]] +[[Notes:] [`join()` is one of the predefined __interruption_points__.]] +] +[endsect] + +[section:interrupt `void interrupt()`] +[variablelist +[[Effects:] [If `*this` refers to a tasklet, request that the tasklet will be +interrupted the next time it enters one of the predefined +__interruption_points__ with interruption enabled, or if it is currently +__blocked__ in a call to one of the predefined __interruption_points__ with +interruption enabled .]] +[[Throws:] [__tasklet_moved__ if not a tasklet of execution.]] +] +[endsect] + +[section:interruption_requested `bool interruption_requested()`] +[variablelist +[[Effects:] [If `*this` refers to a tasklet, the function returns if the +interruption of the tasklet was already requested.]] +[[Throws:] [__tasklet_moved__ if `*this` is not a tasklet of execution.]] +] +[endsect] + +[section:cancel `void cancel()`] +[variablelist +[[Effects:] [If `*this` refers to a tasklet, request that the tasklet will be +canceled the next time it yields its execution.]] +[[Throws:] [__tasklet_moved__ if `*this` is not a tasklet of execution.]] +] +[endsect] + +[section:is_alive `bool is_alive()`] +[variablelist +[[Effects:] [If `*this` refers to a tasklet, the function returns false if the +tasklet is terminated or not started Otherwise it returns true.]] +[[Throws:] [__tasklet_moved__ if `*this` is not a tasklet of execution.]] +] +[endsect] + +[section:unspec_operator `operator unspecified-bool-type() const`] +[variablelist +[[Returns:] [If `*this` refers to a tasklet, the function returns true. +Otherwise false.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:not_operator `bool operator!() const`] +[variablelist +[[Returns:] [If `*this` refers not to a tasklet, the function returns true. +Otherwise false.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:equals `bool operator==( tasklet const& other) const`] +[variablelist +[[Returns:] [`get_id()==other.get_id()`]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:not_equals `bool operator!=( tasklet const& other) const`] +[variablelist +[[Returns:] [`get_id()!=other.get_id()`]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:swap `void swap( tasklet & other)`] +[variablelist +[[Effects:] [Exchanges the tasklets associated with `*this` and `other`, so +`*this` is associated with the tasklet associated with `other` prior to the +call, and vice-versa.]] +[[Postconditions:] [`this->get_id()` returns the same value as `other.get_id()` +prior to the call. `other.get_id()` returns the same value as `this->get_id()` +prior to the call.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:non_member_swap Non-member function `swap()`] + + #include + + void swap( tasklet & lhs, tasklet & rhs); + +[variablelist +[[Effects:] [[link tasklet.tasklet_management.tasklet.swap `lhs.swap( rhs)`].]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:non_member_make_tasklet Non-member template function `make_tasklet()`] + + #include + + template< typename Fn > + tasklet make_tasklet( Fn fn, std::size_t stack_size); + + template< typename Fn, typename A1, typename A2,... > + tasklet make_tasklet( Fn fn, A1 a1, A2 a2,..., std::size_t stack_size); + +[variablelist +[[Effects:] [Creates a tasklet.]] +] +[endsect] + +[section:id Class `boost::tasklet::id`] + + #include + + class tasklet::id + { + public: + id(); + + bool operator==( id const& y) const; + bool operator!=( id const& y) const; + bool operator<( id const& y) const; + bool operator>( id const& y) const; + bool operator<=( id const& y) const; + bool operator>=( id const& y) const; + + template< typename charT, typename traitsT > + friend std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream & os, id const& x); + }; + +[section:constructor `id()`] +[variablelist +[[Effects:] [Constructs a __tasklet_id__ instance that represents +__not_a_tasklet__.]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:is_equal `bool operator==( id const& y) const`] +[variablelist +[[Returns:] [`true` if `*this` and `y` both represent the same tasklet of +execution, or both represent __not_a_tasklet__, `false` otherwise.]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:not_equal `bool operator!=( id const& y) const`] +[variablelist +[[Returns:] [`true` if `*this` and `y` represent different tasklets of +execution, or one represents a tasklet of execution, and the other represent +__not_a_tasklet__, `false` otherwise.]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:less_than `bool operator<( id const& y) const`] +[variablelist +[[Returns:] [`true` if `*this!=y` is `true` and the implementation-defined total +order of __tasklet_id__ values places `*this` before `y`, `false` otherwise.]] +[[Throws:] [Nothing]] +[[Note:] [A __tasklet_id__ instance representing __not_a_tasklet__ will always +compare less than an instance representing a tasklet of execution.]] +] +[endsect] + +[section:greater_than `bool operator>( id const& y) const`] +[variablelist +[[Returns:] [`y<*this`]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:less_than_or_equal `bool operator<=( id const& y) const`] +[variablelist +[[Returns:] [`!(y<*this)`]] +[[Throws:] [Nothing]] +] +[endsect] + +[section:greater_than_or_equal `bool operator>=( id const& y) const`] +[variablelist +[[Returns:] [`!(*this + friend std::basic_ostream< charT, traitsT > & + operator<<( std::basic_ostream & os, id const& x); + +[variablelist +[[Effects:] [Writes a representation of the __tasklet_id__ instance `x` to the +stream `os`, such that the representation of two instances of __tasklet_id__ `a` +and `b` is the same if `a==b`, and different if `a!=b`.]] +[[Returns:] [`os`]] +] +[endsect] + +[endsect] + +[endsect] + +[section:this_tasklet Namespace `this_tasklet`] + +[section:get_id Non-member function `get_id()`] + + #include + + namespace this_tasklet + { + tasklet::id get_id(); + } + +[variablelist +[[Returns:] [An instance of __tasklet_id__ that represents that currently +executing tasklet.]] +[[Throws:] [__tasklet_error__ if an error occurs.]] +] +[endsect] + +[section:interruption_point Non-member function `interruption_point()`] + + #include + + namespace this_tasklet + { + void interruption_point(); + } + +[variablelist +[[Effects:] [Check to see if the current tasklet has been interrupted.]] +[[Throws:] [__tasklet_interrupted__ if __interruption_enabled__ and +__interruption_requested__ both return `true`.]] +] +[endsect] + +[section:interruption_requested Non-member function `interruption_requested()`] + + #include + + namespace this_tasklet + { + bool interruption_requested(); + } + +[variablelist +[[Returns:] [`true` if interruption has been requested for the current tasklet, +`false` otherwise.]] +[[Throws:] [__tasklet_error__ if an error occurs.]] +] +[endsect] + +[section:interruption_enabled Non-member function `interruption_enabled()`] + + #include + + namespace this_tasklet + { + bool interruption_enabled(); + } + +[variablelist +[[Returns:] [`true` if interruption has been enabled for the current tasklet, +`false` otherwise.]] +[[Throws:] [__tasklet_error__ if an error occurs.]] +] +[endsect] + +[section:cancel Non-member function `cancel()`] + + #include + + namespace this_tasklet + { + void cancel(); + } + +[variablelist +[[Effects:] [Cancels the current tasklet.]] +[[Throws:] [__tasklet_error__ if an error occurs.]] +] +[endsect] + +[section:non_member_yield Non-member function `yield()`] + + #include + + namespace this_tasklet + { + void yield(); + } + +[variablelist +[[Effects:] [Gives up the remainder of the current tasklet's time slice, to +allow other tasklets to run.]] +[[Throws:] [__tasklet_error__ if an error occurs.]] +] +[endsect] + +[section:attaskletexit Non-member template function `at_tasklet_exit()`] + + #include + + template + void at_tasklet_exit(Callable func); + +[variablelist +[[Effects:] [A copy of `func` is placed in +tasklet-specific storage. This copy is invoked when the current tasklet +exits (even if the tasklet has been interrupted).]] +[[Postconditions:] [A copy of `func` has been saved for invocation on tasklet +exit.]] +[[Throws:] [`std::bad_alloc` if memory cannot be allocated for the copy of the +function, __tasklet_error__ if any other error occurs within the tasklet +library. Any exception thrown whilst copying `func` into internal storage.]] +[[Note:] [This function is *not* called if the tasklet was terminated +forcefully using platform-specific APIs, or if the tasklet is +terminated due to a call to `exit()`, `abort()` or +`std::terminate()`. In particular, returning from `main()` is +equivalent to call to `exit()`, so will not call any functions +registered with `at_tasklet_exit()`]] +] +[endsect] + +[section:submit Non-member function `submit_tasklet( tasklet)`] + + #include + + namespace this_tasklet + { + void submit_tasklet( tasklet); + } + +[variablelist +[[Effects:] [Submits the passed tasklet to the scheduler running the active +tasklet.]] +[[Throws:] [__tasklet_error__ if an error occurs.]] +] +[endsect] + +[section:disable_interruption Class `disable_interruption`] + + #include + + namespace this_tasklet + { + class disable_interruption + { + public: + disable_interruption(); + ~disable_interruption(); + }; + } + +`boost::this_tasklet::disable_interruption` disables interruption for the +current tasklet on construction, and restores the prior interruption state on +destruction. Instances of `disable_interruption` cannot be copied or moved. + +[section:constructor `disable_interruption()`] +[variablelist +[[Effects:] [Stores the current state of __interruption_enabled__ and disables +interruption for the current tasklet.]] +[[Postconditions:] [__interruption_enabled__ returns `false` for the current +tasklet.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:destructor `~disable_interruption()`] +[variablelist +[[Preconditions:] [Must be called from the same tasklet from which `*this` was +constructed.]] +[[Effects:] [Restores the current state of __interruption_enabled__ for the +current tasklet to that prior to the construction of `*this`.]] +[[Postconditions:] [__interruption_enabled__ for the current tasklet returns the +value stored in the constructor of `*this`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[endsect] + +[section:restore_interruption Class `restore_interruption`] + + #include + + namespace this_tasklet + { + class restore_interruption + { + public: + explicit restore_interruption(disable_interruption& disabler); + ~restore_interruption(); + }; + } + +On construction of an instance of `boost::this_tasklet::restore_interruption`, +the interruption state for the current tasklet is restored to the interruption +state stored by the constructor of the supplied instance of +__disable_interruption__. When the instance is destroyed, interruption is again +disabled. Instances of `restore_interruption` cannot be copied or moved. + +[section:constructor `explicit restore_interruption(disable_interruption& disabler)`] +[variablelist +[[Preconditions:] [Must be called from the same tasklet from which `disabler` +was constructed.]] +[[Effects:] [Restores the current state of __interruption_enabled__ for the +current tasklet to that prior to the construction of `disabler`.]] +[[Postconditions:] [__interruption_enabled__ for the current tasklet returns the +value stored in the constructor of `disabler`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:destructor `~restore_interruption()`] +[variablelist +[[Preconditions:] [Must be called from the same tasklet from which `*this` was +constructed.]] +[[Effects:] [Disables interruption for the current tasklet.]] +[[Postconditions:] [__interruption_enabled__ for the current tasklet returns +`false`.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[endsect] + +[endsect] + +[section:scheduler Scheduler] + +[heading Synopsis] + +The template __scheduler__ is responsible for managing and scheduling of +tasklets passed to it. The scheduling algorithm is provided as an template +argument to __scheduler__ (default is __round_robin__). The class implementing +the scheduling algorithm must derive from class __strategy__. It is possible to +migrate tasklets between schedulers. + +[note If the tasklet is not thread-affine (using thread-local-storage) it can be +migrated to a scheduler running in another thread.] + +Usally __scheduler__ will be invoked until all tasklets are scheduled and +finished. + + boost::tasklets::scheduler<> sched; + sched.make_tasklet( some_fn); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + +[section:scheduler Template `template< typename Strategy > scheduler`] + + #include + + template< typename Strategy > + class scheduler + { + public: + scheduler(); + + ~scheduler(); + + bool run(); + + bool empty(); + + std::size_t size(); + + std::size_t ready(); + + void submit_tasklet( tasklet); + + template< typename Fn > + void make_tasklet( Fn fn, std::size_t stack_size); + + template< typename Fn > + void make_tasklet( Fn && fn, std::size_t stack_size); + + template< typename Fn, typename A1, typename A2,..., typename A10 > + void make_tasklet( Fn fn, A0 a0, A1 a1,..., A10 a10, std:size_t stack_size); + + void migrate_tasklet( tasklet f); + + template< typename OtherStrategy > + void migrate_tasklet( tasklet::id const& id, scheduler< OtherStrategy & sched); + }; + + +[section:ctor `scheduler()`] +[variablelist +[[Effects:] [Creates an object of type scheduler< Strategy >.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:dtor `~scheduler()`] +[variablelist +[[Effects:] [Detaches all managed tasklets and destroys the scheduler.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:run `bool run()`] +[variablelist +[[Effects:] [Executes a tasklet from the internal storage and removes terminated +tasklets. The function returns `true` if a tasklet could be executed or a +terminated tasklet could be removed - otherwise `false`.]] +[[Throws:] [__system_error__ if a system call failed.]] +] +[endsect] + +[section:empty `bool empty()`] +[variablelist +[[Effects:] [Returns `true` if the scheduler contains tasklets (maybe runnable +or waiting).]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:size `std::size_t size()`] +[variablelist +[[Effects:] [Returns how many tasklets the scheduler contains (maybe runnable or +waiting).]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:ready `std::size_t ready()`] +[variablelist +[[Effects:] [Returns how many tasklets are ready to run in the scheduler.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:submit_tasklet `void submit_tasklet( tasklet f)`] +[variablelist +[[Effects:] [This function stores the passed tasklet in the scheduler.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:make_tasklet2 `template< typename Fn > void make_tasklet( Fn fn, std::size_t stack_size)`] +[variablelist +[[Effects:] [The functions create a tasklet which gets stored in the internal +structures from scheduler.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:make_tasklet3 `template< typename Fn, typename A1, typename A2,..., typename A10 > void make_tasklet( Fn fn, A0 a0, A1 a1,..., A10 a10, std::size_t stack_size)`] +[variablelist +[[Effects:] [The functions create a tasklet which gets stored in the internal +structures from scheduler.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:migrate_tasklet `void migrate_tasklet( tasklet f)`] +[variablelist +[[Effects:] [The tasklet will be migrated to the scheduler.]] +[[Throws:] [Throws __tasklet_moved__, tasklet_error, scheduler_error.]] +] +[endsect] + +[section:migrate_tasklet_t `template< typename OtherStrategy > void migrate_tasklet( tasklet::id const& id, scheduler< OtherStrategy > & sched)`] +[variablelist +[[Effects:] [The tasklet will be migrated to the scheduler.]] +[[Throws:] [Throws __tasklet_moved__, tasklet_error, scheduler_error.]] +] +[endsect] + +[endsect] + +[endsect] + +[section:strategy Strategy] + +[heading Synopsis] + +The template __scheduler__ accepts the implementation of the scheduling +algorithm as a template argument (currently __round_robin__ is the choosen as +the default). Each class destined to implement a scheduling algorithm must +derive from __strategy__. The member `active_tasklet` holds the current +(running) tasklet and `master_tasklet` executes the scheduling logic. + + +[section:strategy Class `strategy`] + + #include + + class strategy + { + protected: + static __thread tasklet * active_tasklet; + + void attach( tasklet &); + void detach( tasklet &); + + void call( tasklet &); + void yield( tasklet &); + + void enable_interruption( tasklet &); + bool interruption_enabled( tasklet const&); + + bool in_state_not_started( tasklet const&); + bool in_state_ready( tasklet const&); + bool in_state_running( tasklet const&); + bool in_state_wait_for_tasklet( tasklet const&); + bool in_state_wait_for_object( tasklet const&); + bool in_state_terminated( tasklet const&); + + void set_state_ready( tasklet &); + void set_state_running( tasklet &); + void set_state_wait_for_tasklet( tasklet &); + void set_state_wait_for_object( tasklet &); + void set_state_terminated( tasklet &); + + public: + strategy(); + + virtual ~strategy(); + virtual void add( tasklet) = 0; + virtual void join( tasklet::id const&) = 0; + virtual void interrupt( tasklet::id const&) = 0; + virtual void reschedule( tasklet::id const&) = 0; + virtual void cancel( tasklet::id const&) = 0; + virtual void yield( tasklet::id const&) = 0; + virtual void wait_for_object( object::id const&, spin_mutex::scoped_lock &) = 0; + virtual void object_notify_one( object::id const&) = 0; + virtual void object_notify_all( object::id const&) = 0; + virtual tasklet release( tasklet::id const&) = 0; + virtual void migrate( tasklet) = 0; + virtual void detach_all() = 0; + virtual bool run() = 0; + virtual bool empty() = 0; + virtual std::size_t size() = 0; + virtual std::size_t ready() = 0; + }; + + +[section:attach `void attach( tasklet &)`] +[variablelist +[[Effects:] [Protected member function in order to register the scheduler in +internal storage from the tasklet.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:detach `void detach( tasklet &)`] +[variablelist +[[Effects:] [Protected member function in order to unregister the scheduler in +internal storage from the tasklet.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:call `void call( tasklet & f)`] +[variablelist +[[Effects:] [Protected member function calls tasklet `f`.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:yield2 `void yield( tasklet & f)`] +[variablelist +[[Effects:] [Protected member function yield tasklet `f`.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:enable_interruption `void enable_interruption( tasklet &)`] +[variablelist +[[Effects:] [Protected member function enables interruption on the tasklet.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:interruption_enabled `bool interruption_enabled( tasklet const&)`] +[variablelist +[[Effects:] [Protected member function tests if interruption is enabled on the +tasklet.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:in_state_not_started `bool in_state_not_started( tasklet const&)`] +[variablelist +[[Effects:] [Protected member function tests if the tasklet is in the state +`started`.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:in_state_ready `bool in_state_ready( tasklet const&)`] +[variablelist +[[Effects:] [Protected member function tests if the tasklet is in the state +`ready`.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:in_state_running `bool in_state_running( tasklet const&)`] +[variablelist +[[Effects:] [Protected member function tests if the tasklet is in the state +`running`.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:in_state_wait_for_tasklet `bool in_state_wait_for_tasklet( tasklet const&)`] +[variablelist +[[Effects:] [Protected member function tests if the tasklet is in the state +`wait for tasklet`.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:in_state_wait_for_object `bool in_state_wait_for_object( tasklet const&)`] +[variablelist +[[Effects:] [Protected member function tests if the tasklet is in the state +`wait for object`.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:in_state_terminated `bool in_state_terminated( tasklet const&)`] +[variablelist +[[Effects:] [Protected member function tests if the tasklet is in the state +`terminated`.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:set_state_ready `void set_state_ready( tasklet &)`] +[variablelist +[[Effects:] [Protected member function sets the state of the tasklet to +`ready`.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:set_state_running `void set_state_running( tasklet &)`] +[variablelist +[[Effects:] [Protected member function sets the state of the tasklet to +`running`.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:set_state_wait_for_tasklet `void set_state_wait_for_tasklet( tasklet &)`] +[variablelist +[[Effects:] [Protected member function sets the state of the tasklet to +`wait for tasklet`.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:set_state_wait_for_object `void set_state_wait_for_object( tasklet &)`] +[variablelist +[[Effects:] [Protected member function sets the state of the tasklet to +`wait for object`.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:set_state_terminated `void set_state_terminated( tasklet &)`] +[variablelist +[[Effects:] [Protected member function sets the state of the tasklet to +`terminated`.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:ctor `strategy()`] +[variablelist +[[Effects:] [Creates an object of type strategy and initializes the +master_tasklet member.]] +[[Throws:] [Nothing.]] +] +[endsect] + + +[section:add `virtual void add( tasklet) = 0`] +[variablelist +[[Precondition:] [Tasklet was not assigned to another scheduler.]] +[[Effects:] [Add a tasklet to the scheduler.]] +] +[endsect] + + +[section:join `virtual void join( tasklet::id const&) = 0`] +[variablelist +[[Precondition:] [Tasklet belongs to this scheduler.]] +[[Effects:] [The active tasklet will yield until the tasklet identified by the +id has finished its +execution or was interrupted.]] +] +[endsect] + + +[section:interrupt `virtual void interrupt( tasklet::id const&) = 0`] +[variablelist +[[Precondition:] [Tasklet belongs to this scheduler.]] +[[Effects:] [The tasklet identified by the id will be interrupted by its next +execution.]] +] +[endsect] + + +[section:reschedule `virtual void reschedule( tasklet::id const&) = 0`] +[variablelist +[[Precondition:] [Tasklet belongs to this scheduler.]] +[[Effects:] [The scheduling-algorithm will be applied uppon the tasklet +identified by the id again.]] +] +[endsect] + + +[section:cancel `virtual void cancel( tasklet::id const&) = 0`] +[variablelist +[[Precondition:] [Tasklet belongs to this scheduler.]] +[[Effects:] [The tasklet with passed identifier will be stopped, e.g. removed +from the run-queue.]] +] +[endsect] + + +[section:yield1 `virtual void yield( tasklet::id const&) = 0`] +[variablelist +[[Precondition:] [Tasklet is the active tasklet.]] +[[Effects:] [Yield active tasklet and switch back to `master_tasklet` in order +to schedule another tasklet from the internal storage.]] +] +[endsect] + + +[section:wait_for_object `virtual void wait_for_object( object::id const&, spin_mutex::scoped_lock &) = 0`] +[variablelist +[[Effects:] [The active tasklet waits on object until notified.]] +] +[endsect] + + +[section:object_notify_one `virtual void object_notify_one( object::id const&) = 0`] +[variablelist +[[Effects:] [Notify one tasklet waiting on the object. The tasklet gets ready +for scheduling.]] +] +[endsect] + + +[section:object_notify_all `virtual void object_notify_all( object::id const&) = 0`] +[variablelist +[[Effects:] [Notify all tasklets waiting on the object. The tasklets get ready +for scheduling.]] +] +[endsect] + + +[section:release `virtual void release( tasklet::id const&) = 0`] +[variablelist +[[Postcondition:] [The tasklet is not terminated or waits on tasklet or +object.]] +[[Effects:] [Release tasklet from scheduler.]] +] +[endsect] + + +[section:migrate `virtual void migrate( tasklet) = 0`] +[variablelist +[[Postcondition:] [The tasklet is not already managed by scheduler.]] +[[Effects:] [Adds tasklet to scheduler.]] +] +[endsect] + + +[section:detach_all `virtual void detach_all() = 0`] +[variablelist +[[Effects:] [Detaches all managed tasklets from this.]] +] +[endsect] + + +[section:run `virtual bool run() = 0`] +[variablelist +[[Effects:] [Executes a tasklet from the internal storage and removes terminated +tasklets. The function returns `true` if a tasklet could be executed or a +terminated tasklet could be removed - otherwise `false`.]] +[[Throws:] [__system_error__ if a system call failed.]] +] +[endsect] + +[section:empty `virtual bool empty() = 0`] +[variablelist +[[Effects:] [Returns `true` if the scheduler contains tasklets (maybe runnable +or waiting).]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:size `virtual std::size_t size() = 0`] +[variablelist +[[Effects:] [Returns how many tasklets the scheduler contains (maybe runnable or +waiting).]] +[[Throws:] [Nothing.]] +] +[endsect] + +[section:ready `virtual std::size_t ready() = 0`] +[variablelist +[[Effects:] [Returns how many tasklets are ready to run in the scheduler.]] +[[Throws:] [Nothing.]] +] +[endsect] + +[endsect] + +[section:round_robin Class `round_robin`] + +The class `round_robin` derives from __strategy__ and implements a simple +round-robin scheduling-algorithm. It does not make use of tasklet-priorities. + +[endsect] + +[endsect] + +[endsect] diff --git a/libs/tasklet/doc/todo.qbk b/libs/tasklet/doc/todo.qbk new file mode 100644 index 00000000..76e0a60e --- /dev/null +++ b/libs/tasklet/doc/todo.qbk @@ -0,0 +1,15 @@ +[/ + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt +] + +[section:todo Todo] + +* optimization of code: + * memory ordering in atomic operations + * implementation of round-robin scheduler +* provide an scheduler which deals with priorities + +[endsect] diff --git a/libs/tasklet/examples/Jamfile.v2 b/libs/tasklet/examples/Jamfile.v2 new file mode 100644 index 00000000..63c77ab8 --- /dev/null +++ b/libs/tasklet/examples/Jamfile.v2 @@ -0,0 +1,27 @@ +# Boost.Tasklet Library Examples Jamfile + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For more information, see http://www.boost.org/ + +project boost/tasklet/example + : requirements + ../build//boost_tasklet + /boost/fiber//boost_fiber + /boost/thread//boost_thread + true + ; + +exe barrier_mt : barrier_mt.cpp ; +exe cancel : cancel.cpp ; +exe future : future.cpp ; +exe interrupt : interrupt.cpp ; +exe join : join.cpp ; +exe future_mt : future_mt.cpp ; +exe migrate_mt : migrate_mt.cpp ; +exe ping_pong_mt : ping_pong_mt.cpp ; +exe ping_pong : ping_pong.cpp ; +exe simple : simple.cpp ; diff --git a/libs/tasklet/examples/barrier_mt.cpp b/libs/tasklet/examples/barrier_mt.cpp new file mode 100644 index 00000000..f5325c4a --- /dev/null +++ b/libs/tasklet/examples/barrier_mt.cpp @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +int value1 = 0; +int value2 = 0; + +inline +void fn1( boost::tasklets::barrier & b) +{ + std::stringstream tss; + tss << boost::this_thread::get_id(); + std::stringstream fss; + fss << boost::this_tasklet::get_id(); + + fprintf( stderr, "start fn1 tasklet %s in thread %s\n", fss.str().c_str(), tss.str().c_str() ); + + ++value1; + fprintf( stderr, "fn1 tasklet %s in thread %s increment %d\n", fss.str().c_str(), tss.str().c_str(), value1); + + fprintf( stderr, "fn1 tasklet %s in thread %s before wait for barrier\n", fss.str().c_str(), tss.str().c_str()); + b.wait(); + fprintf( stderr, "fn1 tasklet %s in thread %s after wait for barrier\n", fss.str().c_str(), tss.str().c_str()); + + ++value1; + fprintf( stderr, "fn1 tasklet %s in thread %s increment %d\n", fss.str().c_str(), tss.str().c_str(), value1); + + ++value1; + fprintf( stderr, "fn1 tasklet %s in thread %s increment %d\n", fss.str().c_str(), tss.str().c_str(), value1); + + ++value1; + fprintf( stderr, "fn1 tasklet %s in thread %s increment %d\n", fss.str().c_str(), tss.str().c_str(), value1); + + ++value1; + fprintf( stderr, "fn1 tasklet %s in thread %s increment %d\n", fss.str().c_str(), tss.str().c_str(), value1); + + fprintf( stderr, "end fn1 tasklet %s in thread %s\n", fss.str().c_str(), tss.str().c_str() ); +} + +inline +void fn2( boost::tasklets::barrier & b) +{ + std::stringstream tss; + tss << boost::this_thread::get_id(); + std::stringstream fss; + fss << boost::this_tasklet::get_id(); + + fprintf( stderr, "start fn2 tasklet %s in thread %s\n", fss.str().c_str(), tss.str().c_str() ); + + ++value2; + fprintf( stderr, "fn2 tasklet %s in thread %s increment %d\n", fss.str().c_str(), tss.str().c_str(), value2); + + ++value2; + fprintf( stderr, "fn2 tasklet %s in thread %s increment %d\n", fss.str().c_str(), tss.str().c_str(), value2); + + ++value2; + fprintf( stderr, "fn2 tasklet %s in thread %s increment %d\n", fss.str().c_str(), tss.str().c_str(), value2); + + ++value2; + fprintf( stderr, "fn2 tasklet %s in thread %s increment %d\n", fss.str().c_str(), tss.str().c_str(), value2); + + fprintf( stderr, "fn2 tasklet %s in thread %s before wait for barrier\n", fss.str().c_str(), tss.str().c_str()); + b.wait(); + fprintf( stderr, "fn2 tasklet %s in thread %s after wait for barrier\n", fss.str().c_str(), tss.str().c_str()); + + ++value2; + fprintf( stderr, "fn2 tasklet %s in thread %s increment %d\n", fss.str().c_str(), tss.str().c_str(), value2); + + fprintf( stderr, "end fn2 tasklet %s in thread %s\n", fss.str().c_str(), tss.str().c_str() ); +} + +void f( + boost::barrier & tb, + boost::tasklets::barrier & fb) +{ + std::stringstream tss; + tss << boost::this_thread::get_id(); + + fprintf( stderr, "start thread %s\n", tss.str().c_str() ); + + boost::tasklets::scheduler<> sched; + + sched.submit_tasklet( + boost::tasklet( + & fn1, boost::ref( fb), boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + tb.wait(); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + fprintf( stderr, "end thread %s\n", tss.str().c_str() ); +} + +void g( + boost::barrier & tb, + boost::tasklets::barrier & fb) +{ + std::stringstream tss; + tss << boost::this_thread::get_id(); + + fprintf( stderr, "start thread %s\n", tss.str().c_str() ); + + boost::tasklets::scheduler<> sched; + + sched.submit_tasklet( + boost::tasklet( + & fn2, boost::ref( fb), boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + tb.wait(); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + fprintf( stderr, "end thread %s\n", tss.str().c_str() ); +} + +int main() +{ + try + { + boost::barrier tb( 2); + boost::tasklets::barrier fb( 2); + + std::cout << "start" << std::endl; + + boost::thread th1( boost::bind( & f, boost::ref( tb), boost::ref( fb) ) ); + boost::thread th2( boost::bind( & g, boost::ref( tb), boost::ref( fb) ) ); + + th1.join(); + th2.join(); + + std::cout << "finish" << std::endl; + + return EXIT_SUCCESS; + } + catch ( boost::tasklets::scheduler_error const& e) + { std::cerr << "scheduler_error: " << e.what() << std::endl; } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/tasklet/examples/cancel.cpp b/libs/tasklet/examples/cancel.cpp new file mode 100644 index 00000000..f5c00037 --- /dev/null +++ b/libs/tasklet/examples/cancel.cpp @@ -0,0 +1,69 @@ +#include +#include +#include + +#include + +#include + +int value1 = 0; +int value2 = 0; + +void fn_1() +{ + for ( int i = 0; i < 5; ++i) + { + ++value1; + std::cout << "fn_1() increment value1 " << value1 << std::endl; + boost::this_tasklet::yield(); + } +} + +void fn_2( boost::tasklet f) +{ + for ( int i = 0; i < 5; ++i) + { + ++value2; + std::cout << "fn_2() increment value2 " << value2 << std::endl; + if ( i == 1) + { + std::cout << "fn_2() cancel tasklet " << f.get_id() << std::endl; + f.cancel(); + } + boost::this_tasklet::yield(); + } +} + +int main() +{ + try + { + boost::tasklets::scheduler<> sched; + + boost::tasklet f( fn_1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( f); + sched.submit_tasklet( + boost::tasklet( + fn_2, f, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + std::cout << "start" << std::endl; + std::cout << "tasklet to be canceled " << f.get_id() << std::endl; + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + std::cout << "finish: value1 == " << value1 << ", value2 == " << value2 << std::endl; + + return EXIT_SUCCESS; + } + catch ( boost::tasklets::scheduler_error const& e) + { std::cerr << "scheduler_error: " << e.what() << std::endl; } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/tasklet/examples/future.cpp b/libs/tasklet/examples/future.cpp new file mode 100644 index 00000000..fec7e657 --- /dev/null +++ b/libs/tasklet/examples/future.cpp @@ -0,0 +1,44 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include + +inline +std::string helloworld_fn() +{ return "Hello World"; } + +int main() +{ + try + { + boost::tasklets::scheduler<> sched; + + boost::tasklets::packaged_task< std::string > pt( helloworld_fn); + boost::tasklets::unique_future< std::string > fu = pt.get_future(); + boost::tasklet t( boost::move( pt), boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( t); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + std::cout << fu.get() << std::endl; + + return EXIT_SUCCESS; + } + catch ( boost::tasklets::scheduler_error const& e) + { std::cerr << "scheduler_error: " << e.what() << std::endl; } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/tasklet/examples/future_mt.cpp b/libs/tasklet/examples/future_mt.cpp new file mode 100644 index 00000000..c1f70fde --- /dev/null +++ b/libs/tasklet/examples/future_mt.cpp @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +class callable +{ +private: + struct impl + { + virtual ~impl() {} + + virtual void exec() = 0; + }; + + template< typename T > + class timpl : public impl + { + private: + boost::tasklets::packaged_task< T > pt_; + + public: + timpl( boost::tasklets::packaged_task< T > & pt) : + pt_( boost::move( pt) ) + {} + + void exec() + { pt_(); } + }; + + boost::shared_ptr< impl > impl_; + +public: + template< typename T > + callable( boost::tasklets::packaged_task< T > & pt) : + impl_( new timpl< T >( pt) ) + {} + + void operator()() + { impl_->exec(); } +}; + +int value1 = 0; +int value2 = 0; + +inline +std::string helloworld_fn() +{ + std::stringstream ss; + ss << boost::this_thread::get_id(); + + fprintf( stderr, "thread %s returns 'Hello World'\n", ss.str().c_str() ); + return "Hello World"; +} + +void increment_fn1() +{ + std::stringstream ss; + ss << boost::this_thread::get_id(); + + for ( int i = 0; i < 5; ++i) + { + ++value1; + fprintf( stderr, "thread %s incremented value1 %d\n", ss.str().c_str(), value1); + boost::this_tasklet::yield(); + } +} + +void increment_fn2() +{ + std::stringstream ss; + ss << boost::this_thread::get_id(); + + for ( int i = 0; i < 3; ++i) + { + ++value2; + fprintf( stderr, "thread %s incremented value2 %d\n", ss.str().c_str(), value2); + } + + ++value2; + fprintf( stderr, "thread %s incremented value2 %d\n", ss.str().c_str(), value2); + boost::this_tasklet::yield(); + ++value2; + fprintf( stderr, "thread %s incremented value2 %d\n", ss.str().c_str(), value2); + boost::this_tasklet::yield(); +} + +void waiting_fn( boost::tasklets::shared_future< std::string > fu) +{ + std::stringstream ss; + ss << boost::this_thread::get_id(); + + fprintf( stderr, "thread %s waits for future\n", ss.str().c_str() ); + std::string result = fu.get(); + fprintf( stderr, "thread %s got string %s from future\n", ss.str().c_str(), result.c_str() ); +} + +void thread_fn1( boost::tasklets::shared_future< std::string > fu) +{ + std::stringstream ss; + ss << boost::this_thread::get_id(); + + fprintf( stderr, "thread %s started\n", ss.str().c_str() ); + + boost::tasklets::scheduler<> sched; + + sched.submit_tasklet( + boost::tasklet( + & increment_fn1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + sched.submit_tasklet( + boost::tasklet( + & waiting_fn, fu, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + fprintf( stderr, "thread %s finished\n", ss.str().c_str() ); +} + +void thread_fn2( callable ca) +{ + std::stringstream ss; + ss << boost::this_thread::get_id(); + + fprintf( stderr, "thread %s started\n", ss.str().c_str() ); + + boost::tasklets::scheduler<> sched; + + sched.submit_tasklet( + boost::tasklet( + & increment_fn2, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + sched.submit_tasklet( + boost::tasklet( + ca, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + fprintf( stderr, "thread %s finished\n", ss.str().c_str() ); +} + +int main() +{ + try + { + boost::tasklets::packaged_task< std::string > pt( helloworld_fn); + boost::tasklets::unique_future< std::string > fu = pt.get_future(); + + boost::thread th1( boost::bind( & thread_fn1, boost::tasklets::shared_future< std::string >( fu) ) ); + boost::thread th2( boost::bind( & thread_fn2, callable( pt) ) ); + + th1.join(); + th2.join(); + + return EXIT_SUCCESS; + } + catch ( boost::tasklets::scheduler_error const& e) + { std::cerr << "scheduler_error: " << e.what() << std::endl; } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/tasklet/examples/interrupt.cpp b/libs/tasklet/examples/interrupt.cpp new file mode 100644 index 00000000..cef5ea6c --- /dev/null +++ b/libs/tasklet/examples/interrupt.cpp @@ -0,0 +1,124 @@ +#include +#include +#include + +#include + +#include + +int value1 = 0; +int value2 = 0; +int value3 = 0; + +void fn_1() +{ + for ( int i = 0; i < 10; ++i) + { + ++value1; + std::cout << "fn_1() increment value1 " << value1 << std::endl; + boost::this_tasklet::interruption_point(); + boost::this_tasklet::yield(); + } +} + +void fn_2() +{ + boost::this_tasklet::disable_interruption disabled; + for ( int i = 0; i < 10; ++i) + { + ++value2; + std::cout << "fn_2() increment value2 " << value2 << std::endl; + boost::this_tasklet::interruption_point(); + boost::this_tasklet::yield(); + } +} + +void fn_3() +{ + boost::this_tasklet::disable_interruption disabled; + for ( int i = 0; i < 10; ++i) + { + ++value3; + std::cout << "fn_3() increment value3 " << value3 << std::endl; + boost::this_tasklet::restore_interruption restored( disabled); + boost::this_tasklet::interruption_point(); + boost::this_tasklet::yield(); + } +} + +void fn_4( boost::tasklet f) +{ + for ( int i = 0; i < 10; ++i) + { + if ( i == 1) + { + std::cout << "fn_4() interrupt tasklet " << f.get_id() << std::endl; + f.interrupt(); + break; + } + boost::this_tasklet::yield(); + } +} + +int main() +{ + try + { + boost::tasklets::scheduler<> sched; + + boost::tasklet f1( fn_1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( f1); + sched.submit_tasklet( + boost::tasklet( + fn_4, f1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + std::cout << "start: interrupt fn_1()" << std::endl; + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + std::cout << "finish: value1 == " << value1 << std::endl; + + boost::tasklet f2( fn_2, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( f2); + sched.submit_tasklet( + boost::tasklet( + fn_4, f2, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + std::cout << "start: interrupt fn_2()" << std::endl; + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + std::cout << "finish: value2 == " << value2 << std::endl; + + boost::tasklet f3( fn_3, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( f3); + sched.submit_tasklet( + boost::tasklet( + fn_4, f3, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + std::cout << "start: interrupt fn_3()" << std::endl; + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + std::cout << "finish: value3 == " << value3 << std::endl; + + return EXIT_SUCCESS; + } + catch ( boost::tasklets::scheduler_error const& e) + { std::cerr << "scheduler_error: " << e.what() << std::endl; } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/tasklet/examples/join.cpp b/libs/tasklet/examples/join.cpp new file mode 100644 index 00000000..64ccb5dd --- /dev/null +++ b/libs/tasklet/examples/join.cpp @@ -0,0 +1,70 @@ +#include +#include +#include + +#include + +#include + +int value1 = 0; +int value2 = 0; + +void fn_1() +{ + for ( int i = 0; i < 5; ++i) + { + ++value1; + std::cout << "fn_1() increment value1 " << value1 << std::endl; + boost::this_tasklet::yield(); + } +} + +void fn_2( boost::tasklet f) +{ + for ( int i = 0; i < 5; ++i) + { + ++value2; + std::cout << "fn_2() increment value2 " << value2 << std::endl; + if ( i == 1) + { + std::cout << "fn_2() join tasklet " << f.get_id() << std::endl; + f.join(); + std::cout << "fn_2() tasklet " << f.get_id() << " joined" << std::endl; + } + boost::this_tasklet::yield(); + } +} + +int main() +{ + try + { + boost::tasklets::scheduler<> sched; + + boost::tasklet f( fn_1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( f); + sched.submit_tasklet( + boost::tasklet( + fn_2, f, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + std::cout << "start" << std::endl; + std::cout << "tasklet to be joined " << f.get_id() << std::endl; + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + std::cout << "finish: value1 == " << value1 << ", value2 == " << value2 << std::endl; + + return EXIT_SUCCESS; + } + catch ( boost::tasklets::scheduler_error const& e) + { std::cerr << "scheduler_error: " << e.what() << std::endl; } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/tasklet/examples/migrate_mt.cpp b/libs/tasklet/examples/migrate_mt.cpp new file mode 100644 index 00000000..8fff637c --- /dev/null +++ b/libs/tasklet/examples/migrate_mt.cpp @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +void g( std::string const& str, int n) +{ + for ( int i = 0; i < n; ++i) + { + std::ostringstream os1; + std::ostringstream os2; + os1 << boost::this_thread::get_id(); + os2 << boost::this_tasklet::get_id(); + fprintf( stderr, "(thread: %s, tasklet: %s) %d: %s\n", os1.str().c_str(), os2.str().c_str(), i, str.c_str() ); + boost::this_tasklet::yield(); + } +} + +void fn1( + boost::tasklet & t, + boost::barrier & b, + boost::tasklets::scheduler<> & sched2, + std::string const& msg, int n) +{ + std::ostringstream os; + os << boost::this_thread::get_id(); + fprintf( stderr, "start (thread1: %s)\n", os.str().c_str() ); + + boost::tasklets::scheduler<> sched1; + sched1.submit_tasklet( + boost::tasklet( + & g, msg, n, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + for ( int i = 0; i < 2; ++i) + sched1.run(); + + b.wait(); + + sched2.migrate_tasklet( t); + + b.wait(); + + std::ostringstream id_os; + id_os << t.get_id(); + + fprintf( stderr, "thread1: tasklet %s migrated\n", id_os.str().c_str() ); + fprintf( stderr, "thread1: scheduler runs %d tasklets\n", static_cast< int >( sched1.size() ) ); + + for (;;) + { + while ( sched1.run() ); + if ( sched1.empty() ) break; + } + + fprintf( stderr, "finish (thread1: %s)\n", os.str().c_str() ); +} + +void fn2( + boost::tasklet & f, + boost::barrier & b, + boost::tasklets::scheduler<> & sched) +{ + std::ostringstream os; + os << boost::this_thread::get_id(); + fprintf( stderr, "start (thread2: %s)\n", os.str().c_str() ); + + sched.submit_tasklet( f); + + sched.run(); + sched.run(); + + b.wait(); + b.wait(); + + fprintf( stderr, "thread2: scheduler runs %d tasklets\n", static_cast< int >( sched.size() ) ); + + fprintf( stderr, "finish (thread2: %s)\n", os.str().c_str() ); +} + +int main() +{ + try + { + boost::tasklets::scheduler<> sched; + boost::barrier b( 2); + + boost::tasklet f( & g, "xyz", 4, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + + std::cout << "start" << std::endl; + + boost::thread th1( + fn1, + f, + boost::ref( b), + boost::ref( sched), + "abc", 5); + boost::thread th2( + fn2, + f, + boost::ref( b), + boost::ref( sched) ); + + th1.join(); + th2.join(); + + std::cout << "finish" << std::endl; + + return EXIT_SUCCESS; + } + catch ( boost::tasklets::scheduler_error const& e) + { std::cerr << "scheduler_error: " << e.what() << std::endl; } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/tasklet/examples/ping_pong.cpp b/libs/tasklet/examples/ping_pong.cpp new file mode 100644 index 00000000..4a98b23c --- /dev/null +++ b/libs/tasklet/examples/ping_pong.cpp @@ -0,0 +1,107 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include + +typedef boost::tasklets::unbounded_channel< std::string > fifo_t; +typedef boost::intrusive_ptr< fifo_t > fifo_ptr; + +inline +void ping( + fifo_t & recv_buf, + fifo_t & send_buf) +{ + boost::optional< std::string > value; + + send_buf.put("ping"); + BOOST_ASSERT( recv_buf.take( value) ); + std::cout << * value << std::endl; + value.reset(); + + send_buf.put("ping"); + BOOST_ASSERT( recv_buf.take( value) ); + std::cout << * value << std::endl; + value.reset(); + + send_buf.put("ping"); + BOOST_ASSERT( recv_buf.take( value) ); + std::cout << * value << std::endl; + value.reset(); + + send_buf.deactivate(); +} + +inline +void pong( + fifo_t & recv_buf, + fifo_t & send_buf) +{ + boost::optional< std::string > value; + + BOOST_ASSERT( recv_buf.take( value) ); + std::cout << * value << std::endl; + value.reset(); + send_buf.put("pong"); + + BOOST_ASSERT( recv_buf.take( value) ); + std::cout << * value << std::endl; + value.reset(); + send_buf.put("pong"); + + BOOST_ASSERT( recv_buf.take( value) ); + std::cout << * value << std::endl; + value.reset(); + send_buf.put("pong"); + + send_buf.deactivate(); +} + +void f( boost::tasklets::scheduler<> & sched) +{ + fifo_t buf1; + fifo_t buf2; + + sched.submit_tasklet( + boost::tasklet( + & ping, boost::ref( buf1), boost::ref( buf2), boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + sched.submit_tasklet( + boost::tasklet( + & pong, boost::ref( buf2), boost::ref( buf1), boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); +} + +int main() +{ + try + { + boost::tasklets::scheduler<> sched; + + sched.submit_tasklet( + boost::tasklet( + & f, boost::ref( sched), boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + std::cout << "start" << std::endl; + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + std::cout << "finish" << std::endl; + + return EXIT_SUCCESS; + } + catch ( boost::tasklets::scheduler_error const& e) + { std::cerr << "scheduler_error: " << e.what() << std::endl; } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/tasklet/examples/ping_pong_mt.cpp b/libs/tasklet/examples/ping_pong_mt.cpp new file mode 100644 index 00000000..965c48b4 --- /dev/null +++ b/libs/tasklet/examples/ping_pong_mt.cpp @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include + +#include + +typedef boost::tasklets::unbounded_channel< std::string > fifo_t; + +inline +void ping( + fifo_t & recv_buf, + fifo_t & send_buf) +{ + std::stringstream tss; + tss << boost::this_thread::get_id(); + std::stringstream fss; + fss << boost::this_tasklet::get_id(); + + fprintf( stderr, "start ping tasklet %s in thread %s\n", fss.str().c_str(), tss.str().c_str() ); + + boost::optional< std::string > value; + + send_buf.put("ping"); + BOOST_ASSERT( recv_buf.take( value) ); + fprintf( stderr, "ping tasklet %s in thread %s recevied %s\n", fss.str().c_str(), tss.str().c_str(), value->c_str()); + value.reset(); + + send_buf.put("ping"); + BOOST_ASSERT( recv_buf.take( value) ); + fprintf( stderr, "ping tasklet %s in thread %s recevied %s\n", fss.str().c_str(), tss.str().c_str(), value->c_str()); + value.reset(); + + send_buf.put("ping"); + BOOST_ASSERT( recv_buf.take( value) ); + fprintf( stderr, "ping tasklet %s in thread %s recevied %s\n", fss.str().c_str(), tss.str().c_str(), value->c_str()); + value.reset(); + + send_buf.deactivate(); + + fprintf( stderr, "end ping tasklet %s in thread %s\n", fss.str().c_str(), tss.str().c_str() ); +} + +inline +void pong( + fifo_t & recv_buf, + fifo_t & send_buf) +{ + std::stringstream tss; + tss << boost::this_thread::get_id(); + std::stringstream fss; + fss << boost::this_tasklet::get_id(); + + fprintf( stderr, "start pong tasklet %s in thread %s\n", fss.str().c_str(), tss.str().c_str() ); + + boost::optional< std::string > value; + + BOOST_ASSERT( recv_buf.take( value) ); + fprintf( stderr, "pong tasklet %s in thread %s recevied %s\n", fss.str().c_str(), tss.str().c_str(), value->c_str()); + value.reset(); + send_buf.put("pong"); + + BOOST_ASSERT( recv_buf.take( value) ); + fprintf( stderr, "pong tasklet %s in thread %s recevied %s\n", fss.str().c_str(), tss.str().c_str(), value->c_str()); + value.reset(); + send_buf.put("pong"); + + BOOST_ASSERT( recv_buf.take( value) ); + fprintf( stderr, "pong tasklet %s in thread %s recevied %s\n", fss.str().c_str(), tss.str().c_str(), value->c_str()); + value.reset(); + send_buf.put("pong"); + + send_buf.deactivate(); + + fprintf( stderr, "end pong tasklet %s in thread %s\n", fss.str().c_str(), tss.str().c_str() ); +} + +void f( + fifo_t & recv_buf, + fifo_t & send_buf) +{ + std::stringstream tss; + tss << boost::this_thread::get_id(); + + fprintf( stderr, "start thread %s\n", tss.str().c_str() ); + + boost::tasklets::scheduler<> sched; + + sched.submit_tasklet( + boost::tasklet( + & ping, boost::ref( recv_buf), boost::ref( send_buf), boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + fprintf( stderr, "end thread %s\n", tss.str().c_str() ); +} + +void g( + fifo_t & recv_buf, + fifo_t & send_buf) +{ + std::stringstream tss; + tss << boost::this_thread::get_id(); + + fprintf( stderr, "start thread %s\n", tss.str().c_str() ); + + boost::tasklets::scheduler<> sched; + + sched.submit_tasklet( + boost::tasklet( + & pong, boost::ref( recv_buf), boost::ref( send_buf), boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + fprintf( stderr, "end thread %s\n", tss.str().c_str() ); +} + +int main() +{ + try + { + fifo_t buf1; + fifo_t buf2; + + std::cout << "start" << std::endl; + + boost::thread th1( boost::bind( & f, boost::ref( buf1), boost::ref( buf2) ) ); + boost::thread th2( boost::bind( & g, boost::ref( buf2), boost::ref( buf1) ) ); + + th1.join(); + th2.join(); + + std::cout << "finish" << std::endl; + + return EXIT_SUCCESS; + } + catch ( boost::tasklets::scheduler_error const& e) + { std::cerr << "scheduler_error: " << e.what() << std::endl; } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/tasklet/examples/simple.cpp b/libs/tasklet/examples/simple.cpp new file mode 100644 index 00000000..02ad1c83 --- /dev/null +++ b/libs/tasklet/examples/simple.cpp @@ -0,0 +1,50 @@ +#include +#include +#include + +#include + +#include + +inline +void fn( std::string const& str, int n) +{ + for ( int i = 0; i < n; ++i) + { + std::cout << i << ": " << str << std::endl; + boost::this_tasklet::yield(); + } +} + +int main() +{ + try + { + boost::tasklets::scheduler<> sched; + + boost::tasklet f( fn, "abc", 5, boost::tasklet::default_stacksize, boost::protected_stack_allocator() ); + sched.submit_tasklet( boost::move( f) ); + sched.submit_tasklet( + boost::tasklet( + & fn, "xyz", 7, boost::tasklet::default_stacksize, boost::protected_stack_allocator() ) ); + + std::cout << "start" << std::endl; + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + std::cout << "finish" << std::endl; + + return EXIT_SUCCESS; + } + catch ( boost::tasklets::scheduler_error const& e) + { std::cerr << "scheduler_error: " << e.what() << std::endl; } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/tasklet/performance/Jamfile.v2 b/libs/tasklet/performance/Jamfile.v2 new file mode 100644 index 00000000..7a5496b8 --- /dev/null +++ b/libs/tasklet/performance/Jamfile.v2 @@ -0,0 +1,18 @@ +# Boost.Tasklet Library Examples Jamfile + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For more information, see http://www.boost.org/ + +project boost/tasklet/example + : requirements + /boost/program_options//boost_program_options + ../build//boost_tasklet + static + multi + ; + +exe performance : performance.cpp ; diff --git a/libs/tasklet/performance/bind_processor.hpp b/libs/tasklet/performance/bind_processor.hpp new file mode 100644 index 00000000..2ec3e310 --- /dev/null +++ b/libs/tasklet/performance/bind_processor.hpp @@ -0,0 +1,28 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef _BIND_PROCESSOR_H +#define _BIND_PROCESSOR_H + +# if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# include "bind_processor_windows.hpp" +# elif defined(linux) || defined(__linux) || defined(__linux__) +# include "bind_processor_linux.hpp" +# elif defined(__IBMCPP__) || defined(_AIX) +# include "bind_processor_aix.hpp" +# elif defined(__hpux) +# include "bind_processor_hpux.hpp" +# elif defined(sun) || defined(__sun) +# include "bind_processor_solaris.hpp" +# elif defined(__FreeBSD__) +#include +# if (__FreeBSD_version ">= 701000") +# include "bind_processor_freebsd.hpp" +# endif +# error "platform not supported" +# endif + +#endif // _BIND_PROCESSOR_H diff --git a/libs/tasklet/performance/bind_processor_aix.hpp b/libs/tasklet/performance/bind_processor_aix.hpp new file mode 100644 index 00000000..9672b6e0 --- /dev/null +++ b/libs/tasklet/performance/bind_processor_aix.hpp @@ -0,0 +1,37 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef _BIND_PROCESSOR_AIX_H +#define _BIND_PROCESSOR_AIX_H + +extern "C" +{ +#include +#include +} + +#include +#include +#include + +#include + +inline +void bind_to_processor( unsigned int n) +{ + BOOST_ASSERT( n >= 0); + BOOST_ASSERT( n < boost::thread::hardware_concurrency() ); + + if ( ::bindprocessor( BINDTHREAD, ::thread_self(), static_cast< cpu_t >( n) ) == -1) + throw boost::system::system_error( + boost::system::error_code( + errno, + boost::system::system_category) ); +} + +#include + +#endif // _BIND_PROCESSOR_AIX_H diff --git a/libs/tasklet/performance/bind_processor_freebsd.hpp b/libs/tasklet/performance/bind_processor_freebsd.hpp new file mode 100644 index 00000000..11d15b9f --- /dev/null +++ b/libs/tasklet/performance/bind_processor_freebsd.hpp @@ -0,0 +1,41 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef _BIND_PROCESSOR_FREEBSD_H +#define _BIND_PROCESSOR_FREEBSD_H + +extern "C" +{ +#include +#include +} + +#include +#include +#include + +#include + +inline +void bind_to_processor( unsigned int n) +{ + BOOST_ASSERT( n >= 0); + BOOST_ASSERT( n < boost::thread::hardware_concurrency() ); + + cpuset_t cpuset; + CPU_ZERO( & cpuset); + CPU_SET( n, & cpuset); + + if ( ::cpuset_setaffinity( CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof( cpuset), & cpuset) == -1) + throw boost::system::system_error( + boost::system::error_code( + errno, + boost::system::system_category) ); +} + +#include + +#endif // _BIND_PROCESSOR_FREEBSD_H diff --git a/libs/tasklet/performance/bind_processor_hpux.hpp b/libs/tasklet/performance/bind_processor_hpux.hpp new file mode 100644 index 00000000..ea4389df --- /dev/null +++ b/libs/tasklet/performance/bind_processor_hpux.hpp @@ -0,0 +1,43 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef _BIND_PROCESSOR_HPUX_H +#define _BIND_PROCESSOR_HPUX_H + +extern "C" +{ +#include +} + +#include +#include +#include + +#include + +inline +void bind_to_processor( unsigned int n) +{ + BOOST_ASSERT( n >= 0); + BOOST_ASSERT( n < boost::thread::hardware_concurrency() ); + + ::pthread_spu_t spu; + int errno_( + ::pthread_processor_bind_np( + PTHREAD_BIND_FORCED_NP, + & spu, + static_cast< pthread_spu_t >( n), + PTHREAD_SELFTID_NP) ); + if ( errno_ != 0) + throw boost::system::system_error( + boost::system::error_code( + errno_, + boost::system::system_category) ); +} + +#include + +#endif // _BIND_PROCESSOR_HPUX_H diff --git a/libs/tasklet/performance/bind_processor_linux.hpp b/libs/tasklet/performance/bind_processor_linux.hpp new file mode 100644 index 00000000..f5a3baba --- /dev/null +++ b/libs/tasklet/performance/bind_processor_linux.hpp @@ -0,0 +1,43 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef _BIND_PRCESSOR_LINUX_H +#define _BIND_PRCESSOR_LINUX_H + +extern "C" +{ +#include +#include +} + +#include +#include +#include + +#include + +inline +void bind_to_processor( unsigned int n) +{ + BOOST_ASSERT( n >= 0); + BOOST_ASSERT( n < CPU_SETSIZE); + BOOST_ASSERT( n < boost::thread::hardware_concurrency() ); + + cpu_set_t cpuset; + CPU_ZERO( & cpuset); + CPU_SET( n, & cpuset); + + int errno_( ::pthread_setaffinity_np( ::pthread_self(), sizeof( cpuset), & cpuset) ); + if ( errno_ != 0) + throw boost::system::system_error( + boost::system::error_code( + errno_, + boost::system::system_category) ); +} + +#include + +#endif // _BIND_PRCESSOR_LINUX_H diff --git a/libs/tasklet/performance/bind_processor_solaris.hpp b/libs/tasklet/performance/bind_processor_solaris.hpp new file mode 100644 index 00000000..25c88b09 --- /dev/null +++ b/libs/tasklet/performance/bind_processor_solaris.hpp @@ -0,0 +1,38 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef _BIND_PROCESSOR_SOLARIS_H +#define _BIND_PROCESSOR_SOLARIS_H + +extern "C" +{ +#include +#include +#include +} + +#include +#include +#include + +#include + +inline +void bind_to_processor( unsigned int n) +{ + BOOST_ASSERT( n >= 0); + BOOST_ASSERT( n < boost::thread::hardware_concurrency() ); + + if ( ::processor_bind( P_LWPID, P_MYID, static_cast< processorid_t >( n), 0) == -1) + throw boost::system::system_error( + boost::system::error_code( + errno, + boost::system::system_category) ); +} + +#include + +#endif // _BIND_PROCESSOR_SOLARIS_H diff --git a/libs/tasklet/performance/bind_processor_windows.hpp b/libs/tasklet/performance/bind_processor_windows.hpp new file mode 100644 index 00000000..d50a31ac --- /dev/null +++ b/libs/tasklet/performance/bind_processor_windows.hpp @@ -0,0 +1,36 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef _BIND_PROCESSOR_WINDOWS_H +#define _BIND_PROCESSOR_WINDOWS_H + +extern "C" +{ +#include +} + +#include +#include +#include + +#include + +inline +void bind_to_processor( unsigned int n) +{ + BOOST_ASSERT( n >= 0); + BOOST_ASSERT( n < boost::thread::hardware_concurrency() ); + + if ( ::SetThreadAffinityMask( ::GetCurrentThread(), ( DWORD_PTR)1 << n) == 0) + throw boost::system::system_error( + boost::system::error_code( + ::GetLastError(), + boost::system::system_category) ); +} + +#include + +#endif // _BIND_PROCESSOR_WINDOWS_H diff --git a/libs/tasklet/performance/performance.cpp b/libs/tasklet/performance/performance.cpp new file mode 100644 index 00000000..f2bec7d8 --- /dev/null +++ b/libs/tasklet/performance/performance.cpp @@ -0,0 +1,125 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include + +#include "bind_processor.hpp" +#include "performance.hpp" + +namespace po = boost::program_options; +namespace pt = boost::posix_time; + +volatile int value = 0; + +void fn( int i) +{ value = i; } + +void test_creation( unsigned int iterations) +{ + int value( 3); + long total( 0); + cycle_t overhead( get_overhead() ); + std::cout << "overhead for rdtsc == " << overhead << " cycles" << std::endl; + + boost::tasklets::scheduler<> sched; + + // cache warm-up + sched.make_tasklet( fn, value, boost::tasklet::default_stacksize); + + for ( unsigned int i = 0; i < iterations; ++i) + { + cycle_t start( get_cycles() ); + sched.make_tasklet( fn, value, boost::tasklet::default_stacksize); + cycle_t diff( get_cycles() - start); + diff -= overhead; + BOOST_ASSERT( diff >= 0); + total += diff; + } + std::cout << "average of " << total/iterations << " cycles per creation" << std::endl; +} + +void test_switching( unsigned int iterations) +{ + int value( 3); + + boost::tasklets::scheduler<> sched; + + // cache warm-up + sched.make_tasklet( fn, value, boost::tasklet::default_stacksize); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + for ( unsigned int i = 0; i < iterations; ++i) + sched.make_tasklet( fn, value, boost::tasklet::default_stacksize); + + cycle_t overhead( get_overhead() ); + std::cout << "overhead for rdtsc == " << overhead << " cycles" << std::endl; + + cycle_t start( get_cycles() ); + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + cycle_t total( get_cycles() - start); + total -= overhead * iterations; + BOOST_ASSERT( total >= 0); + std::cout << "average of " << total/iterations << " cycles per switch" << std::endl; +} + +int main( int argc, char * argv[]) +{ + try + { + unsigned int iterations( 0); + + po::options_description desc("allowed options"); + desc.add_options() + ("help,h", "help message") + ("creating,c", "test creation") + ("switching,s", "test switching") + ("iterations,i", po::value< unsigned int >( & iterations), "iterations"); + + po::variables_map vm; + po::store( + po::parse_command_line( + argc, + argv, + desc), + vm); + po::notify( vm); + + if ( vm.count("help") ) + { + std::cout << desc << std::endl; + return EXIT_SUCCESS; + } + + if ( 0 == iterations) throw std::invalid_argument("iterations must be greater than zero"); + + bind_to_processor( 0); + + if ( vm.count("creating") ) test_creation( iterations); + + if ( vm.count("switching") ) test_switching( iterations); + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/libs/tasklet/performance/performance.hpp b/libs/tasklet/performance/performance.hpp new file mode 100644 index 00000000..5f45c955 --- /dev/null +++ b/libs/tasklet/performance/performance.hpp @@ -0,0 +1,26 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef PERFORMANCE_H +#define PERFORMANCE_H + +// msvc/icc, i386 +#if defined(_MSC_VER) && defined(_M_IX86) +#include "performance_msvc_i386.hpp" + +// gcc/icc, i386 +#elif defined(__GNUC__) && defined(__i386__) +#include "performance_gcc_i386.hpp" + +// gcc/icc, x86_64 +#elif defined(__GNUC__) && defined(__x86_64__) +#include "performance_gcc_x86-64.hpp" + +#else +#error "this platform is not supported" +#endif + +#endif // PERFORMANCE_H diff --git a/libs/tasklet/performance/performance_gcc_i386.hpp b/libs/tasklet/performance/performance_gcc_i386.hpp new file mode 100644 index 00000000..495bfa5f --- /dev/null +++ b/libs/tasklet/performance/performance_gcc_i386.hpp @@ -0,0 +1,62 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef PERFORMANCE_GCC_I386_H +#define PERFORMANCE_GCC_I386_H + +#include +#include +#include + +#include +#include +#include + +typedef uint64_t cycle_t; + +inline +cycle_t get_cycles() +{ + uint32_t res[2]; + + __asm__ __volatile__ ( + "xorl %%eax, %%eax\n" + "cpuid\n" + ::: "%eax", "%ebx", "%ecx", "%edx" + ); + __asm__ __volatile__ ("rdtsc" : "=a" (res[0]), "=d" (res[1]) ); + __asm__ __volatile__ ( + "xorl %%eax, %%eax\n" + "cpuid\n" + ::: "%eax", "%ebx", "%ecx", "%edx" + ); + + return * ( cycle_t *)res; +} + +struct measure +{ + cycle_t operator()() + { + cycle_t start( get_cycles() ); + return get_cycles() - start; + } +}; + +inline +cycle_t get_overhead() +{ + std::size_t iterations( 10); + std::vector< cycle_t > overhead( iterations, 0); + for ( std::size_t i( 0); i < iterations; ++i) + std::generate( + overhead.begin(), overhead.end(), + measure() ); + BOOST_ASSERT( overhead.begin() != overhead.end() ); + return * std::min_element( overhead.begin(), overhead.end() ); +} + +#endif // PERFORMANCE_GCC_I386_H diff --git a/libs/tasklet/performance/performance_gcc_x86-64.hpp b/libs/tasklet/performance/performance_gcc_x86-64.hpp new file mode 100644 index 00000000..d68f726b --- /dev/null +++ b/libs/tasklet/performance/performance_gcc_x86-64.hpp @@ -0,0 +1,62 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef PERFORMANCE_GCC_X86_64_H +#define PERFORMANCE_GCC_X86_64_H + +#include +#include +#include + +#include +#include +#include + +typedef uint64_t cycle_t; + +inline +cycle_t get_cycles() +{ + uint32_t res[2]; + + __asm__ __volatile__ ( + "xorl %%eax, %%eax\n" + "cpuid\n" + ::: "%rax", "%rbx", "%rcx", "%rdx" + ); + __asm__ __volatile__ ("rdtsc" : "=a" (res[0]), "=d" (res[1]) ); + __asm__ __volatile__ ( + "xorl %%eax, %%eax\n" + "cpuid\n" + ::: "%rax", "%rbx", "%rcx", "%rdx" + ); + + return * ( cycle_t *)res; +} + +struct measure +{ + cycle_t operator()() + { + cycle_t start( get_cycles() ); + return get_cycles() - start; + } +}; + +inline +cycle_t get_overhead() +{ + std::size_t iterations( 10); + std::vector< cycle_t > overhead( iterations, 0); + for ( std::size_t i( 0); i < iterations; ++i) + std::generate( + overhead.begin(), overhead.end(), + measure() ); + BOOST_ASSERT( overhead.begin() != overhead.end() ); + return * std::min_element( overhead.begin(), overhead.end() ); +} + +#endif // PERFORMANCE_GCC_X86_64_H diff --git a/libs/tasklet/performance/performance_msvc_i386.hpp b/libs/tasklet/performance/performance_msvc_i386.hpp new file mode 100644 index 00000000..cbbec734 --- /dev/null +++ b/libs/tasklet/performance/performance_msvc_i386.hpp @@ -0,0 +1,60 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef PERFORMANCE_MSVC_X86_64_H +#define PERFORMANCE_MSVC_X86_64_H + +#include +#include +#include + +#include +#include +#include + +typedef uint64_t cycle_t; + +inline +cycle_t get_cycles() +{ + uint32_t res[2] + + __asm { + xor eax, eax + cpuid + rdtsc + mov dword ptr res[0], eax + mov dword ptr res[1], edx + xor eax, eax + cpuid + }; + + return * ( cycle_t *)res; +} + +struct measure +{ + cycle_t operator()() + { + cycle_t start( get_cycles() ); + return get_cycles() - start; + } +}; + +inline +cycle_t get_overhead() +{ + std::size_t iterations( 10); + std::vector< cycle_t > overhead( iterations, 0); + for ( std::size_t i( 0); i < iterations; ++i) + std::generate( + overhead.begin(), overhead.end(), + measure() ); + BOOST_ASSERT( overhead.begin() != overhead.end() ); + return * std::min_element( overhead.begin(), overhead.end() ); +} + +#endif // PERFORMANCE_MSVC_X86_64_H diff --git a/libs/tasklet/src/auto_reset_event.cpp b/libs/tasklet/src/auto_reset_event.cpp new file mode 100644 index 00000000..2969fc5c --- /dev/null +++ b/libs/tasklet/src/auto_reset_event.cpp @@ -0,0 +1,47 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_TASKLET_SOURCE + +#include "boost/tasklet/auto_reset_event.hpp" + +#include + +#include + +namespace boost { +namespace tasklets { + +auto_reset_event::auto_reset_event( bool isset) : + state_( isset ? SET : RESET) +{} + +void +auto_reset_event::set() +{ state_.store( SET); } + +void +auto_reset_event::wait() +{ + state expected = SET; + while ( ! state_.compare_exchange_strong( expected, RESET) ) + { + if ( this_tasklet::runs_as_tasklet() ) + this_tasklet::yield(); + else + this_thread::yield(); + expected = SET; + } +} + +bool +auto_reset_event::try_wait() +{ + state expected = SET; + return state_.compare_exchange_strong( expected, RESET); +} + +}} diff --git a/libs/tasklet/src/barrier.cpp b/libs/tasklet/src/barrier.cpp new file mode 100644 index 00000000..a2036ae9 --- /dev/null +++ b/libs/tasklet/src/barrier.cpp @@ -0,0 +1,44 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_TASKLET_SOURCE + +#include "boost/tasklet/barrier.hpp" + +#include + +namespace boost { +namespace tasklets { + +barrier::barrier( std::size_t initial) : + initial_( initial), + current_( initial_), + cycle_( true), + mtx_(), + cond_() +{ if ( initial == 0) throw std::invalid_argument("invalid barrier count"); } + +bool +barrier::wait() +{ + mutex::scoped_lock lk( mtx_); + bool cycle( cycle_); + if ( 0 == --current_) + { + cycle_ = ! cycle_; + current_ = initial_; + cond_.notify_all(); + return true; + } + else + { + while ( cycle == cycle_) + cond_.wait( lk); + } + return false; +} + +}} diff --git a/libs/tasklet/src/condition.cpp b/libs/tasklet/src/condition.cpp new file mode 100644 index 00000000..07248dc8 --- /dev/null +++ b/libs/tasklet/src/condition.cpp @@ -0,0 +1,92 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_TASKLET_SOURCE + +#include "boost/tasklet/condition.hpp" + +#include + +namespace boost { +namespace tasklets { + +condition::condition() : + oid_( this), + waiting_tasklets_(), + oidx_( waiting_tasklets_.get< ordered_idx_tag >() ), + sidx_( waiting_tasklets_.get< sequenced_idx_tag >() ), + cmd_( SLEEPING), + waiters_( 0), + enter_mtx_(), + check_mtx_(), + mtx_() +{} + +condition::~condition() +{ + BOOST_ASSERT( waiting_tasklets_.empty() ); + BOOST_ASSERT( 0 == waiters_.load() ); +} + +void +condition::notify_one() +{ + enter_mtx_.lock(); + + if ( 0 == waiters_.load() ) + { + enter_mtx_.unlock(); + return; + } + + command expected = SLEEPING; + while ( ! cmd_.compare_exchange_strong( expected, NOTIFY_ONE) ) + { + if ( this_tasklet::runs_as_tasklet() ) + this_tasklet::yield(); + else + this_thread::yield(); + expected = SLEEPING; + } + + spin_mutex::scoped_lock lk( mtx_); + tasklet f( * sidx_.begin() ); + sidx_.pop_front(); + BOOST_ASSERT( f.impl_->attached_strategy() ); + f.impl_->attached_strategy()->object_notify_one( oid_); +} + +void +condition::notify_all() +{ + enter_mtx_.lock(); + + if ( 0 == waiters_.load() ) + { + enter_mtx_.unlock(); + return; + } + + command expected = SLEEPING; + while ( ! cmd_.compare_exchange_strong( expected, NOTIFY_ALL) ) + { + if ( this_tasklet::runs_as_tasklet() ) + this_tasklet::yield(); + else + this_thread::yield(); + expected = SLEEPING; + } + + spin_mutex::scoped_lock lk( mtx_); + BOOST_FOREACH( tasklet f, sidx_) + { + BOOST_ASSERT( f.impl_->attached_strategy() ); + f.impl_->attached_strategy()->object_notify_all( oid_); + } + waiting_tasklets_.clear(); +} + +}} diff --git a/libs/tasklet/src/count_down_event.cpp b/libs/tasklet/src/count_down_event.cpp new file mode 100644 index 00000000..816b7405 --- /dev/null +++ b/libs/tasklet/src/count_down_event.cpp @@ -0,0 +1,61 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_TASKLET_SOURCE + +#include "boost/tasklet/count_down_event.hpp" + +#include + +#include +#include + +namespace boost { +namespace tasklets { + +count_down_event::count_down_event( std::size_t initial) : + initial_( initial), + current_( initial_) +{} + +std::size_t +count_down_event::initial() const +{ return initial_; } + +std::size_t +count_down_event::current() const +{ return current_.load(); } + +bool +count_down_event::is_set() const +{ return 0 == current_.load(); } + +void +count_down_event::set() +{ + for (;;) + { + if ( 0 == current_.load() ) + return; + std::size_t expected = current_.load(); + if ( current_.compare_exchange_strong( expected, expected - 1) ) + return; + } +} + +void +count_down_event::wait() +{ + while ( 0 != current_.load() ) + { + if ( this_tasklet::runs_as_tasklet() ) + this_tasklet::yield(); + else + this_thread::yield(); + } +} + +}} diff --git a/libs/tasklet/src/detail/tasklet_base.cpp b/libs/tasklet/src/detail/tasklet_base.cpp new file mode 100644 index 00000000..341faf17 --- /dev/null +++ b/libs/tasklet/src/detail/tasklet_base.cpp @@ -0,0 +1,75 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_TASKLET_SOURCE + +#include + +#include + +#include +#include + +#include + +namespace boost { +namespace tasklets { +namespace detail { + +BOOST_TASKLET_DECL void trampoline( void * vp) +{ + BOOST_ASSERT( vp); + detail::tasklet_base * self( static_cast< detail::tasklet_base * >( vp) ); + try + { self->exec(); } + catch ( tasklet_interrupted const&) {} + catch (...) {} + this_tasklet::cancel(); +} + +void +tasklet_base::run() +{ fib_.run(); } + +void +tasklet_base::yield() +{ fib_.yield(); } + +int +tasklet_base::priority() const +{ return priority_; } + +void +tasklet_base::priority( int prio) +{ priority_ = prio; } + +state_type +tasklet_base::state() const +{ return state_; } + +void +tasklet_base::state( state_type st) +{ state_ = st; } + +interrupt_type & +tasklet_base::interrupt() +{ return interrupt_; } + +void +tasklet_base::interrupt( interrupt_type intr) +{ interrupt_ = intr; } + +strategy * +tasklet_base::attached_strategy() +{ return st_; } + +void +tasklet_base::attached_strategy( strategy * st) +{ st_ = st; } + +}}} + +#include diff --git a/libs/tasklet/src/manual_reset_event.cpp b/libs/tasklet/src/manual_reset_event.cpp new file mode 100644 index 00000000..9da043ca --- /dev/null +++ b/libs/tasklet/src/manual_reset_event.cpp @@ -0,0 +1,83 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_TASKLET_SOURCE + +#include "boost/tasklet/manual_reset_event.hpp" + +#include +#include + +#include + +namespace boost { +namespace tasklets { + +manual_reset_event::manual_reset_event( bool isset) : + state_( isset ? SET : RESET), + waiters_( 0), + enter_mtx_() +{} + +void +manual_reset_event::set() +{ + enter_mtx_.lock(); + + state expected = RESET; + if ( ! state_.compare_exchange_strong( expected, SET) || + 0 == waiters_.load() ) + enter_mtx_.unlock(); +} + +void +manual_reset_event::reset() +{ + mutex::scoped_lock lk( enter_mtx_); + BOOST_ASSERT( lk); + + state_.store( RESET); +} + +void +manual_reset_event::wait() +{ + { + mutex::scoped_lock lk( enter_mtx_); + BOOST_ASSERT( lk); + waiters_.fetch_add( 1); + } + + while ( RESET == state_.load() ) + { + if ( this_tasklet::runs_as_tasklet() ) + this_tasklet::yield(); + else + this_thread::yield(); + } + + if ( 1 == waiters_.fetch_sub( 1) ) + enter_mtx_.unlock(); +} + +bool +manual_reset_event::try_wait() +{ + { + mutex::scoped_lock lk( enter_mtx_); + BOOST_ASSERT( lk); + waiters_.fetch_add( 1); + } + + bool result = SET == state_.load(); + + if ( 1 == waiters_.fetch_sub( 1) ) + enter_mtx_.unlock(); + + return result; +} + +}} diff --git a/libs/tasklet/src/mutex.cpp b/libs/tasklet/src/mutex.cpp new file mode 100644 index 00000000..6aef604f --- /dev/null +++ b/libs/tasklet/src/mutex.cpp @@ -0,0 +1,83 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_TASKLET_SOURCE + +#include + +#include +#include + +#include + +namespace boost { +namespace tasklets { + +mutex::mutex() : + oid_( this), + state_( UNLOCKED), + mtx_(), + waiting_tasklets_(), + oidx_( waiting_tasklets_.get< ordered_idx_tag >() ), + sidx_( waiting_tasklets_.get< sequenced_idx_tag >() ) +{} + +mutex::~mutex() +{ BOOST_ASSERT( waiting_tasklets_.empty() ); } + +void +mutex::lock() +{ + for (;;) + { + spin_mutex::scoped_lock lk( mtx_); + if ( UNLOCKED == state_) + { + state_ = LOCKED; + // TODO: store identifier of tasklet/thread + lk.unlock(); + break; + } + else if ( this_tasklet::runs_as_tasklet() ) + { + tasklet * f( strategy::active_tasklet); + BOOST_ASSERT( f); + oidx_.insert( * f); + BOOST_ASSERT( f->impl_->attached_strategy() ); + f->impl_->attached_strategy()->wait_for_object( oid_, lk); + } + else + { + lk.unlock(); + this_thread::yield(); + } + } +} + +bool +mutex::try_lock() +{ + spin_mutex::scoped_lock lk( mtx_); + if ( LOCKED == state_) return false; + state_ = LOCKED; + return true; +} + +void +mutex::unlock() +{ + // TODO: only the tasklet/thread locked the mutex + // can call unlock() + spin_mutex::scoped_lock lk( mtx_); + state_ = UNLOCKED; + if ( waiting_tasklets_.empty() ) return; + tasklet f( * sidx_.begin() ); + sidx_.pop_front(); + BOOST_ASSERT( f.impl_->attached_strategy() ); + f.impl_->attached_strategy()->object_notify_one( oid_); +} + +}} diff --git a/libs/tasklet/src/round_robin.cpp b/libs/tasklet/src/round_robin.cpp new file mode 100644 index 00000000..06f24f75 --- /dev/null +++ b/libs/tasklet/src/round_robin.cpp @@ -0,0 +1,478 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_TASKLET_SOURCE + +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +namespace boost { +namespace tasklets { + +round_robin::round_robin() : + mtx_(), + tasklets_(), + objects_(), + runnable_tasklets_(), + terminated_tasklets_() +{} + +void +round_robin::add( tasklet & t) +{ + spin_mutex::scoped_lock lk( mtx_); + + if ( ! t) throw tasklet_moved(); + + BOOST_ASSERT( in_state_not_started( t) ); + + // set state to ready + set_state_ready( t); + + // attach to this scheduler + attach( t); + + // insert tasklet to tasklet-list + std::pair< std::map< tasklet::id, schedulable >::iterator, bool > result( + tasklets_.insert( + std::make_pair( + t.get_id(), + schedulable( t) ) ) ); + + // check result + if ( ! result.second) throw scheduler_error("inserting tasklet failed"); + + // put tasklet to runnable-queue + runnable_tasklets_.push_back( result.first->first); +} + +void +round_robin::join( tasklet & t) +{ + spin_mutex::scoped_lock lk( mtx_); + + BOOST_ASSERT( t); + BOOST_ASSERT( ! in_state_not_started( t) ); + BOOST_ASSERT( active_tasklet); + tasklet_map::iterator i = tasklets_.find( t.get_id() ); + if ( i == tasklets_.end() ) return; + schedulable s( i->second); + + // nothing to do for a terminated tasklet + if ( in_state_terminated( t) ) return; + + // prevent self-join + if ( active_tasklet->get_id() == t.get_id() ) + throw scheduler_error("self-join denied"); + + // register on tasklet to be joined + tasklets_[t.get_id()].joining_tasklets.push_back( active_tasklet->get_id() ); + + // set state waiting + set_state_wait_for_tasklet( * active_tasklet); + + // set tasklet-id waiting-on + tasklets_[active_tasklet->get_id()].waiting_on_tasklet = t.get_id(); + + // switch to master-tasklet + lk.unlock(); + strategy::yield( * active_tasklet); + lk.lock(); + + // tasklet returned + BOOST_ASSERT( in_state_running( * active_tasklet) ); + BOOST_ASSERT( ! tasklets_[active_tasklet->get_id()].waiting_on_tasklet); + + // check if interruption was requested + if ( interruption_enabled( * active_tasklet) ) + throw tasklet_interrupted(); +} + +void +round_robin::interrupt( tasklet & t) +{ + spin_mutex::scoped_lock lk( mtx_); + + BOOST_ASSERT( t); + BOOST_ASSERT( ! in_state_not_started( t) ); + tasklet_map::iterator i = tasklets_.find( t.get_id() ); + if ( i == tasklets_.end() ) return; + schedulable s( i->second); + + // nothing to do for al terminated tasklet + if ( in_state_terminated( t) ) return; + + enable_interruption( t); + + // if tasklet is waiting + if ( in_state_wait_for_tasklet( t) ) + { + // tasklet is waiting (joining) on another tasklet + // remove it from the waiting-queue, reset waiting-on + // and reset the waiting state + BOOST_ASSERT( s.waiting_on_tasklet); + tasklets_[* s.waiting_on_tasklet].joining_tasklets.remove( t.get_id() ); + tasklets_[t.get_id()].waiting_on_tasklet.reset(); + set_state_ready( t); + runnable_tasklets_.push_back( t.get_id() ); + } else if ( in_state_wait_for_object( t) ) { + // tasklet is waiting on an object + // remove it from the waiting-queue, reset waiting-on + // and reset the waiting state + BOOST_ASSERT( s.waiting_on_object); + objects_[* s.waiting_on_object].remove( t.get_id() ); + tasklets_[t.get_id()].waiting_on_object.reset(); + set_state_ready( t); + runnable_tasklets_.push_back( t.get_id()); + } +} + +void +round_robin::reschedule( tasklet & t) +{ + spin_mutex::scoped_lock lk( mtx_); + + BOOST_ASSERT( t); + BOOST_ASSERT( ! in_state_not_started( t) ); + BOOST_ASSERT( ! in_state_terminated( t) ); + tasklet_map::iterator i = tasklets_.find( t.get_id() ); + if ( i == tasklets_.end() ) return; +} + +void +round_robin::cancel( tasklet & t) +{ + spin_mutex::scoped_lock lk( mtx_); + + BOOST_ASSERT( t); + BOOST_ASSERT( ! in_state_not_started( t) ); + BOOST_ASSERT( active_tasklet); + + // nothing to do for al terminated tasklet + if ( in_state_terminated( t) ) return; + + tasklet_map::iterator i( tasklets_.find( t.get_id() ) ); + if ( i == tasklets_.end() ) return; + schedulable s( i->second); + + // invoke each tasklet waiting on this tasklet + BOOST_FOREACH( tasklet::id id__, s.joining_tasklets) + { + schedulable s__( tasklets_[id__]); + tasklet f__( s__.f); + BOOST_ASSERT( s__.waiting_on_tasklet); + BOOST_ASSERT( in_state_wait_for_tasklet( f__) ); + + // clear waiting-on + tasklets_[id__].waiting_on_tasklet.reset(); + + // put tasklet on runnable-queue + set_state_ready( f__); + runnable_tasklets_.push_back( id__); + } + // clear waiting-queue + tasklets_[t.get_id()].joining_tasklets.clear(); + + // if tasklet is ready remove it from the runnable-queue + // and put it to terminated-queue + if ( in_state_ready( t) ) + { + set_state_terminated( t); + runnable_tasklets_.remove( t.get_id() ); + terminated_tasklets_.push( t.get_id() ); + } + // if tasklet is running (== active tasklet) + // reset active tasklet + // put it to terminated-queue and switch + // to master tasklet + else if ( in_state_running( t) ) + { + BOOST_ASSERT( active_tasklet->get_id() == t.get_id() ); + set_state_terminated( t); + terminated_tasklets_.push( t.get_id() ); + lk.unlock(); + strategy::yield( t); + } + // if tasklet is waiting then remove it from the + // waiting-queue and put it to terminated-queue + else if ( in_state_wait_for_tasklet( t) ) + { + BOOST_ASSERT( s.waiting_on_tasklet); + set_state_terminated( t); + tasklets_[* s.waiting_on_tasklet].joining_tasklets.remove( t.get_id() ); + terminated_tasklets_.push( t.get_id() ); + } + else + BOOST_ASSERT( ! "should never reached"); +} + +void +round_robin::yield() +{ + spin_mutex::scoped_lock lk( mtx_); + + BOOST_ASSERT( active_tasklet); + BOOST_ASSERT( in_state_running( * active_tasklet) ); + BOOST_ASSERT( ! tasklets_[active_tasklet->get_id()].waiting_on_tasklet); + + // set state ready + set_state_ready( * active_tasklet); + + // put tasklet to runnable-queue + runnable_tasklets_.push_back( active_tasklet->get_id() ); + + // switch to master-tasklet + lk.unlock(); + strategy::yield( * active_tasklet); +} + +void +round_robin::wait_for_object( object::id const& oid, spin_mutex::scoped_lock & slk) +{ + spin_mutex::scoped_lock lk( mtx_); + + BOOST_ASSERT( active_tasklet); + tasklet::id id = active_tasklet->get_id(); + tasklet_map::iterator i = tasklets_.find( id); + if ( i == tasklets_.end() ) return; + schedulable s( i->second); + tasklet f( s.f); + BOOST_ASSERT( f); + BOOST_ASSERT( active_tasklet->get_id() == id); + BOOST_ASSERT( in_state_running( f) ); + BOOST_ASSERT( ! s.waiting_on_tasklet); + BOOST_ASSERT( ! s.waiting_on_object); + + // register on object to be waiting on + objects_[oid].push_back( id); + + // set state waiting + set_state_wait_for_object( f); + + // set object-id waiting-on + tasklets_[id].waiting_on_object = oid; + + // release lock protecting sync. primitive + slk.unlock(); + + // switch to master-tasklet + lk.unlock(); + strategy::yield( * active_tasklet); + lk.lock(); + + // tasklet returned + BOOST_ASSERT( active_tasklet->get_id() == id); + BOOST_ASSERT( in_state_running( tasklets_[id].f) ); + BOOST_ASSERT( ! tasklets_[id].waiting_on_tasklet); + BOOST_ASSERT( ! tasklets_[id].waiting_on_object); + + // check if interruption was requested + if ( interruption_enabled( f) ) + throw tasklet_interrupted(); +} + +void +round_robin::object_notify_one( object::id const& oid) +{ + spin_mutex::scoped_lock lk( mtx_); + + object_map::iterator oi( objects_.find( oid) ); + BOOST_ASSERT( oi != objects_.end() ); + if ( oi->second.empty() ) return; + tasklet::id id( oi->second.front() ); + oi->second.pop_front(); + + tasklet_map::iterator fi = tasklets_.find( id); + BOOST_ASSERT( fi != tasklets_.end() ); + schedulable s( fi->second); + tasklet f( s.f); + BOOST_ASSERT( f); + BOOST_ASSERT( ! s.waiting_on_tasklet); + BOOST_ASSERT( s.waiting_on_object); + BOOST_ASSERT( * s.waiting_on_object == oid); + + // remove object waiting for + tasklets_[id].waiting_on_object.reset(); + + // set state ready + set_state_ready( f); + + // put tasklet to runnable-queue + runnable_tasklets_.push_back( id); +} + +void +round_robin::object_notify_all( object::id const& oid) +{ + spin_mutex::scoped_lock lk( mtx_); + + object_map::iterator oi( objects_.find( oid) ); + if ( oi == objects_.end() ) + // NOTE: because of previous calls to this function + // all references may be already released + // this can happen by calling condition::notify_all() + return; + tasklet_id_list::iterator fe( oi->second.end() ); + for ( + tasklet_id_list::iterator fi( oi->second.begin() ); + fi != fe; + ++fi) + { + tasklet::id id( * fi); + + tasklet_map::iterator i = tasklets_.find( id); + BOOST_ASSERT( i != tasklets_.end() ); + schedulable s( i->second); + tasklet f( s.f); + BOOST_ASSERT( f); + BOOST_ASSERT( ! s.waiting_on_tasklet); + BOOST_ASSERT( s.waiting_on_object); + BOOST_ASSERT( * s.waiting_on_object == oid); + + // remove object waiting for + tasklets_[id].waiting_on_object.reset(); + + // set state ready + set_state_ready( f); + + // put tasklet to runnable-queue + runnable_tasklets_.push_back( id); + } + objects_.erase( oi); +} + +void +round_robin::release( tasklet & t) +{ + spin_mutex::scoped_lock lk( mtx_); + + BOOST_ASSERT( t); + tasklet_map::iterator fi = tasklets_.find( t.get_id() ); + if ( fi == tasklets_.end() ) + throw scheduler_error("tasklet not managed by scheduler"); + schedulable s( fi->second); + if ( ! in_state_ready( t) || ! s.joining_tasklets.empty() ) + throw tasklet_error("tasklet can not be released"); + BOOST_ASSERT( ! s.waiting_on_tasklet); + BOOST_ASSERT( ! s.waiting_on_object); + + runnable_tasklets_.remove( t.get_id() ); + tasklets_.erase( t.get_id() ); +} + +void +round_robin::migrate( tasklet & t) +{ + spin_mutex::scoped_lock lk( mtx_); + + if ( ! t) throw tasklet_moved(); + + BOOST_ASSERT( in_state_ready( t) ); + + // attach to this scheduler + attach( t); + + // insert tasklet to tasklet-list + std::pair< std::map< tasklet::id, schedulable >::iterator, bool > result( + tasklets_.insert( + std::make_pair( + t.get_id(), + schedulable( t) ) ) ); + + // check result + if ( ! result.second) throw scheduler_error("migrating tasklet failed"); + + // put tasklet to runnable-queue + runnable_tasklets_.push_back( result.first->first); +} + +void +round_robin::detach_all() +{ + spin_mutex::scoped_lock lk( mtx_); + BOOST_FOREACH( tasklet_map::value_type va, tasklets_) + { detach( va.second.f); } +} + +bool +round_robin::run() +{ + spin_mutex::scoped_lock lk( mtx_); + + bool result( false); + if ( ! runnable_tasklets_.empty() ) + { + schedulable s = tasklets_[runnable_tasklets_.front()]; + runnable_tasklets_.pop_front(); + active_tasklet = & s.f; + BOOST_ASSERT( ! s.waiting_on_tasklet); + BOOST_ASSERT( ! s.waiting_on_object); + BOOST_ASSERT( in_state_ready( * active_tasklet) ); + set_state_running( * active_tasklet); + lk.unlock(); + call( * active_tasklet); + lk.lock(); + active_tasklet = 0; + result = true; + } + + while ( ! terminated_tasklets_.empty() ) + { + tasklet_map::iterator i( tasklets_.find( terminated_tasklets_.front() ) ); + BOOST_ASSERT( i != tasklets_.end() ); + schedulable s( i->second); + tasklet f( s.f); + terminated_tasklets_.pop(); + BOOST_ASSERT( s.joining_tasklets.empty() ); + BOOST_ASSERT( ! s.waiting_on_tasklet); + BOOST_ASSERT( ! s.waiting_on_object); + BOOST_ASSERT( in_state_terminated( f) ); + tasklets_.erase( i); + } + return result; +} + +bool +round_robin::empty() const +{ + spin_mutex::scoped_lock lk( mtx_); + return tasklets_.empty(); +} + +std::size_t +round_robin::size() const +{ + spin_mutex::scoped_lock lk( mtx_); + return tasklets_.size(); +} + +std::size_t +round_robin::ready() const +{ + spin_mutex::scoped_lock lk( mtx_); + return runnable_tasklets_.size(); +} + +bool +round_robin::has_ready() const +{ + spin_mutex::scoped_lock lk( mtx_); + return ! runnable_tasklets_.empty(); +} + +}} + +#include diff --git a/libs/tasklet/src/spin_condition.cpp b/libs/tasklet/src/spin_condition.cpp new file mode 100644 index 00000000..15ad44d3 --- /dev/null +++ b/libs/tasklet/src/spin_condition.cpp @@ -0,0 +1,53 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_TASKLET_SOURCE + +#include "boost/tasklet/spin_condition.hpp" + +#include + +namespace boost { +namespace tasklets { + +void +spin_condition::notify_( command cmd) +{ + enter_mtx_.lock(); + + if ( 0 == waiters_.load() ) + { + enter_mtx_.unlock(); + return; + } + + command expected = SLEEPING; + while ( ! cmd_.compare_exchange_strong( expected, cmd) ) + { + if ( this_tasklet::runs_as_tasklet() ) + this_tasklet::yield(); + else + this_thread::yield(); + expected = SLEEPING; + } +} + +spin_condition::spin_condition() : + cmd_( SLEEPING), + waiters_( 0), + enter_mtx_(), + check_mtx_() +{} + +void +spin_condition::notify_one() +{ notify_( NOTIFY_ONE); } + +void +spin_condition::notify_all() +{ notify_( NOTIFY_ALL); } + +}} diff --git a/libs/tasklet/src/spin_mutex.cpp b/libs/tasklet/src/spin_mutex.cpp new file mode 100644 index 00000000..511b6718 --- /dev/null +++ b/libs/tasklet/src/spin_mutex.cpp @@ -0,0 +1,48 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_TASKLET_SOURCE + +#include + +#include + +#include + +namespace boost { +namespace tasklets { + +spin_mutex::spin_mutex() : + state_( UNLOCKED) +{} + +void +spin_mutex::lock() +{ + for (;;) + { + state expected = UNLOCKED; + if ( state_.compare_exchange_strong( expected, LOCKED) ) + break; + if ( this_tasklet::runs_as_tasklet() ) + this_tasklet::yield(); + else + this_thread::yield(); + } +} + +bool +spin_mutex::try_lock() +{ + state expected = UNLOCKED; + return state_.compare_exchange_strong( expected, LOCKED); +} + +void +spin_mutex::unlock() +{ state_.store( UNLOCKED); } + +}} diff --git a/libs/tasklet/src/strategy.cpp b/libs/tasklet/src/strategy.cpp new file mode 100644 index 00000000..197d6eb9 --- /dev/null +++ b/libs/tasklet/src/strategy.cpp @@ -0,0 +1,194 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_TASKLET_SOURCE + +#include + +#include + +#include + +#include +#include +#include +#include + +namespace boost { +namespace tasklets { + +BOOST_TASKLET_TSSDECL tasklet * strategy::active_tasklet = 0; + +bool +strategy::runs_as_tasklet_() +{ return 0 != active_tasklet; } + +tasklet::id +strategy::get_id_() +{ + if ( ! active_tasklet) throw tasklet_error("not a tasklet"); + return active_tasklet->get_id(); +} + +void +strategy::interruption_point_() +{ + if ( ! active_tasklet) throw tasklet_error("not a tasklet"); + if ( detail::INTERRUPTION_ENABLED == active_tasklet->impl_->interrupt() ) + throw tasklet_interrupted(); +} + +bool +strategy::interruption_requested_() +{ + if ( ! active_tasklet) throw tasklet_error("not a tasklet"); + return active_tasklet->interruption_requested(); +} + +detail::interrupt_type & +strategy::interrupt_flags_() +{ + if ( ! active_tasklet) throw tasklet_error("not a tasklet"); + return active_tasklet->impl_->interrupt(); +} + +bool +strategy::interruption_enabled_() +{ + if ( ! active_tasklet) throw tasklet_error("not a tasklet"); + return ( active_tasklet->impl_->interrupt() & detail::INTERRUPTION_ENABLED) != 0; +} + +int +strategy::priority_() +{ + if ( ! active_tasklet) throw tasklet_error("not a tasklet"); + return active_tasklet->priority(); +} + +void +strategy::priority_( int prio) +{ + if ( ! active_tasklet) throw tasklet_error("not a tasklet"); + active_tasklet->priority( prio); +} + +void +strategy::yield_() +{ + if ( ! active_tasklet) throw tasklet_error("not a tasklet"); + if ( ! active_tasklet->impl_->attached_strategy() ) throw scheduler_error("no valid scheduler"); + active_tasklet->impl_->attached_strategy()->yield(); +} + +void +strategy::cancel_() +{ + if ( ! active_tasklet) throw tasklet_error("not a tasklet"); + if ( ! active_tasklet->impl_->attached_strategy() ) throw scheduler_error("no valid scheduler"); + active_tasklet->impl_->attached_strategy()->cancel( * active_tasklet); +} + +void +strategy::submit_tasklet_( tasklet f) +{ + if ( ! active_tasklet) throw tasklet_error("not a tasklet"); + if ( ! active_tasklet->impl_->attached_strategy() ) throw scheduler_error("no valid scheduler"); + active_tasklet->impl_->attached_strategy()->add( f); +} + +strategy::strategy() +{} + +strategy::~strategy() +{} + +void +strategy::call( tasklet & f) +{ + BOOST_ASSERT( f); + BOOST_ASSERT( f.impl_->attached_strategy() ); + BOOST_ASSERT( detail::STATE_RUNNING == f.impl_->state() ); + f.impl_->run(); +} + +void +strategy::yield( tasklet & f) +{ + BOOST_ASSERT( f); + BOOST_ASSERT( f.impl_->attached_strategy() ); + BOOST_ASSERT( detail::STATE_RUNNING == f.impl_->state() || detail::STATE_READY == f.impl_->state() ); + f.impl_->yield(); +} + +void +strategy::attach( tasklet & f) +{ f.impl_->attached_strategy( this); } + +void +strategy::detach( tasklet & f) +{ f.impl_->attached_strategy( 0); } + +void +strategy::enable_interruption( tasklet & f) +{ + detail::interrupt_type intr( f.impl_->interrupt() ); + // remove disabled flag + intr &= ~detail::INTERRUPTION_DISABLED; + // set enabled flag + intr |= detail::INTERRUPTION_ENABLED; + f.impl_->interrupt( intr); +} + +bool +strategy::interruption_enabled( tasklet const& f) +{ return detail::INTERRUPTION_ENABLED == f.impl_->interrupt(); } + +bool +strategy::in_state_not_started( tasklet const& f) +{ return detail::STATE_NOT_STARTED == f.impl_->state(); } + +bool +strategy::in_state_ready( tasklet const& f) +{ return detail::STATE_READY == f.impl_->state(); } + +bool +strategy::in_state_running( tasklet const& f) +{ return detail::STATE_RUNNING == f.impl_->state(); } + +bool +strategy::in_state_wait_for_tasklet( tasklet const& f) +{ return detail::STATE_WAIT_FOR_TASKLET == f.impl_->state(); } + +bool +strategy::in_state_wait_for_object( tasklet const& f) +{ return detail::STATE_WAIT_FOR_OBJECT == f.impl_->state(); } + +bool +strategy::in_state_terminated( tasklet const& f) +{ return detail::STATE_TERMINATED == f.impl_->state(); } + +void +strategy::set_state_ready( tasklet & f) +{ f.impl_->state( detail::STATE_READY); } + +void +strategy::set_state_running( tasklet & f) +{ f.impl_->state( detail::STATE_RUNNING); } + +void +strategy::set_state_wait_for_tasklet( tasklet & f) +{ f.impl_->state( detail::STATE_WAIT_FOR_TASKLET); } + +void +strategy::set_state_wait_for_object( tasklet & f) +{ f.impl_->state( detail::STATE_WAIT_FOR_OBJECT); } + +void +strategy::set_state_terminated( tasklet & f) +{ f.impl_->state( detail::STATE_TERMINATED); } + +}} diff --git a/libs/tasklet/src/tasklet.cpp b/libs/tasklet/src/tasklet.cpp new file mode 100644 index 00000000..059e8492 --- /dev/null +++ b/libs/tasklet/src/tasklet.cpp @@ -0,0 +1,133 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#define BOOST_TASKLET_SOURCE + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace boost { +namespace tasklets { + +std::size_t +tasklet::default_stacksize = 65536; + +tasklet::tasklet() : + impl_() +{} + +tasklet::tasklet( tasklet const& other) : + impl_( other.impl_) +{} + +tasklet & +tasklet::operator=( BOOST_COPY_ASSIGN_REF( tasklet) other) +{ + if ( this == & other) return * this; + impl_ = other.impl_; + return * this; +} + +tasklet::tasklet( BOOST_RV_REF( tasklet) other) +{ + impl_ = other.impl_; + other.impl_.reset(); +} + +tasklet & +tasklet::operator=( BOOST_RV_REF( tasklet) other) +{ + tasklet tmp( other); + swap( tmp); + return * this; +} + +tasklet::operator unspecified_bool_type() const +{ return impl_; } + +bool +tasklet::operator!() const +{ return ! impl_; } + +bool +tasklet::operator==( tasklet const& other) const +{ return get_id() == other.get_id(); } + +bool +tasklet::operator!=( tasklet const& other) const +{ return !( get_id() == other.get_id() ); } + +void +tasklet::swap( tasklet & other) +{ impl_.swap( other.impl_); } + +tasklet::id +tasklet::get_id() const +{ return tasklet::id( impl_); } + +bool +tasklet::is_alive() const +{ + if ( ! impl_) throw tasklet_moved(); + return ( impl_->state() & IS_ALIVE_BIT_MASK) != 0; +} + +int +tasklet::priority() const +{ + if ( ! impl_) throw tasklet_moved(); + return impl_->priority(); +} + +void +tasklet::priority( int prio) +{ + if ( ! impl_) throw tasklet_moved(); + impl_->priority( prio); + if ( is_alive() ) + impl_->attached_strategy()->reschedule( * this); +} + +void +tasklet::interrupt() +{ + if ( ! impl_) throw tasklet_moved(); + impl_->attached_strategy()->interrupt( * this); +} + +bool +tasklet::interruption_requested() const +{ + if ( ! impl_) throw tasklet_moved(); + return ( impl_->interrupt() & detail::INTERRUPTION_ENABLED) != 0; +} + +void +tasklet::cancel() +{ + if ( ! impl_) throw tasklet_moved(); + impl_->attached_strategy()->cancel( * this); +} + +void +tasklet::join() +{ + if ( ! impl_) throw tasklet_moved(); + impl_->attached_strategy()->join( * this); +} + +}} + +#include diff --git a/libs/tasklet/test/Jamfile.v2 b/libs/tasklet/test/Jamfile.v2 new file mode 100644 index 00000000..9a2df04c --- /dev/null +++ b/libs/tasklet/test/Jamfile.v2 @@ -0,0 +1,44 @@ +# Boost.tasklet Library Tests Jamfile + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import testing ; + +project boost/tasklet/test + : requirements + ../../test/build//boost_unit_test_framework + /boost/fiber//boost_fiber + /boost/tasklet//boost_tasklet + /boost/thread//boost_thread + static + multi + ; + +rule tasklet-test ( source ) +{ + return + [ run $(source).cpp ] + ; +} + +test-suite tasklet : + [ tasklet-test test_spin_mutex ] + [ tasklet-test test_spin_condition ] + [ tasklet-test test_tasklet ] + [ tasklet-test test_scheduler ] + [ tasklet-test test_utility ] + [ tasklet-test test_cancel ] + [ tasklet-test test_priority ] + [ tasklet-test test_join ] + [ tasklet-test test_interrupt ] + [ tasklet-test test_mutex ] + [ tasklet-test test_lock ] + [ tasklet-test test_condition ] + [ tasklet-test test_barrier ] + [ tasklet-test test_auto_reset_event ] + [ tasklet-test test_manual_reset_event ] + [ tasklet-test test_count_down_event ] + ; diff --git a/libs/tasklet/test/condition_test_common.hpp b/libs/tasklet/test/condition_test_common.hpp new file mode 100644 index 00000000..51ffa045 --- /dev/null +++ b/libs/tasklet/test/condition_test_common.hpp @@ -0,0 +1,63 @@ +#ifndef TASK_CONDITION_TEST_COMMON_HPP +#define TASK_CONDITION_TEST_COMMON_HPP +// Copyright (C) 2007 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include + +unsigned const timeout_seconds=5; + +struct wait_for_flag : private boost::noncopyable +{ + boost::tasklet::mutex mutex; + boost::tasklet::condition cond_var; + bool flag; + unsigned woken; + + wait_for_flag(): + flag(false),woken(0) + {} + + struct check_flag + { + bool const& flag; + + check_flag(bool const& flag_): + flag(flag_) + {} + + bool operator()() const + { + return flag; + } + private: + void operator=(check_flag&); + }; + + + void wait_without_predicate() + { + boost::tasklet::mutex::scoped_lock lock(mutex); + while(!flag) + { + cond_var.wait(lock); + } + ++woken; + } + + void wait_with_predicate() + { + boost::tasklet::mutex::scoped_lock lock(mutex); + cond_var.wait(lock,check_flag(flag)); + if(flag) + { + ++woken; + } + } +}; + +#endif diff --git a/libs/tasklet/test/test_auto_reset_event.cpp b/libs/tasklet/test/test_auto_reset_event.cpp new file mode 100644 index 00000000..ca087d5b --- /dev/null +++ b/libs/tasklet/test/test_auto_reset_event.cpp @@ -0,0 +1,132 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +int value = 0; + +void wait_fn( boost::tasklets::auto_reset_event & ev) +{ + ev.wait(); + ++value; +} + +// check wait +void test_case_1() +{ + value = 0; + boost::tasklets::scheduler<> sched; + boost::tasklets::auto_reset_event ev; + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( ev), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( ev), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + ev.set(); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + ev.set(); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); +} + +void test_case_2() +{ + value = 0; + boost::tasklets::scheduler<> sched; + boost::tasklets::auto_reset_event ev( true); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( ev), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( ev), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + ev.set(); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); +} + +void test_case_3() +{ + boost::tasklets::scheduler<> sched; + boost::tasklets::auto_reset_event ev; + + BOOST_CHECK_EQUAL( false, ev.try_wait() ); + + ev.set(); + + BOOST_CHECK_EQUAL( true, ev.try_wait() ); + BOOST_CHECK_EQUAL( false, ev.try_wait() ); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: auto-reset-event test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + + return test; +} diff --git a/libs/tasklet/test/test_barrier.cpp b/libs/tasklet/test/test_barrier.cpp new file mode 100644 index 00000000..89dbbe10 --- /dev/null +++ b/libs/tasklet/test/test_barrier.cpp @@ -0,0 +1,149 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include +#include + +#include + +int value1 = 0; +int value2 = 0; + +void fn_1( boost::tasklets::barrier & b) +{ + ++value1; + boost::this_tasklet::yield(); + + b.wait(); + + ++value1; + boost::this_tasklet::yield(); + ++value1; + boost::this_tasklet::yield(); + ++value1; + boost::this_tasklet::yield(); + ++value1; +} + +void fn_2( boost::tasklets::barrier & b) +{ + ++value2; + boost::this_tasklet::yield(); + ++value2; + boost::this_tasklet::yield(); + ++value2; + boost::this_tasklet::yield(); + + b.wait(); + + ++value2; + boost::this_tasklet::yield(); + ++value2; +} + +void test_case_1() +{ + value1 = 0; + value2 = 0; + + boost::tasklets::scheduler<> sched; + + boost::tasklets::barrier b( 2); + sched.submit_tasklet( + boost::tasklet( + fn_1, boost::ref( b), boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + sched.submit_tasklet( + boost::tasklet( + fn_2, boost::ref( b), boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 2, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 3, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 4, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 4, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 5, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 5, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 4, value1); + BOOST_CHECK_EQUAL( 5, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 5, value2); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 5, value2); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: barrier test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + + return test; +} diff --git a/libs/tasklet/test/test_cancel.cpp b/libs/tasklet/test/test_cancel.cpp new file mode 100644 index 00000000..85a48fa5 --- /dev/null +++ b/libs/tasklet/test/test_cancel.cpp @@ -0,0 +1,183 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include +#include + +#include + +int value1 = 0; +int value2 = 0; +int value3 = 0; + +void fn_1() +{ + for ( int i = 0; i < 3; ++i) + { + ++value1; + boost::this_tasklet::yield(); + if ( i == 1) + boost::this_tasklet::cancel(); + } +} + +void fn_2() +{ + for ( int i = 0; i < 3; ++i) + { + ++value2; + boost::this_tasklet::yield(); + } +} + +void fn_3( boost::tasklet f) +{ + for ( int i = 0; i < 3; ++i) + { + ++value3; + if ( i == 1) f.cancel(); + boost::this_tasklet::yield(); + } +} + +void test_case_1() +{ + value1 = 0; + value2 = 0; + + boost::tasklets::scheduler<> sched; + + sched.submit_tasklet( + boost::tasklet( + fn_1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + sched.submit_tasklet( + boost::tasklet( + fn_2, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 2, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 2, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 3, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 3, value2); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 3, value2); +} + +void test_case_2() +{ + value2 = 0; + value3 = 0; + + boost::tasklets::scheduler<> sched; + + boost::tasklet f( fn_2, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( f); + sched.submit_tasklet( + boost::tasklet( + fn_3, f, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( 0, value2); + BOOST_CHECK_EQUAL( 0, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value2); + BOOST_CHECK_EQUAL( 0, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value2); + BOOST_CHECK_EQUAL( 1, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 1, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 2, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 3, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 3, value3); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 3, value3); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: cancel test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + + return test; +} diff --git a/libs/tasklet/test/test_condition.cpp b/libs/tasklet/test/test_condition.cpp new file mode 100644 index 00000000..b6f7f38e --- /dev/null +++ b/libs/tasklet/test/test_condition.cpp @@ -0,0 +1,261 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +int value = 0; + +void notify_one_fn( boost::tasklets::condition & cond) +{ + cond.notify_one(); +} + +void notify_all_fn( boost::tasklets::condition & cond) +{ + cond.notify_all(); +} + +void wait_fn( + boost::tasklets::mutex & mtx, + boost::tasklets::condition & cond) +{ + boost::tasklets::mutex::scoped_lock lk( mtx); + cond.wait( lk); + ++value; +} + +void test_case_1() +{ + value = 0; + boost::tasklets::scheduler<> sched; + boost::tasklets::mutex mtx; + boost::tasklets::condition cond; + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( mtx), + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + sched.submit_tasklet( + boost::tasklet( + notify_one_fn, + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); +} + +void test_case_2() +{ + value = 0; + boost::tasklets::scheduler<> sched; + boost::tasklets::mutex mtx; + boost::tasklets::condition cond; + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( mtx), + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( mtx), + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + sched.submit_tasklet( + boost::tasklet( + notify_one_fn, + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + sched.submit_tasklet( + boost::tasklet( + notify_one_fn, + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); +} + +void test_case_3() +{ + value = 0; + boost::tasklets::scheduler<> sched; + boost::tasklets::mutex mtx; + boost::tasklets::condition cond; + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( mtx), + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( mtx), + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + sched.submit_tasklet( + boost::tasklet( + notify_all_fn, + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( mtx), + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); + + sched.submit_tasklet( + boost::tasklet( + notify_all_fn, + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 3, value); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 3, value); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: condition test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + + return test; +} diff --git a/libs/tasklet/test/test_count_down_event.cpp b/libs/tasklet/test/test_count_down_event.cpp new file mode 100644 index 00000000..5c679b84 --- /dev/null +++ b/libs/tasklet/test/test_count_down_event.cpp @@ -0,0 +1,96 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +int value = 0; + +void wait_fn( boost::tasklets::count_down_event & ev) +{ + ev.wait(); + ++value; +} + +void test_case_1() +{ + unsigned int n = 3; + boost::tasklets::scheduler<> sched; + boost::tasklets::count_down_event ev( n); + + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), n); + + ev.set(); + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), 2); + + ev.set(); + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), 1); + + ev.set(); + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), 0); + + ev.set(); + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), 0); +} + +void test_case_2() +{ + value = 0; + unsigned int n = 3; + boost::tasklets::scheduler<> sched; + boost::tasklets::count_down_event ev( n); + + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), n); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( ev), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + ev.set(); + BOOST_CHECK( sched.run() ); + BOOST_CHECK( value != 1); + + ev.set(); + BOOST_CHECK( sched.run() ); + BOOST_CHECK( value != 1); + + ev.set(); + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( ev.initial(), n); + BOOST_CHECK_EQUAL( ev.current(), 0); + BOOST_CHECK_EQUAL( 1, value); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Task: spin-count-down-event test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + + return test; +} diff --git a/libs/tasklet/test/test_generic_locks.cpp b/libs/tasklet/test/test_generic_locks.cpp new file mode 100644 index 00000000..b75b0e13 --- /dev/null +++ b/libs/tasklet/test/test_generic_locks.cpp @@ -0,0 +1,384 @@ +// (C) Copyright 2008 Anthony Williams +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include + +namespace tsk = boost::task; + +void test_lock_two_uncontended() +{ + boost::tasklets::mutex m1,m2; + + boost::tasklets::mutex::scoped_lock l1(m1,boost::defer_lock), + l2(m2,boost::defer_lock); + + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); + + boost::lock(l1,l2); + + BOOST_CHECK(l1.owns_lock()); + BOOST_CHECK(l2.owns_lock()); +} + +struct wait_data +{ + boost::tasklets::mutex m; + bool flag; + boost::tasklets::condition cond; + + wait_data(): + flag(false) + {} + + void wait() + { + boost::tasklets::mutex::scoped_lock l(m); + while(!flag) + { + cond.wait(l); + } + } + + void signal() + { + boost::tasklets::mutex::scoped_lock l(m); + flag=true; + cond.notify_all(); + } +}; + +void lock_pair(boost::tasklets::mutex* m1,boost::tasklets::mutex* m2) +{ + boost::lock(*m1,*m2); + boost::tasklets::mutex::scoped_lock l1(*m1,boost::adopt_lock), + l2(*m2,boost::adopt_lock); +} + +void test_lock_five_uncontended() +{ + boost::tasklets::mutex m1,m2,m3,m4,m5; + + boost::tasklets::mutex::scoped_lock l1(m1,boost::defer_lock), + l2(m2,boost::defer_lock), + l3(m3,boost::defer_lock), + l4(m4,boost::defer_lock), + l5(m5,boost::defer_lock); + + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); + BOOST_CHECK(!l3.owns_lock()); + BOOST_CHECK(!l4.owns_lock()); + BOOST_CHECK(!l5.owns_lock()); + + boost::lock(l1,l2,l3,l4,l5); + + BOOST_CHECK(l1.owns_lock()); + BOOST_CHECK(l2.owns_lock()); + BOOST_CHECK(l3.owns_lock()); + BOOST_CHECK(l4.owns_lock()); + BOOST_CHECK(l5.owns_lock()); +} + +void lock_five(boost::tasklets::mutex* m1,boost::tasklets::mutex* m2,boost::tasklets::mutex* m3,boost::tasklets::mutex* m4,boost::tasklets::mutex* m5) +{ + boost::lock(*m1,*m2,*m3,*m4,*m5); + m1->unlock(); + m2->unlock(); + m3->unlock(); + m4->unlock(); + m5->unlock(); +} + +void lock_n(boost::tasklets::mutex* mutexes,unsigned count) +{ + boost::lock(mutexes,mutexes+count); + for(unsigned i=0;i + struct is_mutex_type + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; +} + + + +void test_lock_five_in_range() +{ + unsigned const num_mutexes=5; + dummy_mutex mutexes[num_mutexes]; + + boost::lock(mutexes,mutexes+num_mutexes); + + for(unsigned i=0;i l1(m1,boost::defer_lock), + l2(m2,boost::defer_lock); + + int const res=boost::try_lock(l1,l2); + + BOOST_CHECK(res==0); + BOOST_CHECK(m1.is_locked); + BOOST_CHECK(!m2.is_locked); + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); +} +void test_try_lock_two_second_locked() +{ + dummy_mutex m1,m2; + m2.lock(); + + boost::unique_lock l1(m1,boost::defer_lock), + l2(m2,boost::defer_lock); + + int const res=boost::try_lock(l1,l2); + + BOOST_CHECK(res==1); + BOOST_CHECK(!m1.is_locked); + BOOST_CHECK(m2.is_locked); + BOOST_CHECK(!l1.owns_lock()); + BOOST_CHECK(!l2.owns_lock()); +} + +void test_try_lock_three() +{ + int const num_mutexes=3; + + for(int i=-1;i=0) + { + mutexes[i].lock(); + } + boost::unique_lock l1(mutexes[0],boost::defer_lock), + l2(mutexes[1],boost::defer_lock), + l3(mutexes[2],boost::defer_lock); + + int const res=boost::try_lock(l1,l2,l3); + + BOOST_CHECK(res==i); + for(int j=0;j=0) + { + mutexes[i].lock(); + } + boost::unique_lock l1(mutexes[0],boost::defer_lock), + l2(mutexes[1],boost::defer_lock), + l3(mutexes[2],boost::defer_lock), + l4(mutexes[3],boost::defer_lock); + + int const res=boost::try_lock(l1,l2,l3,l4); + + BOOST_CHECK(res==i); + for(int j=0;j=0) + { + mutexes[i].lock(); + } + boost::unique_lock l1(mutexes[0],boost::defer_lock), + l2(mutexes[1],boost::defer_lock), + l3(mutexes[2],boost::defer_lock), + l4(mutexes[3],boost::defer_lock), + l5(mutexes[4],boost::defer_lock); + + int const res=boost::try_lock(l1,l2,l3,l4,l5); + + BOOST_CHECK(res==i); + for(int j=0;jadd(BOOST_TEST_CASE(&test_lock_two_uncontended)); + test->add(BOOST_TEST_CASE(&test_lock_two_other_thread_locks_in_order)); + test->add(BOOST_TEST_CASE(&test_lock_two_other_thread_locks_in_opposite_order)); + test->add(BOOST_TEST_CASE(&test_lock_five_uncontended)); + test->add(BOOST_TEST_CASE(&test_lock_five_other_thread_locks_in_order)); + test->add(BOOST_TEST_CASE(&test_lock_five_other_thread_locks_in_different_order)); + test->add(BOOST_TEST_CASE(&test_lock_five_in_range)); + test->add(BOOST_TEST_CASE(&test_lock_ten_in_range)); + test->add(BOOST_TEST_CASE(&test_lock_ten_other_thread_locks_in_different_order)); + test->add(BOOST_TEST_CASE(&test_try_lock_two_uncontended)); + test->add(BOOST_TEST_CASE(&test_try_lock_two_first_locked)); + test->add(BOOST_TEST_CASE(&test_try_lock_two_second_locked)); + test->add(BOOST_TEST_CASE(&test_try_lock_three)); + test->add(BOOST_TEST_CASE(&test_try_lock_four)); + test->add(BOOST_TEST_CASE(&test_try_lock_five)); + + return test; +} diff --git a/libs/tasklet/test/test_interrupt.cpp b/libs/tasklet/test/test_interrupt.cpp new file mode 100644 index 00000000..228deed7 --- /dev/null +++ b/libs/tasklet/test/test_interrupt.cpp @@ -0,0 +1,248 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include +#include + +#include + +int value1 = 0; +int value2 = 0; +bool interrupted = false; + +void fn_1() +{ + try + { + for ( int i = 0; i < 5; ++i) + { + ++value1; + boost::this_tasklet::interruption_point(); + boost::this_tasklet::yield(); + } + } + catch ( boost::tasklets::tasklet_interrupted const&) + { interrupted = true; } +} + +void fn_2() +{ + boost::this_tasklet::disable_interruption disabler; + if ( boost::this_tasklet::interruption_enabled() ) + throw std::logic_error("interruption enabled"); + for ( int i = 0; i < 5; ++i) + { + ++value1; + boost::this_tasklet::interruption_point(); + boost::this_tasklet::yield(); + } +} + +void fn_3() +{ + try + { + boost::this_tasklet::disable_interruption disabler; + if ( boost::this_tasklet::interruption_enabled() ) + throw std::logic_error("interruption enabled"); + for ( int i = 0; i < 5; ++i) + { + ++value1; + boost::this_tasklet::restore_interruption restorer( disabler); + boost::this_tasklet::interruption_point(); + boost::this_tasklet::yield(); + } + } + catch ( boost::tasklets::tasklet_interrupted const&) + { interrupted = true; } +} + +void fn_5( boost::tasklet f) +{ + for ( int i = 0; i < 5; ++i) + { + ++value2; + if ( i == 1) f.interrupt(); + if ( i >= 1) + { + if ( ! f.interruption_requested() ) + throw std::logic_error(""); + } + boost::this_tasklet::yield(); + } +} + +void test_case_1() +{ + value1 = 0; + value2 = 0; + interrupted = false; + + boost::tasklets::scheduler<> sched; + + boost::tasklet f( fn_1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( f); + sched.submit_tasklet( + boost::tasklet( + fn_5, f, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( false, interrupted); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( false, interrupted); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( false, interrupted); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( false, interrupted); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 2, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( true, interrupted); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 2, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( true, interrupted); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 3, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( true, interrupted); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 4, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( true, interrupted); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 5, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( true, interrupted); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 5, value2); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( true, interrupted); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 5, value2); +} + +void test_case_2() +{ + value1 = 0; + value2 = 0; + interrupted = false; + + boost::tasklets::scheduler<> sched; + + boost::tasklet f( fn_2, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( f); + sched.submit_tasklet( + boost::tasklet( + fn_5, f, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + BOOST_CHECK_EQUAL( false, interrupted); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 5, value2); + BOOST_CHECK_EQUAL( false, interrupted); +} + +void test_case_3() +{ + value1 = 0; + value2 = 0; + interrupted = false; + + boost::tasklets::scheduler<> sched; + + boost::tasklet f( fn_3, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( f); + sched.submit_tasklet( + boost::tasklet( + fn_5, f, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + BOOST_CHECK_EQUAL( false, interrupted); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 5, value2); + BOOST_CHECK_EQUAL( true, interrupted); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: interrupt test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + + return test; +} diff --git a/libs/tasklet/test/test_join.cpp b/libs/tasklet/test/test_join.cpp new file mode 100644 index 00000000..8d7ae1c4 --- /dev/null +++ b/libs/tasklet/test/test_join.cpp @@ -0,0 +1,362 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include +#include + +#include + +int value1 = 0; +int value2 = 0; +int value3 = 0; +bool interrupted = false; + +void fn_1() +{ + for ( int i = 0; i < 5; ++i) + { + ++value1; + boost::this_tasklet::yield(); + } +} + +void fn_2( boost::tasklet f) +{ + try + { + for ( int i = 0; i < 5; ++i) + { + ++value2; + if ( i == 1) f.join(); + boost::this_tasklet::yield(); + } + } + catch ( boost::tasklets::tasklet_interrupted const&) + { interrupted = true; } +} + +void fn_3( boost::tasklet f) +{ + for ( int i = 0; i < 5; ++i) + { + ++value3; + if ( i == 3) f.cancel(); + boost::this_tasklet::yield(); + } +} + +void fn_4( boost::tasklet f) +{ + for ( int i = 0; i < 5; ++i) + { + ++value3; + if ( i == 3) f.interrupt(); + boost::this_tasklet::yield(); + } +} + +void test_case_1() +{ + value1 = 0; + value2 = 0; + + boost::tasklets::scheduler<> sched; + + boost::tasklet f( fn_1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( f); + sched.submit_tasklet( + boost::tasklet( + fn_2, f, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 2, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 2, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 4, value1); + BOOST_CHECK_EQUAL( 2, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 2, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 2, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 2, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 3, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 4, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 5, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 5, value2); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 5, value2); +} + +void test_case_2() +{ + value1 = 0; + value2 = 0; + value3 = 0; + + boost::tasklets::scheduler<> sched; + + boost::tasklet f( fn_1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( f); + sched.submit_tasklet( + boost::tasklet( + fn_2, f, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + sched.submit_tasklet( + boost::tasklet( + fn_3, f, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + BOOST_CHECK_EQUAL( 0, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + BOOST_CHECK_EQUAL( 0, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); + BOOST_CHECK_EQUAL( 0, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); + BOOST_CHECK_EQUAL( 1, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 1, value2); + BOOST_CHECK_EQUAL( 1, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 1, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 2, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 2, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 3, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 4, value1); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 3, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 4, value1); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 4, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 4, value1); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 4, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 4, value1); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 5, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 4, value1); + BOOST_CHECK_EQUAL( 3, value2); + BOOST_CHECK_EQUAL( 5, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 4, value1); + BOOST_CHECK_EQUAL( 3, value2); + BOOST_CHECK_EQUAL( 5, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 4, value1); + BOOST_CHECK_EQUAL( 4, value2); + BOOST_CHECK_EQUAL( 5, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 4, value1); + BOOST_CHECK_EQUAL( 5, value2); + BOOST_CHECK_EQUAL( 5, value3); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 4, value1); + BOOST_CHECK_EQUAL( 5, value2); + BOOST_CHECK_EQUAL( 5, value3); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 4, value1); + BOOST_CHECK_EQUAL( 5, value2); + BOOST_CHECK_EQUAL( 5, value3); +} + +void test_case_3() +{ + value1 = 0; + value2 = 0; + value3 = 0; + + boost::tasklets::scheduler<> sched; + + boost::tasklet f1( fn_1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( f1); + boost::tasklet f2( fn_2, f1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + sched.submit_tasklet( f2); + sched.submit_tasklet( + boost::tasklet( + fn_4, f2, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + BOOST_CHECK_EQUAL( 0, value3); + BOOST_CHECK_EQUAL( false, interrupted); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 5, value1); + BOOST_CHECK_EQUAL( 2, value2); + BOOST_CHECK_EQUAL( 5, value3); + BOOST_CHECK_EQUAL( true, interrupted); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: join test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + + return test; +} diff --git a/libs/tasklet/test/test_lock.cpp b/libs/tasklet/test/test_lock.cpp new file mode 100644 index 00000000..60c4aa2f --- /dev/null +++ b/libs/tasklet/test/test_lock.cpp @@ -0,0 +1,209 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// This test is based on the tests of Boost.Thread + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +struct dummy_mutex +{ + bool is_locked; + + dummy_mutex() : + is_locked( false) + {} + + void lock() + { is_locked = true; } + + bool try_lock() + { + if ( is_locked) + return false; + is_locked = true; + return true; + } + + void unlock() + { is_locked = false; } +}; + +void test_lock() +{ + boost::tasklets::scheduler<> sched; + boost::tasklets::mutex mtx; + boost::unique_lock< boost::tasklets::mutex > lk( mtx); + + BOOST_CHECK( lk); + BOOST_CHECK( lk.owns_lock() ); + + lk.unlock(); + + BOOST_CHECK( ! lk); + BOOST_CHECK( ! lk.owns_lock() ); +} + +void test_defer_lock() +{ + boost::tasklets::scheduler<> sched; + boost::tasklets::mutex mtx; + boost::unique_lock< boost::tasklets::mutex > lk( mtx, boost::defer_lock); + + BOOST_CHECK( ! lk); + BOOST_CHECK( ! lk.owns_lock() ); + + lk.lock(); + + BOOST_CHECK( lk); + BOOST_CHECK( lk.owns_lock() ); +} + +void test_adopt_lock() +{ + boost::tasklets::scheduler<> sched; + boost::tasklets::mutex mtx; + mtx.lock(); + boost::unique_lock< boost::tasklets::mutex > lk( mtx, boost::adopt_lock); + + BOOST_CHECK( lk); + BOOST_CHECK( lk.owns_lock() ); +} + +void test_try_lock() +{ + boost::tasklets::scheduler<> sched; + boost::tasklets::mutex mtx; + boost::unique_lock< boost::tasklets::mutex > lk( mtx, boost::defer_lock); + + BOOST_CHECK( ! lk); + BOOST_CHECK( ! lk.owns_lock() ); + + lk.try_lock(); + + BOOST_CHECK( lk); + BOOST_CHECK( lk.owns_lock() ); +} + +void test_lock_twice() +{ + boost::tasklets::scheduler<> sched; + boost::tasklets::mutex mtx; + boost::unique_lock< boost::tasklets::mutex > lk( mtx); + + BOOST_CHECK_THROW( lk.lock(), boost::lock_error); +} + +void test_try_lock_twice() +{ + boost::tasklets::scheduler<> sched; + boost::tasklets::mutex mtx; + boost::unique_lock< boost::tasklets::mutex > lk( mtx); + + BOOST_CHECK_THROW( lk.try_lock(), boost::lock_error); +} + +void test_unlock_twice() +{ + boost::tasklets::scheduler<> sched; + boost::tasklets::mutex mtx; + boost::unique_lock< boost::tasklets::mutex > lk( mtx); + lk.unlock(); + + BOOST_CHECK_THROW( lk.unlock(), boost::lock_error); +} + +void test_default_ctor() +{ + boost::unique_lock< boost::tasklets::mutex > lk; + + BOOST_CHECK( ! lk); + BOOST_CHECK( ! lk.owns_lock() ); +} + +void test_lock_concept() +{ + boost::tasklets::scheduler<> sched; + boost::tasklets::mutex mtx1, mtx2, mtx3; + + boost::tasklets::mutex::scoped_lock lk1( mtx1, boost::defer_lock), + lk2( mtx2, boost::defer_lock), + lk3( mtx3, boost::defer_lock); + + BOOST_CHECK( ! lk1.owns_lock() ); + BOOST_CHECK( ! lk2.owns_lock() ); + BOOST_CHECK( ! lk3.owns_lock() ); + + boost::lock( lk1, lk2, lk3); + + BOOST_CHECK( lk1.owns_lock() ); + BOOST_CHECK( lk2.owns_lock() ); + BOOST_CHECK( lk3.owns_lock() ); +} + +void test_try_lock_concept() +{ + dummy_mutex mtx1, mtx2; + mtx2.lock(); + + boost::unique_lock< dummy_mutex > lk1( mtx1, boost::defer_lock), + lk2( mtx2, boost::defer_lock); + + int res = boost::try_lock( lk1, lk2); + + BOOST_CHECK( res == 1); + BOOST_CHECK( ! mtx1.is_locked); + BOOST_CHECK( mtx2.is_locked); + BOOST_CHECK( ! lk1.owns_lock() ); + BOOST_CHECK( ! lk2.owns_lock() ); +} + +void test_swap() +{ + boost::tasklets::scheduler<> sched; + boost::tasklets::mutex mtx1, mtx2; + + boost::unique_lock< boost::tasklets::mutex > lk1( mtx1), lk2( mtx2); + + BOOST_CHECK_EQUAL( lk1.mutex(), & mtx1); + BOOST_CHECK_EQUAL( lk2.mutex(), & mtx2); + + lk1.swap( lk2); + + BOOST_CHECK_EQUAL( lk1.mutex(), & mtx2); + BOOST_CHECK_EQUAL( lk2.mutex(), & mtx1); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test_framework::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: lock test suite"); + + test->add( BOOST_TEST_CASE( & test_lock) ); + test->add( BOOST_TEST_CASE( & test_defer_lock) ); + test->add( BOOST_TEST_CASE( & test_adopt_lock) ); + test->add( BOOST_TEST_CASE( & test_try_lock) ); + test->add( BOOST_TEST_CASE( & test_lock_twice) ); + test->add( BOOST_TEST_CASE( & test_try_lock_twice) ); + test->add( BOOST_TEST_CASE( & test_unlock_twice) ); + test->add( BOOST_TEST_CASE( & test_default_ctor) ); + test->add( BOOST_TEST_CASE( & test_lock_concept) ); + test->add( BOOST_TEST_CASE( & test_try_lock_concept) ); + test->add( BOOST_TEST_CASE( & test_swap) ); + + return test; +} diff --git a/libs/tasklet/test/test_manual_reset_event.cpp b/libs/tasklet/test/test_manual_reset_event.cpp new file mode 100644 index 00000000..071c219f --- /dev/null +++ b/libs/tasklet/test/test_manual_reset_event.cpp @@ -0,0 +1,170 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +int value = 0; + +void wait_fn( boost::tasklets::manual_reset_event & ev) +{ + ev.wait(); + ++value; +} + +void test_case_1() +{ + value = 0; + boost::tasklets::scheduler<> sched; + boost::tasklets::manual_reset_event ev; + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( ev), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( ev), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + ev.set(); + + while ( sched.run() ); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); +} + +void test_case_2() +{ + value = 0; + boost::tasklets::scheduler<> sched; + boost::tasklets::manual_reset_event ev; + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( ev), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( ev), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + ev.set(); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + ev.reset(); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + ev.set(); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); +} + +void test_case_3() +{ + value = 0; + boost::tasklets::scheduler<> sched; + boost::tasklets::manual_reset_event ev( true); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( ev), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( ev), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + while ( sched.run() ); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); +} + +void test_case_4() +{ + boost::tasklets::scheduler<> sched; + boost::tasklets::manual_reset_event ev; + + BOOST_CHECK_EQUAL( false, ev.try_wait() ); + + ev.set(); + + BOOST_CHECK_EQUAL( true, ev.try_wait() ); + BOOST_CHECK_EQUAL( true, ev.try_wait() ); + + ev.reset(); + + BOOST_CHECK_EQUAL( false, ev.try_wait() ); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: manual-reset-event test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + + return test; +} diff --git a/libs/tasklet/test/test_mutex.cpp b/libs/tasklet/test/test_mutex.cpp new file mode 100644 index 00000000..75c5f626 --- /dev/null +++ b/libs/tasklet/test/test_mutex.cpp @@ -0,0 +1,132 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// This test is based on the tests of Boost.Thread + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +template< typename M > +struct test_lock +{ + typedef M mutex_type; + typedef typename M::scoped_lock lock_type; + + void operator()( boost::tasklets::scheduler<> & sched) + { + mutex_type mtx; + + // Test the lock's constructors. + { + lock_type lk(mtx, boost::defer_lock); + BOOST_CHECK(!lk); + } + lock_type lk(mtx); + BOOST_CHECK(lk ? true : false); + + // Test the lock and unlock methods. + lk.unlock(); + BOOST_CHECK(!lk); + lk.lock(); + BOOST_CHECK(lk ? true : false); + } +}; + +void do_test_mutex( boost::tasklets::scheduler<> & sched) +{ + test_lock< boost::tasklets::mutex >()( sched); +} + +void test_case1() +{ + boost::tasklets::scheduler<> sched; + sched.submit_tasklet( + boost::tasklet( + & do_test_mutex, boost::ref( sched), boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + sched.run(); +} + +int value1 = 0; +int value2 = 0; + +void test_fn1( boost::tasklets::mutex & mtx) +{ + boost::tasklets::mutex::scoped_lock lk( mtx); + ++value1; + for ( int i = 0; i < 3; ++i) + boost::this_tasklet::yield(); +} + +void test_fn2( boost::tasklets::mutex & mtx) +{ + ++value2; + boost::tasklets::mutex::scoped_lock lk( mtx); + ++value2; +} + +void test_case2() +{ + boost::tasklets::scheduler<> sched; + boost::tasklets::mutex mtx; + sched.submit_tasklet( + boost::tasklet( + & test_fn1, boost::ref( mtx), boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + sched.submit_tasklet( + boost::tasklet( + & test_fn2, boost::ref( mtx), boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 2, value2); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 2, value2); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test_framework::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: mutex test suite"); + + test->add(BOOST_TEST_CASE(&test_case1)); + test->add(BOOST_TEST_CASE(&test_case2)); + + return test; +} diff --git a/libs/tasklet/test/test_priority.cpp b/libs/tasklet/test/test_priority.cpp new file mode 100644 index 00000000..913bb657 --- /dev/null +++ b/libs/tasklet/test/test_priority.cpp @@ -0,0 +1,39 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include +#include + +#include + + +void zero_args_fn() +{} + +void test_case_1() +{ + boost::tasklet f( zero_args_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + BOOST_CHECK_EQUAL( 0, f.priority() ); + + f.priority( 5); + BOOST_CHECK_EQUAL( 5, f.priority() ); + + f.priority( -5); + BOOST_CHECK_EQUAL( -5, f.priority() ); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: priority test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + + return test; +} diff --git a/libs/tasklet/test/test_scheduler.cpp b/libs/tasklet/test/test_scheduler.cpp new file mode 100644 index 00000000..9b239720 --- /dev/null +++ b/libs/tasklet/test/test_scheduler.cpp @@ -0,0 +1,262 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include +#include +#include + +#include + +int value1 = 0; +int value2 = 0; +int value3 = 0; + +void zero_args_fn() {} +void one_args_fn( int) {} + +void value1_fn() +{ value1 = 1; } + +void value2_fn() +{ value2 = 1; } + +void value3_fn() +{ value3 = 1; } + +void yield1_fn() +{ + for ( int i = 0; i < 5; ++i) + { + ++value1; + boost::this_tasklet::yield(); + } +} + +void yield2_fn() +{ + for ( int i = 0; i < 5; ++i) + { + ++value2; + boost::this_tasklet::yield(); + } +} + +void test_case_1() +{ + value1 = 0; + value2 = 0; + + boost::tasklets::scheduler<> sched; + + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK( ! sched.run() ); + + boost::tasklet f( boost::tasklet( zero_args_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + BOOST_CHECK( ! f.is_alive() ); + sched.submit_tasklet( f); + BOOST_CHECK( f.is_alive() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + + sched.submit_tasklet( + boost::tasklet( zero_args_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + + BOOST_CHECK( ! f.is_alive() ); +} + +void test_case_2() +{ + value1 = 0; + value2 = 0; + + boost::tasklets::scheduler<> sched; + + sched.submit_tasklet( + boost::tasklet( + value1_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + sched.submit_tasklet( + boost::tasklet( + value2_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( ! sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); +} + +void test_case_3() +{ + value1 = 0; + value2 = 0; + + boost::tasklets::scheduler<> sched1, sched2; + + boost::tasklet f( & yield1_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + boost::tasklet::id id = f.get_id(); + sched1.submit_tasklet( f); + sched2.submit_tasklet( + boost::tasklet( + & yield2_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( ! sched1.empty() ); + BOOST_CHECK( ! sched2.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched1.size() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched2.size() ); + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched1.run() ); + BOOST_CHECK( ! sched1.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched1.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched2.run() ); + BOOST_CHECK( ! sched2.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched2.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched1.run() ); + BOOST_CHECK( ! sched1.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched1.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched2.run() ); + BOOST_CHECK( ! sched2.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched2.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 2, value2); + + sched2.migrate_tasklet( f); + BOOST_CHECK_EQUAL( std::size_t( 0), sched1.size() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched2.size() ); + + BOOST_CHECK( ! sched1.run() ); + BOOST_CHECK( sched1.empty() ); + + BOOST_CHECK( sched2.run() ); + BOOST_CHECK( ! sched2.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched2.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 3, value2); + + BOOST_CHECK( sched2.run() ); + BOOST_CHECK( ! sched2.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched2.size() ); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 3, value2); +} + +void test_case_4() +{ + value1 = 0; + value2 = 0; + + boost::tasklets::scheduler<> sched1, sched2; + + boost::tasklet f( & yield1_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + boost::tasklet::id id = f.get_id(); + sched1.submit_tasklet( f); + sched2.submit_tasklet( + boost::tasklet( + & yield2_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( ! sched1.empty() ); + BOOST_CHECK( ! sched2.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched1.size() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched2.size() ); + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched1.run() ); + BOOST_CHECK( ! sched1.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched1.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched2.run() ); + BOOST_CHECK( ! sched2.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched2.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched1.run() ); + BOOST_CHECK( ! sched1.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched1.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 1, value2); + + BOOST_CHECK( sched2.run() ); + BOOST_CHECK( ! sched2.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched2.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 2, value2); + + sched2.migrate_tasklet( f); + BOOST_CHECK_EQUAL( std::size_t( 0), sched1.size() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched2.size() ); + + BOOST_CHECK( ! sched1.run() ); + BOOST_CHECK( sched1.empty() ); + + BOOST_CHECK( sched2.run() ); + BOOST_CHECK( ! sched2.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched2.size() ); + BOOST_CHECK_EQUAL( 2, value1); + BOOST_CHECK_EQUAL( 3, value2); + + BOOST_CHECK( sched2.run() ); + BOOST_CHECK( ! sched2.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched2.size() ); + BOOST_CHECK_EQUAL( 3, value1); + BOOST_CHECK_EQUAL( 3, value2); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: scheduler test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + + return test; +} diff --git a/libs/tasklet/test/test_spin_condition.cpp b/libs/tasklet/test/test_spin_condition.cpp new file mode 100644 index 00000000..44d0c83d --- /dev/null +++ b/libs/tasklet/test/test_spin_condition.cpp @@ -0,0 +1,248 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +int value = 0; + +void notify_one_fn( boost::tasklets::spin_condition & cond) +{ + cond.notify_one(); +} + +void notify_all_fn( boost::tasklets::spin_condition & cond) +{ + cond.notify_all(); +} + +void wait_fn( + boost::tasklets::spin_mutex & mtx, + boost::tasklets::spin_condition & cond) +{ + boost::tasklets::spin_mutex::scoped_lock lk( mtx); + cond.wait( lk); + ++value; +} + +void test_case_1() +{ + value = 0; + boost::tasklets::spin_mutex mtx; + boost::tasklets::spin_condition cond; + boost::tasklets::scheduler<> sched; + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( mtx), + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + sched.submit_tasklet( + boost::tasklet( + notify_one_fn, + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); +} + +void test_case_2() +{ + value = 0; + boost::tasklets::spin_mutex mtx; + boost::tasklets::spin_condition cond; + boost::tasklets::scheduler<> sched; + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( mtx), + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( mtx), + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + sched.submit_tasklet( + boost::tasklet( + notify_one_fn, + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); +} + +void test_case_3() +{ + value = 0; + boost::tasklets::spin_mutex mtx; + boost::tasklets::spin_condition cond; + boost::tasklets::scheduler<> sched; + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( mtx), + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( mtx), + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + sched.submit_tasklet( + boost::tasklet( + notify_all_fn, + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 3), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value); + + sched.submit_tasklet( + boost::tasklet( + wait_fn, + boost::ref( mtx), + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); + + sched.submit_tasklet( + boost::tasklet( + notify_all_fn, + boost::ref( cond), + boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 2, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 3, value); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: spin-condition test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + + return test; +} diff --git a/libs/tasklet/test/test_spin_mutex.cpp b/libs/tasklet/test/test_spin_mutex.cpp new file mode 100644 index 00000000..4c6c8c9c --- /dev/null +++ b/libs/tasklet/test/test_spin_mutex.cpp @@ -0,0 +1,131 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// This test is based on the tests of Boost.Thread + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +template< typename M > +struct test_lock +{ + typedef M spin_mutex_type; + typedef typename M::scoped_lock lock_type; + + void operator()() + { + spin_mutex_type spin_mutex; + boost::tasklets::spin_condition condition; + + // Test the lock's constructors. + { + lock_type lock(spin_mutex, boost::defer_lock); + BOOST_CHECK(!lock); + } + lock_type lock(spin_mutex); + BOOST_CHECK(lock ? true : false); + + // Test the lock and unlock methods. + lock.unlock(); + BOOST_CHECK(!lock); + lock.lock(); + BOOST_CHECK(lock ? true : false); + } +}; + +void do_test_spin_mutex() +{ + test_lock< boost::tasklets::spin_mutex >()(); +} + +void test_case1() +{ + boost::tasklets::scheduler<> sched; + sched.submit_tasklet( boost::tasklet( & do_test_spin_mutex, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + sched.run(); +} + +int value1 = 0; +int value2 = 0; + +void test_fn1( boost::tasklets::spin_mutex & mtx) +{ + boost::tasklets::spin_mutex::scoped_lock lk( mtx); + ++value1; + for ( int i = 0; i < 3; ++i) + boost::this_tasklet::yield(); +} + +void test_fn2( boost::tasklets::spin_mutex & mtx) +{ + boost::tasklets::spin_mutex::scoped_lock lk( mtx); + ++value2; +} + +void test_case2() +{ + boost::tasklets::spin_mutex mtx; + boost::tasklets::scheduler<> sched; + sched.submit_tasklet( boost::tasklet( & test_fn1, boost::ref( mtx), boost::tasklet::default_stacksize, boost::protected_stack_allocator())); + sched.submit_tasklet( boost::tasklet( & test_fn2, boost::ref( mtx), boost::tasklet::default_stacksize, boost::protected_stack_allocator())); + + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 2), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 1), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 0, value2); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test_framework::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: spin-mutex test suite"); + + test->add(BOOST_TEST_CASE(&test_case1)); + test->add(BOOST_TEST_CASE(&test_case2)); + + return test; +} diff --git a/libs/tasklet/test/test_tasklet.cpp b/libs/tasklet/test/test_tasklet.cpp new file mode 100644 index 00000000..bfe26a52 --- /dev/null +++ b/libs/tasklet/test/test_tasklet.cpp @@ -0,0 +1,121 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include +#include +#include + +#include + +void zero_args_fn() +{ BOOST_ASSERT( boost::this_tasklet::runs_as_tasklet() ); } + +void one_args_fn( int) +{ BOOST_ASSERT( boost::this_tasklet::runs_as_tasklet() ); } + +void two_args_fn( int, std::string const&) +{ BOOST_ASSERT( boost::this_tasklet::runs_as_tasklet() ); } + +void test_case_1() +{ + boost::tasklet f1( one_args_fn, 10, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + boost::tasklet f2; + BOOST_CHECK( f1); + BOOST_CHECK( ! f2); + BOOST_CHECK( ! f1.is_alive() ); +} + +void test_case_2() +{ + boost::tasklet f1; + f1 = boost::tasklet( zero_args_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + boost::tasklet f2( one_args_fn, 1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + boost::tasklet f3; + f3 = boost::tasklet( two_args_fn, 1, "abc", boost::tasklet::default_stacksize, boost::protected_stack_allocator()); +} + +void test_case_3() +{ + boost::tasklet f1( & zero_args_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + f1 = boost::tasklet( zero_args_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + boost::tasklet f2( one_args_fn, 1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + f2 = boost::tasklet( one_args_fn, 1, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); +} + +void test_case_4() +{ + boost::tasklet f1( one_args_fn, 10, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + BOOST_CHECK( f1); + boost::tasklet f2( boost::move( f1) ); + BOOST_CHECK( ! f1); + BOOST_CHECK( f2); + boost::tasklet f3; + BOOST_CHECK( ! f3); + f3 = f2; + BOOST_CHECK( f3); + BOOST_CHECK_EQUAL( f2, f3); +} + +void test_case_5() +{ + boost::tasklet f1( zero_args_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + boost::tasklet f2( zero_args_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + boost::tasklet f3; + BOOST_CHECK( f1); + BOOST_CHECK( f2); + BOOST_CHECK( ! f3); + + BOOST_CHECK( f1 != f2); + BOOST_CHECK( f1 != f3); + BOOST_CHECK( f2 != f3); + + std::ostringstream os1; + os1 << f1.get_id(); + std::ostringstream os2; + os2 << f2.get_id(); + std::ostringstream os3; + os3 << f3.get_id(); + + std::string not_a_tasklet("{not-a-tasklet}"); + BOOST_CHECK( os1.str() != os2.str() ); + BOOST_CHECK( os1.str() != os3.str() ); + BOOST_CHECK( os2.str() != os3.str() ); + BOOST_CHECK( os1.str() != not_a_tasklet); + BOOST_CHECK( os2.str() != not_a_tasklet); + BOOST_CHECK( os3.str() == not_a_tasklet); +} + +void test_case_6() +{ + boost::tasklet f1( zero_args_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + boost::tasklet f2( zero_args_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()); + + boost::tasklet::id id1 = f1.get_id(); + boost::tasklet::id id2 = f2.get_id(); + + f1.swap( f2); + + BOOST_CHECK_EQUAL( f1.get_id(), id2); + BOOST_CHECK_EQUAL( f2.get_id(), id1); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: tasklet test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + test->add( BOOST_TEST_CASE( & test_case_5) ); + test->add( BOOST_TEST_CASE( & test_case_6) ); + + return test; +} diff --git a/libs/tasklet/test/test_unique_lock.cpp b/libs/tasklet/test/test_unique_lock.cpp new file mode 100644 index 00000000..ce102b97 --- /dev/null +++ b/libs/tasklet/test/test_unique_lock.cpp @@ -0,0 +1,200 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// This test is based on the tests of Boost.Thread + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +struct dummy_mutex +{ + bool is_locked; + + dummy_mutex() : + is_locked( false) + {} + + void lock() + { is_locked = true; } + + bool try_lock() + { + if ( is_locked) + return false; + is_locked = true; + return true; + } + + void unlock() + { is_locked = false; } +}; + +void test_lock() +{ + boost::tasklets::mutex mtx; + boost::unique_lock< boost::tasklets::mutex > lk( mtx); + + BOOST_CHECK( lk); + BOOST_CHECK( lk.owns_lock() ); + + lk.unlock(); + + BOOST_CHECK( ! lk); + BOOST_CHECK( ! lk.owns_lock() ); +} + +void test_defer_lock() +{ + boost::tasklets::mutex mtx; + boost::unique_lock< boost::tasklets::mutex > lk( mtx, boost::defer_lock); + + BOOST_CHECK( ! lk); + BOOST_CHECK( ! lk.owns_lock() ); + + lk.lock(); + + BOOST_CHECK( lk); + BOOST_CHECK( lk.owns_lock() ); +} + +void test_adopt_lock() +{ + boost::tasklets::mutex mtx; + mtx.lock(); + boost::unique_lock< boost::tasklets::mutex > lk( mtx, boost::adopt_lock); + + BOOST_CHECK( lk); + BOOST_CHECK( lk.owns_lock() ); +} + +void test_try_lock() +{ + boost::tasklets::mutex mtx; + boost::unique_lock< boost::tasklets::mutex > lk( mtx, boost::defer_lock); + + BOOST_CHECK( ! lk); + BOOST_CHECK( ! lk.owns_lock() ); + + lk.try_lock(); + + BOOST_CHECK( lk); + BOOST_CHECK( lk.owns_lock() ); +} + +void test_lock_twice() +{ + boost::tasklets::mutex mtx; + boost::unique_lock< boost::tasklets::mutex > lk( mtx); + + BOOST_CHECK_THROW( lk.lock(), boost::lock_error); +} + +void test_try_lock_twice() +{ + boost::tasklets::mutex mtx; + boost::unique_lock< boost::tasklets::mutex > lk( mtx); + + BOOST_CHECK_THROW( lk.try_lock(), boost::lock_error); +} + +void test_unlock_twice() +{ + boost::tasklets::mutex mtx; + boost::unique_lock< boost::tasklets::mutex > lk( mtx); + lk.unlock(); + + BOOST_CHECK_THROW( lk.unlock(), boost::lock_error); +} + +void test_default_ctor() +{ + boost::unique_lock< boost::tasklets::mutex > lk; + + BOOST_CHECK( ! lk); + BOOST_CHECK( ! lk.owns_lock() ); +} + +void test_lock_concept() +{ + boost::tasklets::mutex mtx1, mtx2, mtx3; + + boost::tasklets::mutex::scoped_lock lk1( mtx1, boost::defer_lock), + lk2( mtx2, boost::defer_lock), + lk3( mtx3, boost::defer_lock); + + BOOST_CHECK( ! lk1.owns_lock() ); + BOOST_CHECK( ! lk2.owns_lock() ); + BOOST_CHECK( ! lk3.owns_lock() ); + + boost::lock( lk1, lk2, lk3); + + BOOST_CHECK( lk1.owns_lock() ); + BOOST_CHECK( lk2.owns_lock() ); + BOOST_CHECK( lk3.owns_lock() ); +} + +void test_try_lock_concept() +{ + dummy_mutex mtx1, mtx2; + mtx2.lock(); + + boost::unique_lock< dummy_mutex > lk1( mtx1, boost::defer_lock), + lk2( mtx2, boost::defer_lock); + + int res = boost::try_lock( lk1, lk2); + + BOOST_CHECK( res == 1); + BOOST_CHECK( ! mtx1.is_locked); + BOOST_CHECK( mtx2.is_locked); + BOOST_CHECK( ! lk1.owns_lock() ); + BOOST_CHECK( ! lk2.owns_lock() ); +} + +void test_swap() +{ + boost::tasklets::mutex mtx1, mtx2; + + boost::unique_lock< boost::tasklets::mutex > lk1( mtx1), lk2( mtx2); + + BOOST_CHECK_EQUAL( lk1.mutex(), & mtx1); + BOOST_CHECK_EQUAL( lk2.mutex(), & mtx2); + + lk1.swap( lk2); + + BOOST_CHECK_EQUAL( lk1.mutex(), & mtx2); + BOOST_CHECK_EQUAL( lk2.mutex(), & mtx1); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test_framework::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: lock test suite"); + + test->add( BOOST_TEST_CASE( & test_lock) ); + test->add( BOOST_TEST_CASE( & test_defer_lock) ); + test->add( BOOST_TEST_CASE( & test_adopt_lock) ); + test->add( BOOST_TEST_CASE( & test_try_lock) ); + test->add( BOOST_TEST_CASE( & test_lock_twice) ); + test->add( BOOST_TEST_CASE( & test_try_lock_twice) ); + test->add( BOOST_TEST_CASE( & test_unlock_twice) ); + test->add( BOOST_TEST_CASE( & test_default_ctor) ); + test->add( BOOST_TEST_CASE( & test_lock_concept) ); + test->add( BOOST_TEST_CASE( & test_try_lock_concept) ); + test->add( BOOST_TEST_CASE( & test_swap) ); + + return test; +} diff --git a/libs/tasklet/test/test_utility.cpp b/libs/tasklet/test/test_utility.cpp new file mode 100644 index 00000000..72184b57 --- /dev/null +++ b/libs/tasklet/test/test_utility.cpp @@ -0,0 +1,158 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include +#include + +#include + +bool runs_as_tasklet = false; +std::string id; +int value = 0; + +void runs_as_fn() +{ runs_as_tasklet = boost::this_tasklet::runs_as_tasklet(); } + +void get_id_fn() +{ + std::ostringstream os; + os << boost::this_tasklet::get_id(); + id = os.str(); +} + +void yield_fn( int n) +{ + for ( int i = 0; i < n; ++i) + { + ++value; + boost::this_tasklet::yield(); + } +} + +void cancel_fn( int n) +{ + if ( n < 3) throw std::invalid_argument("must be greater than 3"); + for ( int i = 0; i < n; ++i) + { + if ( i == 2) boost::this_tasklet::cancel(); + ++value; + boost::this_tasklet::yield(); + } +} + +void submit_fn( int n) +{ + boost::this_tasklet::submit_tasklet( + boost::tasklet( + & yield_fn, n, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); +} + +void test_case_1() +{ + boost::tasklets::scheduler<> sched; + sched.submit_tasklet( + boost::tasklet( + runs_as_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( sched.run() ); + + BOOST_CHECK( runs_as_tasklet); +} + +void test_case_2() +{ + boost::tasklets::scheduler<> sched; + sched.submit_tasklet( + boost::tasklet( + get_id_fn, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + + BOOST_CHECK( sched.run() ); + + BOOST_CHECK( ! id.empty() ); + BOOST_CHECK( std::string("{not-a-tasklet}") != id); +} + +void test_case_3() +{ + value = 0; + boost::tasklets::scheduler<> sched; + sched.submit_tasklet( + boost::tasklet( + yield_fn, 3, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( 1, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( 2, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( 3, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( 3, value); + + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( 3, value); +} + +void test_case_4() +{ + value = 0; + boost::tasklets::scheduler<> sched; + sched.submit_tasklet( + boost::tasklet( + cancel_fn, 5, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + BOOST_CHECK_EQUAL( 0, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( 1, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK_EQUAL( 2, value); + + BOOST_CHECK( sched.run() ); + BOOST_CHECK( sched.empty() ); + BOOST_CHECK_EQUAL( std::size_t( 0), sched.size() ); + BOOST_CHECK( ! sched.run() ); + BOOST_CHECK_EQUAL( 2, value); +} + +void test_case_5() +{ + value = 0; + boost::tasklets::scheduler<> sched; + sched.submit_tasklet( + boost::tasklet( + submit_fn, 5, boost::tasklet::default_stacksize, boost::protected_stack_allocator()) ); + BOOST_CHECK_EQUAL( 0, value); + + for (;;) + { + while ( sched.run() ); + if ( sched.empty() ) break; + } + BOOST_CHECK_EQUAL( 5, value); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Tasklet: utility test suite"); + + test->add( BOOST_TEST_CASE( & test_case_1) ); + test->add( BOOST_TEST_CASE( & test_case_2) ); + test->add( BOOST_TEST_CASE( & test_case_3) ); + test->add( BOOST_TEST_CASE( & test_case_4) ); + test->add( BOOST_TEST_CASE( & test_case_5) ); + + return test; +} diff --git a/people/people.htm b/people/people.htm new file mode 100644 index 00000000..dba531d7 --- /dev/null +++ b/people/people.htm @@ -0,0 +1,15 @@ + + + + + +Automatic redirection failed, please go to + +http://www.boost.org/users/people.html. +
+

© Copyright Beman Dawes, 2008

+

Distributed under the Boost Software License, Version 1.0. (See +LICENSE_1_0.txt or +www.boost.org/LICENSE_1_0.txt)

+ + \ No newline at end of file