annotate thread/AsynchronousTask.h @ 69:6afa0e011f74

* Fix some locking problems
author cannam
date Thu, 21 May 2009 16:39:35 +0000
parents 09aba2ccd94a
children 701233f8ed41
rev   line source
cannam@67 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@67 2
cannam@67 3 /*
cannam@67 4 QM DSP Library
cannam@67 5
cannam@67 6 Centre for Digital Music, Queen Mary, University of London.
cannam@67 7 This file Copyright 2009 QMUL.
cannam@67 8 */
cannam@67 9
cannam@67 10 #ifndef _ASYNCHRONOUS_TASK_H_
cannam@67 11 #define _ASYNCHRONOUS_TASK_H_
cannam@67 12
cannam@67 13 #include "Thread.h"
cannam@67 14
cannam@69 15 #include <iostream>
cannam@69 16
cannam@67 17 /**
cannam@67 18 * AsynchronousTask provides a thread pattern implementation for
cannam@67 19 * threads which are used to perform a series of similar operations in
cannam@67 20 * parallel with other threads of the same type.
cannam@67 21 *
cannam@67 22 * For example, a thread used to calculate FFTs of a particular block
cannam@67 23 * size in the context of a class that needs to calculate many block
cannam@67 24 * sizes of FFT at once may be a candidate for an AsynchronousTask.
cannam@67 25 *
cannam@67 26 * The general use pattern is:
cannam@67 27 *
cannam@67 28 * caller -> request thread A calculate something
cannam@67 29 * caller -> request thread B calculate something
cannam@67 30 * caller -> request thread C calculate something
cannam@67 31 * caller -> wait for threads A, B, and C
cannam@67 32 *
cannam@67 33 * Here threads A, B, and C may be AsynchronousTasks. An important
cannam@67 34 * point is that the caller must be prepared to block when waiting for
cannam@67 35 * these threads to complete (i.e. they are started asynchronously,
cannam@67 36 * but testing for completion is synchronous).
cannam@67 37 */
cannam@67 38 class AsynchronousTask : public Thread
cannam@67 39 {
cannam@67 40 public:
cannam@67 41 AsynchronousTask() :
cannam@67 42 m_todo("AsynchronousTask: task to perform"),
cannam@67 43 m_done("AsynchronousTask: task complete"),
cannam@67 44 m_inTask(false),
cannam@67 45 m_finishing(false)
cannam@67 46 {
cannam@67 47 start();
cannam@67 48 }
cannam@67 49 virtual ~AsynchronousTask()
cannam@67 50 {
cannam@69 51 m_todo.lock();
cannam@67 52 m_finishing = true;
cannam@67 53 m_todo.signal();
cannam@69 54 m_todo.unlock();
cannam@67 55 wait();
cannam@67 56 }
cannam@67 57
cannam@67 58 // Subclass must provide methods to request task and obtain
cannam@67 59 // results, which the caller calls. The method that requests a
cannam@67 60 // new task should set up any internal state and call startTask(),
cannam@67 61 // which then calls back on the subclass implementation of
cannam@67 62 // performTask from within its work thread. The method that
cannam@67 63 // obtains results should call awaitTask() and then return any
cannam@67 64 // results from internal state.
cannam@67 65
cannam@67 66 protected:
cannam@67 67 void startTask() {
cannam@69 68 m_done.lock();
cannam@67 69 m_todo.lock();
cannam@67 70 m_inTask = true;
cannam@67 71 m_todo.signal();
cannam@67 72 m_todo.unlock();
cannam@67 73 }
cannam@67 74 void awaitTask() {
cannam@69 75 m_done.wait();
cannam@67 76 m_done.unlock();
cannam@67 77 }
cannam@67 78
cannam@67 79 virtual void performTask() = 0;
cannam@67 80
cannam@67 81 private:
cannam@67 82 virtual void run() {
cannam@67 83 m_todo.lock();
cannam@69 84 while (1) {
cannam@67 85 while (!m_inTask && !m_finishing) {
cannam@67 86 m_todo.wait();
cannam@67 87 }
cannam@67 88 if (m_finishing) {
cannam@69 89 m_done.lock();
cannam@68 90 m_inTask = false;
cannam@68 91 m_done.signal();
cannam@69 92 m_done.unlock();
cannam@67 93 break;
cannam@67 94 }
cannam@69 95 performTask();
cannam@69 96 m_done.lock();
cannam@69 97 m_inTask = false;
cannam@69 98 m_done.signal();
cannam@69 99 m_done.unlock();
cannam@67 100 }
cannam@67 101 m_todo.unlock();
cannam@67 102 }
cannam@67 103
cannam@67 104 Condition m_todo;
cannam@67 105 Condition m_done;
cannam@67 106 bool m_inTask;
cannam@67 107 bool m_finishing;
cannam@67 108 };
cannam@67 109
cannam@67 110 #endif