Example Programs

Active Objects

active_object_example.cpp

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

pipeline.cpp

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

transform.cpp

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

Monitor Objects

monitor_demo.cpp

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

Mutex Debugging

acyclic_mutex_demo.cpp

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