async/callback.cpp

This is a simple example showing how to send work to some thread and receive the results in a callback.

#include <iostream>
#include <list>
#include <boost/bind.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/mutex.hpp>
#include <toast/async/worker_pool.hpp>
#include <toast/async/thread_specific_request_queue.hpp>

boost::condition condition;
boost::mutex mutex;

/* This function will be called when the worker thread pushes the callback
 * bound with the result of do_work onto the thread_specific_request_queue
 * for the main thread (the thread that called make_request).  This is
 * actually called on the worker thread and so whatever communication
 * mechanims it uses to notify the main thread must be syncrhonized.  In
 * this case I'm using a boost::condition, if I had used pipes and polls they
 * would be synchronized for me by the OS.
 */
void notify()
{
  boost::mutex::scoped_lock lock(mutex);
  std::cout << "YooHoo, I've put data on your queue!" << std::endl;
  condition.notify_one();
}

/* This is the work component to be done on the worker thread */
int do_work(int i, int j)
{
  std::cout << "So... you want to know what " << i << " + " << j 
            << " is do ya?" << std::endl;
  return i + j;
}

/* This is the callback component to be done back on the main thread
 * (the thread that called make_request).
 */ 
void callback(int i)
{
  std::cout << "How would I have ever known that it was " << i 
            << " without toast::async?" << std::endl;
}

int main()
{
  toast::async::init_default_worker_pool(3, true);

  /* ask the thread_specific_request_queue to call this function whenever
   * something is pushed onto it.
   */
  toast::async::thread_specific_request_queue::on_push(&notify);

  /* Here the magical request is made.  Look at the make_request.cpp example
   * to get an idea of how make_request makes all this happen.
   */
  toast::async::request r =
    toast::async::make_request<int>(boost::bind(&do_work, 5, 12), &callback);
  toast::async::default_worker_pool().push(r);
 
  /* A list is used for efficient transfer of ownership of requests between
   * 'queues'.  Copying lots of data in a critical section is generally not
   * a good idea, so using the std::list eliminates the need to copy anything.
   */
  std::list<toast::async::request> current;
  
  // synchronize the condition variable
  boost::mutex::scoped_lock lock(mutex);
  while(current.empty()) {
    // pop gets everything off the queue and puts it in the list
    toast::async::thread_specific_request_queue::pop(current);
    if(current.empty()) condition.wait(lock);
  }
  /* generally just calling front like this wouldn't be natural.  I know there
   * is exactly one element though, so I look at it and call it's operator().
   */
  current.front()();
}

SourceForge.net Logo