Task类中GetEvents函数返回当前除了kAlive外的所有事件标识,同时清除fEvents中除kAlive之外的所有其他标识。
class A
{
private:
public:
A()
{
fEvents = 0;
}
typedef unsigned int EventFlags;
EventFlags fEvents;
enum
{
kAlive = 0x80000000, //EventFlags, again
kAliveOff = 0x7fffffff
};
//EVENTS
//here are all the events that can be sent to a task
enum
{
kKillEvent = 0x1 << 0x0, //these are all of type "EventFlags"
kIdleEvent = 0x1 << 0x1,
kStartEvent = 0x1 << 0x2,
kTimeoutEvent = 0x1 << 0x3,
//socket events
kReadEvent = 0x1 << 0x4, //All of type "EventFlags"
kWriteEvent = 0x1 << 0x5,
//update event
kUpdateEvent = 0x1 << 0x6
};
EventFlags GetEvents()
{
//Mask off every event currently in the mask except for the alive bit, of course,
//which should remain unaffected and unreported by this call.
EventFlags events = fEvents & kAliveOff;
(void)atomic_sub(&fEvents, events);
return events;
}
};
int main(int argc, char * argv[])
{
A a;
a.fEvents = A::kKillEvent | A::kWriteEvent | A::kAlive;
cout << setbase(16) << "fEvents:" << a.fEvents << endl;
int flag = a.GetEvents();
cout << setbase(16) << "GetEvents返回值:" << flag << endl;
cout << setbase(16) << "fEvents:" << a.fEvents << endl;
return 0;
}
Task::Signal将task任务添加到线程的队列中。如果该task指定了某个线程,则添加到对应的线程的队列中。如果没有指定,则添加到线程池的某个线程队列中。而线程池的每个线程都在不停的调用void TaskThread::Entry()中的 theTask = this->WaitForTask()获取线程任务队列中的任务。
在调用void Task::Signal(EventFlags events)时 ,将fEvents设置成包含events和kAlive的事件。 events |= kAlive;
EventFlags oldEvents = atomic_or(&fEvents, events);
在Run中,所有的Task派生类都会在Run中调用GetEvents获取当前时间,同时将当前事件清空。
//监测Task中Run()函数的返回值,共有三种情况
23 //1、返回负数,表明任务已经完全结束
24 if (theTimeout < 0)
25 {
26 delete theTask; //删除Task对象
27 theTask = NULL;
28 doneProcessingEvent = true;
19 }
30 //2、返回0,表明任务希望在下次传信时被再次立即执行
31 else if (theTimeout=0)
32 {
//如果theTask->fEvents的值==Task::kAlive,则将theTask->fEvents设置为0,函数返回1,否则返回0
33 doneProcessingEvent = compare_and_store(Task::kAlive, 0, &theTask->fEvents);
34 if (doneProcessingEvent)
35 theTask = NULL;
36 }
//3、返回正数,表明任务希望在等待theTimeout时间后再次执行
37 else
38 {
/*将该任务加入到Heap中,并且纪录它希望等待的时间。Entry()函数将通过waitfortask()函数进行检测,如果等待的时间到了,就再次运行该任务*/
39 theTask->fTimerHeapElem.SetValue(OS::Milliseconds() + theTimeout);
40 fHeap.Insert(&theTask->fTimerHeapElem);
41 (void)atomic_or(&theTask->fEvents, Task::kIdleEvent);//设置Idle事件
42 doneProcessingEvent = true;
43 }
//此处略…
}
注意,如果Task的Run()函数返回值TimeOut为正数,意味着该任务是一个周期性的工作,例如发送数据的视频泵(pump),需要每隔一定时间 就发出一定量的视频数据,直至整个节目结束。为此,在第38~43行,将该任务加入到堆fHeap中去,并且标记该任务下次运行的时间为TimeOut毫 秒之后
void Task::Signal(EventFlags events)
{
if (!this->Valid())
return;
//Fancy no mutex implementation. We atomically mask the new events into
//the event mask. Because atomic_or returns the old state of the mask,
//we only schedule this task once.
events |= kAlive;
EventFlags oldEvents = atomic_or(&fEvents, events);
if ((!(oldEvents & kAlive)) && (TaskThreadPool::sNumTaskThreads > 0))
{
//指定了某个线程
if (fUseThisThread != NULL)
// Task needs to be placed on a particular thread.
{
if (TASK_DEBUG) if (fTaskName[0] == 0) ::strcpy(fTaskName, " corrupt task");
if (TASK_DEBUG) qtss_printf("Task::Signal enque TaskName=%s fUseThisThread=%lu q elem=%lu enclosing=%lu\n", fTaskName, (UInt32) fUseThisThread, (UInt32) &fTaskQueueElem, (UInt32) this);
fUseThisThread->fTaskQueue.EnQueue(&fTaskQueueElem);
}
//添加到线程池的某个线程队列中
else
{
//find a thread to put this task on
unsigned int theThread = atomic_add(&sThreadPicker, 1);
theThread %= TaskThreadPool::sNumTaskThreads;
if (TASK_DEBUG) if (fTaskName[0] == 0) ::strcpy(fTaskName, " corrupt task");
if (TASK_DEBUG) qtss_printf("Task::Signal enque TaskName=%s thread=%lu q elem=%lu enclosing=%lu\n", fTaskName, (UInt32)TaskThreadPool::sTaskThreadArray[theThread],(UInt32) &fTaskQueueElem,(UInt32) this);
TaskThreadPool::sTaskThreadArray[theThread]->fTaskQueue.EnQueue(&fTaskQueueElem);
}
}
else
if (TASK_DEBUG) qtss_printf("Task::Signal sent to dead TaskName=%s q elem=%lu enclosing=%lu\n", fTaskName, (UInt32) &fTaskQueueElem, (UInt32) this);
}
Task* TaskThread::WaitForTask()
{
while (true)
{
SInt64 theCurrentTime = OS::Milliseconds();
if ((fHeap.PeekMin() != NULL) && (fHeap.PeekMin()->GetValue() <= theCurrentTime))
{
if (TASK_DEBUG) qtss_printf("TaskThread::WaitForTask found timer-task=%s thread %lu fHeap.CurrentHeapSize(%lu) taskElem = %lu enclose=%lu\n",((Task*)fHeap.PeekMin()->GetEnclosingObject())->fTaskName, (UInt32) this, fHeap.CurrentHeapSize(), (UInt32) fHeap.PeekMin(), (UInt32) fHeap.PeekMin()->GetEnclosingObject());
return (Task*)fHeap.ExtractMin()->GetEnclosingObject();
}
//if there is an element waiting for a timeout, figure out how long we should wait.
SInt64 theTimeout = 0;
if (fHeap.PeekMin() != NULL)
theTimeout = fHeap.PeekMin()->GetValue() - theCurrentTime;
Assert(theTimeout >= 0);
//
// Make sure we can't go to sleep for some ridiculously short
// period of time
// Do not allow a timeout below 10 ms without first verifying reliable udp 1-2mbit live streams.
// Test with streamingserver.xml pref reliablUDP printfs enabled and look for packet loss and check client for buffer ahead recovery.
if (theTimeout < 10)
theTimeout = 10;
//wait...
OSQueueElem* theElem = fTaskQueue.DeQueueBlocking(this, (SInt32) theTimeout);
if (theElem != NULL)
{
if (TASK_DEBUG) qtss_printf("TaskThread::WaitForTask found signal-task=%s thread %lu fTaskQueue.GetLength(%lu) taskElem = %lu enclose=%lu\n", ((Task*)theElem->GetEnclosingObject())->fTaskName, (UInt32) this, fTaskQueue.GetQueue()->GetLength(), (UInt32) theElem, (UInt32)theElem->GetEnclosingObject() );
return (Task*)theElem->GetEnclosingObject();
}
//
// If we are supposed to stop, return NULL, which signals the caller to stop
if (OSThread::GetCurrent()->IsStopRequested())
return NULL;
}
}
/*
*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*
*/
/*
File: Task.h
Contains: Tasks are objects that can be scheduled. To schedule a task, you call its
signal method, and pass in an event (events are bits and all events are defined
below).
Once Signal() is called, the task object will be scheduled. When it runs, its
Run() function will get called. In order to clear the event, the derived task
object must call GetEvents() (which returns the events that were sent).
Calling GetEvents() implicitly "clears" the events returned. All events must
be cleared before the Run() function returns, or Run() will be invoked again
immediately.
*/
#ifndef __TASK_H__
#define __TASK_H__
#include "OSQueue.h"
#include "OSHeap.h"
#include "OSThread.h"
#include "OSMutexRW.h"
#define TASK_DEBUG 0
class TaskThread;
class Task
{
public:
typedef unsigned int EventFlags;
//EVENTS
//here are all the events that can be sent to a task
enum
{
kKillEvent = 0x1 << 0x0, //these are all of type "EventFlags"
kIdleEvent = 0x1 << 0x1,
kStartEvent = 0x1 << 0x2,
kTimeoutEvent = 0x1 << 0x3,
//socket events
kReadEvent = 0x1 << 0x4, //All of type "EventFlags"
kWriteEvent = 0x1 << 0x5,
//update event
kUpdateEvent = 0x1 << 0x6
};
//CONSTRUCTOR / DESTRUCTOR
//You must assign priority at create time.
Task();
virtual ~Task() {}
//return:
// >0-> invoke me after this number of MilSecs with a kIdleEvent
// 0 don't reinvoke me at all.
//-1 delete me
//Suggested practice is that any task should be deleted by returning true from the
//Run function. That way, we know that the Task is not running at the time it is
//deleted. This object provides no protection against calling a method, such as Signal,
//at the same time the object is being deleted (because it can't really), so watch
//those dangling references!
virtual SInt64 Run() = 0;
//Send an event to this task.
void Signal(EventFlags eventFlags);
void GlobalUnlock();
//判断fTaskName是否命名正确,如果fTaskName为空或者不是以“live_”开头,则无效
Bool16 Valid(); // for debugging
//任务名称
char fTaskName[48];
//设置fTaskName,fTaskName命令方式:live_name
void SetTaskName(char* name);
protected:
//Only the tasks themselves may find out what events they have received
EventFlags GetEvents();
// ForceSameThread
//
// A task, inside its run function, may want to ensure that the same task thread
// is used for subsequent calls to Run(). This may be the case if the task is holding
// a mutex between calls to run. By calling this function, the task ensures that the
// same task thread will be used for the next call to Run(). It only applies to the
// next call to run.
void ForceSameThread() {
fUseThisThread = (TaskThread*)OSThread::GetCurrent();
Assert(fUseThisThread != NULL);
if (TASK_DEBUG) if (fTaskName[0] == 0) ::strcpy(fTaskName, " corrupt task");
if (TASK_DEBUG) qtss_printf("Task::ForceSameThread fUseThisThread %lu task %s enque elem=%lu enclosing %lu\n", (UInt32)fUseThisThread, fTaskName,(UInt32) &fTaskQueueElem,(UInt32) this);
}
SInt64 CallLocked() { ForceSameThread();
fWriteLock = true;
return (SInt64) 10; // minimum of 10 milliseconds between locks
}
private:
enum
{
kAlive = 0x80000000, //EventFlags, again
kAliveOff = 0x7fffffff
};
void SetTaskThread(TaskThread *thread);
EventFlags fEvents;
TaskThread* fUseThisThread;
Bool16 fWriteLock;
#if DEBUG
//The whole premise of a task is that the Run function cannot be re-entered.
//This debugging variable ensures that that is always the case
volatile UInt32 fInRunCount;
#endif
//This could later be optimized by using a timing wheel instead of a heap,
//and that way we wouldn't need both a heap elem and a queue elem here (just queue elem)
OSHeapElem fTimerHeapElem;
OSQueueElem fTaskQueueElem;
//Variable used for assigning tasks to threads in a round-robin fashion
static unsigned int sThreadPicker;
friend class TaskThread;
};
class TaskThread : public OSThread
{
public:
//Implementation detail: all tasks get run on TaskThreads.
TaskThread() : OSThread(), fTaskThreadPoolElem()
{fTaskThreadPoolElem.SetEnclosingObject(this);}
virtual ~TaskThread() { this->StopAndWaitForThread(); }
private:
enum
{
kMinWaitTimeInMilSecs = 10 //UInt32
};
virtual void Entry();
Task* WaitForTask();
OSQueueElem fTaskThreadPoolElem;
OSHeap fHeap;
OSQueue_Blocking fTaskQueue;
friend class Task;
friend class TaskThreadPool;
};
//Because task threads share a global queue of tasks to execute,
//there can only be one pool of task threads. That is why this object
//is static.
class TaskThreadPool {
public:
//Adds some threads to the pool
static Bool16 AddThreads(UInt32 numToAdd);
static void SwitchPersonality( char *user = NULL, char *group = NULL);
static void RemoveThreads();
private:
static TaskThread** sTaskThreadArray;
static UInt32 sNumTaskThreads;
static OSMutexRW sMutexRW;
friend class Task;
friend class TaskThread;
};
#endif
/*
*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*
*/
/*
File: Task.cpp
Contains: implements Task class
*/
#include "Task.h"
#include "OS.h"
#include "OSMemory.h"
#include "atomic.h"
#include "OSMutexRW.h"
unsigned int Task::sThreadPicker = 0;
OSMutexRW TaskThreadPool::sMutexRW;
static char* sTaskStateStr="live_"; //Alive
Task::Task()
: fEvents(0), fUseThisThread(NULL), fWriteLock(false), fTimerHeapElem(), fTaskQueueElem()
{
#if DEBUG
fInRunCount = 0;
#endif
this->SetTaskName("unknown");
fTaskQueueElem.SetEnclosingObject(this);
fTimerHeapElem.SetEnclosingObject(this);
}
void Task::SetTaskName(char* name)
{
if (name == NULL)
return;
::strncpy(fTaskName,sTaskStateStr,sizeof(fTaskName));
::strncat(fTaskName,name,sizeof(fTaskName));
fTaskName[sizeof(fTaskName) -1] = 0; //terminate in case it is longer than ftaskname.
}
Bool16 Task::Valid()
{
if ( (this->fTaskName == NULL)
|| (0 != ::strncmp(sTaskStateStr,this->fTaskName, 5))
)
{
if (TASK_DEBUG) qtss_printf(" Task::Valid Found invalid task = %ld\n", this);
return false;
}
return true;
}
Task::EventFlags Task::GetEvents()
{
//Mask off every event currently in the mask except for the alive bit, of course,
//which should remain unaffected and unreported by this call.
EventFlags events = fEvents & kAliveOff;
(void)atomic_sub(&fEvents, events);
return events;
}
void Task::Signal(EventFlags events)
{
if (!this->Valid())
return;
//Fancy no mutex implementation. We atomically mask the new events into
//the event mask. Because atomic_or returns the old state of the mask,
//we only schedule this task once.
events |= kAlive;
EventFlags oldEvents = atomic_or(&fEvents, events);
if ((!(oldEvents & kAlive)) && (TaskThreadPool::sNumTaskThreads > 0))
{
if (fUseThisThread != NULL)
// Task needs to be placed on a particular thread.
{
if (TASK_DEBUG) if (fTaskName[0] == 0) ::strcpy(fTaskName, " corrupt task");
if (TASK_DEBUG) qtss_printf("Task::Signal enque TaskName=%s fUseThisThread=%lu q elem=%lu enclosing=%lu\n", fTaskName, (UInt32) fUseThisThread, (UInt32) &fTaskQueueElem, (UInt32) this);
fUseThisThread->fTaskQueue.EnQueue(&fTaskQueueElem);
}
else
{
//find a thread to put this task on
unsigned int theThread = atomic_add(&sThreadPicker, 1);
theThread %= TaskThreadPool::sNumTaskThreads;
if (TASK_DEBUG) if (fTaskName[0] == 0) ::strcpy(fTaskName, " corrupt task");
if (TASK_DEBUG) qtss_printf("Task::Signal enque TaskName=%s thread=%lu q elem=%lu enclosing=%lu\n", fTaskName, (UInt32)TaskThreadPool::sTaskThreadArray[theThread],(UInt32) &fTaskQueueElem,(UInt32) this);
TaskThreadPool::sTaskThreadArray[theThread]->fTaskQueue.EnQueue(&fTaskQueueElem);
}
}
else
if (TASK_DEBUG) qtss_printf("Task::Signal sent to dead TaskName=%s q elem=%lu enclosing=%lu\n", fTaskName, (UInt32) &fTaskQueueElem, (UInt32) this);
}
void Task::GlobalUnlock()
{
if (this->fWriteLock)
{ this->fWriteLock = false;
TaskThreadPool::sMutexRW.Unlock();
}
}
void TaskThread::Entry()
{
Task* theTask = NULL;
while (true)
{
theTask = this->WaitForTask();
//
// WaitForTask returns NULL when it is time to quit
if (theTask == NULL || false == theTask->Valid() )
return;
Bool16 doneProcessingEvent = false;
while (!doneProcessingEvent)
{
//If a task holds locks when it returns from its Run function,
//that would be catastrophic and certainly lead to a deadlock
#if DEBUG
Assert(this->GetNumLocksHeld() == 0);
Assert(theTask->fInRunCount == 0);
theTask->fInRunCount++;
#endif
theTask->fUseThisThread = NULL; // Each invocation of Run must independently
// request a specific thread.
SInt64 theTimeout = 0;
if (theTask->fWriteLock)
{
OSMutexWriteLocker mutexLocker(&TaskThreadPool::sMutexRW);
if (TASK_DEBUG) qtss_printf("TaskThread::Entry run global locked TaskName=%s CurMSec=%.3f thread=%ld task=%ld\n", theTask->fTaskName, OS::StartTimeMilli_Float() ,(SInt32) this,(SInt32) theTask);
theTimeout = theTask->Run();
theTask->fWriteLock = false;
}
else
{
OSMutexReadLocker mutexLocker(&TaskThreadPool::sMutexRW);
if (TASK_DEBUG) qtss_printf("TaskThread::Entry run TaskName=%s CurMSec=%.3f thread=%ld task=%ld\n", theTask->fTaskName, OS::StartTimeMilli_Float(), (SInt32) this,(SInt32) theTask);
theTimeout = theTask->Run();
}
#if DEBUG
Assert(this->GetNumLocksHeld() == 0);
theTask->fInRunCount--;
Assert(theTask->fInRunCount == 0);
#endif
if (theTimeout < 0)
{
if (TASK_DEBUG)
{
qtss_printf("TaskThread::Entry delete TaskName=%s CurMSec=%.3f thread=%ld task=%ld\n", theTask->fTaskName, OS::StartTimeMilli_Float(), (SInt32) this, (SInt32) theTask);
theTask->fUseThisThread = NULL;
if (NULL != fHeap.Remove(&theTask->fTimerHeapElem))
qtss_printf("TaskThread::Entry task still in heap before delete\n");
if (NULL != theTask->fTaskQueueElem.InQueue())
qtss_printf("TaskThread::Entry task still in queue before delete\n");
theTask->fTaskQueueElem.Remove();
if (theTask->fEvents &~ Task::kAlive)
qtss_printf ("TaskThread::Entry flags still set before delete\n");
(void)atomic_sub(&theTask->fEvents, 0);
::strncat (theTask->fTaskName, " deleted", sizeof(theTask->fTaskName) -1);
}
theTask->fTaskName[0] = 'D'; //mark as dead
delete theTask;
theTask = NULL;
doneProcessingEvent = true;
}
else if (theTimeout == 0)
{
//We want to make sure that 100% definitely the task's Run function WILL
//be invoked when another thread calls Signal. We also want to make sure
//that if an event sneaks in right as the task is returning from Run()
//(via Signal) that the Run function will be invoked again.
doneProcessingEvent = compare_and_store(Task::kAlive, 0, &theTask->fEvents);
if (doneProcessingEvent)
theTask = NULL;
}
else
{
//note that if we get here, we don't reset theTask, so it will get passed into
//WaitForTask
if (TASK_DEBUG) qtss_printf("TaskThread::Entry insert TaskName=%s in timer heap thread=%lu elem=%lu task=%ld timeout=%.2f\n", theTask->fTaskName, (UInt32) this, (UInt32) &theTask->fTimerHeapElem,(SInt32) theTask, (float)theTimeout / (float) 1000);
theTask->fTimerHeapElem.SetValue(OS::Milliseconds() + theTimeout);
fHeap.Insert(&theTask->fTimerHeapElem);
(void)atomic_or(&theTask->fEvents, Task::kIdleEvent);
doneProcessingEvent = true;
}
#if TASK_DEBUG
SInt64 yieldStart = OS::Milliseconds();
#endif
this->ThreadYield();
#if TASK_DEBUG
SInt64 yieldDur = OS::Milliseconds() - yieldStart;
static SInt64 numZeroYields;
if ( yieldDur > 1 )
{
if (TASK_DEBUG) qtss_printf( "TaskThread::Entry time in Yield %i, numZeroYields %i\n", (long)yieldDur, (long)numZeroYields );
numZeroYields = 0;
}
else
numZeroYields++;
#endif
}
}
}
Task* TaskThread::WaitForTask()
{
while (true)
{
SInt64 theCurrentTime = OS::Milliseconds();
if ((fHeap.PeekMin() != NULL) && (fHeap.PeekMin()->GetValue() <= theCurrentTime))
{
if (TASK_DEBUG) qtss_printf("TaskThread::WaitForTask found timer-task=%s thread %lu fHeap.CurrentHeapSize(%lu) taskElem = %lu enclose=%lu\n",((Task*)fHeap.PeekMin()->GetEnclosingObject())->fTaskName, (UInt32) this, fHeap.CurrentHeapSize(), (UInt32) fHeap.PeekMin(), (UInt32) fHeap.PeekMin()->GetEnclosingObject());
return (Task*)fHeap.ExtractMin()->GetEnclosingObject();
}
//if there is an element waiting for a timeout, figure out how long we should wait.
SInt64 theTimeout = 0;
if (fHeap.PeekMin() != NULL)
theTimeout = fHeap.PeekMin()->GetValue() - theCurrentTime;
Assert(theTimeout >= 0);
//
// Make sure we can't go to sleep for some ridiculously short
// period of time
// Do not allow a timeout below 10 ms without first verifying reliable udp 1-2mbit live streams.
// Test with streamingserver.xml pref reliablUDP printfs enabled and look for packet loss and check client for buffer ahead recovery.
if (theTimeout < 10)
theTimeout = 10;
//wait...
OSQueueElem* theElem = fTaskQueue.DeQueueBlocking(this, (SInt32) theTimeout);
if (theElem != NULL)
{
if (TASK_DEBUG) qtss_printf("TaskThread::WaitForTask found signal-task=%s thread %lu fTaskQueue.GetLength(%lu) taskElem = %lu enclose=%lu\n", ((Task*)theElem->GetEnclosingObject())->fTaskName, (UInt32) this, fTaskQueue.GetQueue()->GetLength(), (UInt32) theElem, (UInt32)theElem->GetEnclosingObject() );
return (Task*)theElem->GetEnclosingObject();
}
//
// If we are supposed to stop, return NULL, which signals the caller to stop
if (OSThread::GetCurrent()->IsStopRequested())
return NULL;
}
}
TaskThread** TaskThreadPool::sTaskThreadArray = NULL;
UInt32 TaskThreadPool::sNumTaskThreads = 0;
Bool16 TaskThreadPool::AddThreads(UInt32 numToAdd)
{
Assert(sTaskThreadArray == NULL);
sTaskThreadArray = new TaskThread*[numToAdd];
for (UInt32 x = 0; x < numToAdd; x++)
{
sTaskThreadArray[x] = NEW TaskThread();
sTaskThreadArray[x]->Start();
}
sNumTaskThreads = numToAdd;
return true;
}
void TaskThreadPool::RemoveThreads()
{
//Tell all the threads to stop
for (UInt32 x = 0; x < sNumTaskThreads; x++)
sTaskThreadArray[x]->SendStopRequest();
//Because any (or all) threads may be blocked on the queue, cycle through
//all the threads, signalling each one
for (UInt32 y = 0; y < sNumTaskThreads; y++)
sTaskThreadArray[y]->fTaskQueue.GetCond()->Signal();
//Ok, now wait for the selected threads to terminate, deleting them and removing
//them from the queue.
for (UInt32 z = 0; z < sNumTaskThreads; z++)
delete sTaskThreadArray[z];
sNumTaskThreads = 0;
}