Download active_object_example.cpp.
// An toy example showing how a full active object class can be defined // from a servant class, a scheduler, and active_functions. // The active_functions all share the same scheduler, so the // methods of the servant class are only called by a single // scheduler thread. // Copyright (C) Frank Mori Hess 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) #include <boost/shared_ptr.hpp> #include <poet/active_function.hpp> #include <iostream> // servant class template <typename T> class passive_value { public: passive_value(const T& initial_value): _value(initial_value) {} const T& add(const T& x) { _value += x; return _value; } const T& multiply(const T& x) { _value *= x; return _value; } const T& get() const { return _value; } private: T _value; }; // active object class template <typename T> class active_value { private: boost::shared_ptr<passive_value<T> > _servant; boost::shared_ptr<poet::scheduler> _scheduler; public: typedef typename poet::active_function<T (T)> add_type; typedef typename poet::active_function<T (T)> multiply_type; typedef typename poet::active_function<T ()> get_type; active_value(const T& initial_value): _servant(new passive_value<T>(initial_value)), _scheduler(new poet::scheduler), add(typename add_type::passive_slot_type(&passive_value<T>::add, _servant, _1), _scheduler), multiply(typename multiply_type::passive_slot_type(&passive_value<T>::multiply, _servant, _1), _scheduler), get(typename get_type::passive_slot_type(&passive_value<T>::get, _servant), _scheduler) {} add_type add; multiply_type multiply; get_type get; }; int main() { active_value<int> my_active_object(1); std::cout << "initial value is " << my_active_object.get().get() << std::endl; my_active_object.add(10); std::cout << "adding 10 and multiplying by 2 gives " << my_active_object.multiply(2).get() << std::endl; return 0; }
Download pipeline.cpp.
// A toy example that calculates the length of a series of // 2 dimensional vectors concurrently using 4 threads. // A real program more concerned with performance // would perform less trivial operations in the active_functions, // to reduce the active_function calling overhead. // Copyright (C) Frank Mori Hess 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) #include <boost/array.hpp> #include <cmath> #include <functional> #include <iostream> #include <poet/active_function.hpp> #include <vector> double square_root(double a) { return std::sqrt(a); } int main() { static const unsigned num_vectors = 10; unsigned i; // input data, assigned actual values later std::vector<boost::array<poet::promise<double>, 2> > promises; // we push the promises on onto the std::vector one at a time to make // sure the std::vector doesn't copy-construct them from each other. for(i = 0; i < num_vectors; ++i) { promises.push_back(boost::array<poet::promise<double>, 2>()); } // active_functions poet::active_function<double (double, double)> active_adder((std::plus<double>())); poet::active_function<double (double, double)> active_multiplier0((std::multiplies<double>())); poet::active_function<double (double, double)> active_multiplier1((std::multiplies<double>())); poet::active_function<double (double)> active_sqrt(&square_root); std::vector<poet::future<double> > lengths; // pass promises through our active_function pipeline for(i = 0; i < promises.size(); ++i) { poet::future<double> product0 = active_multiplier0(promises.at(i).at(0), promises.at(i).at(0)); poet::future<double> product1 = active_multiplier1(promises.at(i).at(1), promises.at(i).at(1)); poet::future<double> sum = active_adder(product0, product1); poet::future<double> root = active_sqrt(sum); lengths.push_back(root); } // fulfill input promises for(i = 0; i < promises.size(); ++i) { promises.at(i).at(0).fulfill(i % 4); promises.at(i).at(1).fulfill(i % 3); } // wait for futures to become ready and convert them to values for(i = 0; i < lengths.size(); ++i) { boost::array<poet::future<double>, 2> vec; vec.at(0) = promises.at(i).at(0); vec.at(1) = promises.at(i).at(1); double value = lengths.at(i); std::cout << "vector " << i << " = {" << vec.at(0) << ", " << vec.at(1) << "}, length = " << value << "\n"; } return 0; }
Download transform.cpp.
// An example which passes an active_function as the functor // used by std::transform, in order to perform a transformation // on containers of futures. // Copyright (C) Frank Mori Hess 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) #include <cmath> #include <functional> #include <iostream> #include <poet/active_function.hpp> #include <vector> int main() { unsigned i; const unsigned vector_length = 10; // input data, assigned actual values later std::vector<poet::promise<double> > x_promises; std::vector<poet::promise<double> > y_promises; // we push the promises on onto the std::vector one at a time to make // sure the std::vector doesn't copy-construct them from each other. for(i = 0; i < vector_length; ++i) { x_promises.push_back(poet::promise<double>()); y_promises.push_back(poet::promise<double>()); } // active_functions std::plus<double> passive_adder; poet::active_function<double (double, double)> active_adder(passive_adder); std::vector<poet::future<double> > x; std::copy(x_promises.begin(), x_promises.end(), std::back_inserter(x)); std::vector<poet::future<double> > y; std::copy(y_promises.begin(), y_promises.end(), std::back_inserter(y)); std::vector<poet::future<double> > sum; std::transform(x.begin(), x.end(), y.begin(), std::back_inserter(sum), active_adder); // fulfill input promises for(i = 0; i < x_promises.size(); ++i) { x_promises.at(i).fulfill(i % 4); y_promises.at(i).fulfill(i % 3); } // wait for futures to become ready and convert them to values for(i = 0; i < sum.size(); ++i) { std::cout << "sum " << i << " = " << sum.at(i).get() << "\n"; } return 0; }
Download monitor_demo.cpp.
// An example of using the monitor_ptr and monitor classes // for automatically locked access to an object and waiting // on the monitor's condition variable. // Copyright (C) Frank Mori Hess 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) #include <boost/bind.hpp> #include <boost/ref.hpp> #include <boost/thread.hpp> #include <boost/thread/mutex.hpp> #include <iostream> #include <poet/monitor.hpp> #include <poet/monitor_ptr.hpp> #include <poet/monitor_base.hpp> class Monitored: public poet::monitor_base { public: Monitored(): _state(0) {} void wait_for_state(int state) { std::cerr << "thread " << boost::this_thread::get_id() << ": " << __FUNCTION__ << ": waiting for state = " << state << std::endl; while(_state != state) wait(); std::cerr << "thread " << boost::this_thread::get_id() << ": " << __FUNCTION__ << ": have state = " << state << std::endl; } void set_state(int state) { std::cerr << "thread " << boost::this_thread::get_id() << ": " << __FUNCTION__ << ": state = " << state << std::endl; _state = state; notify_all(); } private: int _state; }; //example of using poet::monitor_ptr typedef poet::monitor_ptr<Monitored> monitor_ptr_type; void monitor_ptr_thread0_function(monitor_ptr_type mymonitor) { poet::monitor_unique_lock<monitor_ptr_type> mon_lock(mymonitor); mon_lock->set_state(1); mon_lock->wait_for_state(2); mon_lock->set_state(3); mon_lock->wait_for_state(4); } void monitor_ptr_thread1_function(monitor_ptr_type mymonitor) { mymonitor->wait_for_state(1); { poet::monitor_unique_lock<monitor_ptr_type> mon_lock(mymonitor); mon_lock->set_state(2); mon_lock->wait_for_state(3); } mymonitor->set_state(4); } int main() { typedef poet::monitor<Monitored> monitor_type; monitor_type mymonitor; boost::thread thread0(boost::bind(&monitor_ptr_thread0_function, mymonitor.get_monitor_ptr())); boost::thread thread1(boost::bind(&monitor_ptr_thread1_function, mymonitor.get_monitor_ptr())); thread0.join(); thread1.join(); return 0; }
The acyclic_mutex_demo example program writes the following graph to stdout (after processing into PNG format with the "dot" program from the graphviz package):
Download acyclic_mutex_demo.cpp.
// An example of using ayclic_mutex to debug mutex // locking order violations. // Copyright (C) Frank Mori Hess 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) #include <boost/thread/locks.hpp> #include <cstdlib> #include <poet/acyclic_mutex.hpp> #include <iostream> typedef poet::acyclic_mutex<boost::mutex> mutex_type; /* dumps mutexes locked by current thread to stderr, and a graphviz file to stdout. */ void cycle_handler() { poet::mutex_grapher::unique_lock grapher; std::cerr << "Detected cycle in locking order graph.\n" << "Currently locked mutexes:"; poet::mutex_grapher::mutex_list_type::const_iterator it; for(it = grapher->locked_mutexes().begin(); it != grapher->locked_mutexes().end(); ++it) { std::cerr << "\n\tname = \"" << grapher->graph()[(*it)->vertex().get()].name << "\""; } std::cerr << std::endl; grapher->write_graphviz(std::cout); exit(1); } int main() { // replace the default cycle handler (which aborts the program) { poet::mutex_grapher::unique_lock grapher; grapher->set_cycle_handler(&cycle_handler); } mutex_type mutex_a("a"); mutex_type mutex_b("b"); mutex_type mutex_x; { boost::unique_lock<mutex_type> lock_a(mutex_a); boost::unique_lock<mutex_type> lock_x(mutex_x); } { boost::unique_lock<mutex_type> lock_x(mutex_x); boost::unique_lock<mutex_type> lock_b(mutex_b); /* this will violate the locking order we have established, and will result in the cycle handler being called */ boost::unique_lock<mutex_type> lock_a(mutex_a); } return 0; }