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;
}