* Implemented Worker class, which executes Jobs in a separate thread.
* Implemented a job for getting a thread's CPU state. * The team debugger uses a worker now. ATM only for getting the CPU state for stopped threads. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31116 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
3609af391d
commit
495676cab6
|
@ -23,11 +23,13 @@ Application Debugger :
|
|||
# ElfFile.cpp
|
||||
Image.cpp
|
||||
ImageInfo.cpp
|
||||
Jobs.cpp
|
||||
Team.cpp
|
||||
TeamDebugger.cpp
|
||||
TeamDebugModel.cpp
|
||||
Thread.cpp
|
||||
ThreadInfo.cpp
|
||||
Worker.cpp
|
||||
|
||||
# arch
|
||||
Architecture.cpp
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "Jobs.h"
|
||||
|
||||
#include <AutoLocker.h>
|
||||
|
||||
#include "CpuState.h"
|
||||
#include "DebuggerInterface.h"
|
||||
#include "Team.h"
|
||||
#include "Thread.h"
|
||||
|
||||
|
||||
GetCpuStateJob::GetCpuStateJob(DebuggerInterface* debuggerInterface,
|
||||
Thread* thread)
|
||||
:
|
||||
fDebuggerInterface(debuggerInterface),
|
||||
fThread(thread)
|
||||
{
|
||||
fThread->AddReference();
|
||||
}
|
||||
|
||||
|
||||
GetCpuStateJob::~GetCpuStateJob()
|
||||
{
|
||||
fThread->RemoveReference();
|
||||
}
|
||||
|
||||
|
||||
JobKey
|
||||
GetCpuStateJob::Key() const
|
||||
{
|
||||
return JobKey(fThread, JOB_TYPE_GET_CPU_STATE);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
GetCpuStateJob::Do()
|
||||
{
|
||||
CpuState* state;
|
||||
status_t error = fDebuggerInterface->GetCpuState(fThread->ID(), state);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
Reference<CpuState> reference(state);
|
||||
|
||||
AutoLocker<Team> locker(fThread->GetTeam());
|
||||
|
||||
if (fThread->State() == THREAD_STATE_STOPPED)
|
||||
fThread->SetCpuState(state);
|
||||
|
||||
return B_OK;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef JOBS_H
|
||||
#define JOBS_H
|
||||
|
||||
#include "Worker.h"
|
||||
|
||||
|
||||
class DebuggerInterface;
|
||||
class Thread;
|
||||
|
||||
|
||||
// job types
|
||||
enum {
|
||||
JOB_TYPE_GET_CPU_STATE
|
||||
};
|
||||
|
||||
|
||||
class GetCpuStateJob : public Job {
|
||||
public:
|
||||
GetCpuStateJob(
|
||||
DebuggerInterface* debuggerInterface,
|
||||
Thread* thread);
|
||||
virtual ~GetCpuStateJob();
|
||||
|
||||
virtual JobKey Key() const;
|
||||
virtual status_t Do();
|
||||
|
||||
private:
|
||||
DebuggerInterface* fDebuggerInterface;
|
||||
Thread* fThread;
|
||||
};
|
||||
|
||||
|
||||
#endif // JOBS_H
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "CpuState.h"
|
||||
#include "DebuggerInterface.h"
|
||||
#include "Jobs.h"
|
||||
#include "MessageCodes.h"
|
||||
#include "Team.h"
|
||||
#include "TeamDebugModel.h"
|
||||
|
@ -29,6 +30,7 @@ TeamDebugger::TeamDebugger()
|
|||
fDebugModel(NULL),
|
||||
fTeamID(-1),
|
||||
fDebuggerInterface(NULL),
|
||||
fWorker(NULL),
|
||||
fDebugEventListener(-1),
|
||||
fTeamWindow(NULL),
|
||||
fTerminating(false)
|
||||
|
@ -43,15 +45,17 @@ TeamDebugger::~TeamDebugger()
|
|||
fTerminating = true;
|
||||
|
||||
fDebuggerInterface->Close();
|
||||
fWorker->ShutDown();
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
if (fDebugEventListener >= 0)
|
||||
wait_for_thread(fDebugEventListener, NULL);
|
||||
|
||||
delete fDebuggerInterface;
|
||||
delete fWorker;
|
||||
delete fDebugModel;
|
||||
delete fTeam;
|
||||
delete fDebuggerInterface;
|
||||
}
|
||||
|
||||
|
||||
|
@ -86,6 +90,15 @@ TeamDebugger::Init(team_id teamID, thread_id threadID, bool stopInMain)
|
|||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// create our worker
|
||||
fWorker = new(std::nothrow) Worker;
|
||||
if (fWorker == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
error = fWorker->Init();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// create debugger interface
|
||||
fDebuggerInterface = new(std::nothrow) DebuggerInterface(fTeamID);
|
||||
if (fDebuggerInterface == NULL)
|
||||
|
@ -207,6 +220,27 @@ TeamDebugger::TeamWindowQuitRequested(TeamWindow* window)
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
TeamDebugger::JobDone(Job* job)
|
||||
{
|
||||
printf("TeamDebugger::JobDone(%p)\n", job);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TeamDebugger::JobFailed(Job* job)
|
||||
{
|
||||
printf("TeamDebugger::JobFailed(%p)\n", job);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TeamDebugger::JobAborted(Job* job)
|
||||
{
|
||||
printf("TeamDebugger::JobAborted(%p)\n", job);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ status_t
|
||||
TeamDebugger::_DebugEventListenerEntry(void* data)
|
||||
{
|
||||
|
@ -335,14 +369,7 @@ TeamDebugger::_HandleThreadStopped(thread_id threadID, CpuState* cpuState)
|
|||
if (thread == NULL)
|
||||
return false;
|
||||
|
||||
// update the thread state
|
||||
thread->SetState(THREAD_STATE_STOPPED);
|
||||
|
||||
if (cpuState != NULL) {
|
||||
thread->SetCpuState(cpuState);
|
||||
} else {
|
||||
// TODO: Trigger updating the CPU state!
|
||||
}
|
||||
_SetThreadState(thread, THREAD_STATE_STOPPED, cpuState);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -435,18 +462,42 @@ TeamDebugger::_HandleImageDeleted(ImageDeletedEvent* event)
|
|||
void
|
||||
TeamDebugger::_UpdateThreadState(::Thread* thread)
|
||||
{
|
||||
CpuState* state = NULL;
|
||||
status_t error = fDebuggerInterface->GetCpuState(thread->ID(), state);
|
||||
CpuState* cpuState = NULL;
|
||||
status_t error = fDebuggerInterface->GetCpuState(thread->ID(), cpuState);
|
||||
|
||||
uint32 newState = THREAD_STATE_UNKNOWN;
|
||||
if (error == B_OK) {
|
||||
newState = THREAD_STATE_STOPPED;
|
||||
state->RemoveReference();
|
||||
cpuState->RemoveReference();
|
||||
} else if (error == B_BAD_THREAD_STATE)
|
||||
newState = THREAD_STATE_RUNNING;
|
||||
|
||||
thread->SetState(newState);
|
||||
thread->SetCpuState(state);
|
||||
_SetThreadState(thread, newState, cpuState);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TeamDebugger::_SetThreadState(::Thread* thread, uint32 state,
|
||||
CpuState* cpuState)
|
||||
{
|
||||
// update the thread state
|
||||
uint32 oldState = thread->State();
|
||||
thread->SetState(state);
|
||||
|
||||
// cancel jobs for this thread
|
||||
if (oldState == THREAD_STATE_STOPPED)
|
||||
fWorker->AbortJob(JobKey(thread, JOB_TYPE_GET_CPU_STATE));
|
||||
|
||||
if (state == THREAD_STATE_STOPPED) {
|
||||
if (cpuState != NULL) {
|
||||
thread->SetCpuState(cpuState);
|
||||
} else {
|
||||
// trigger updating the CPU state
|
||||
fWorker->ScheduleJob(new(std::nothrow) GetCpuStateJob(
|
||||
fDebuggerInterface, thread),
|
||||
this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -469,7 +520,7 @@ TeamDebugger::_HandleThreadAction(thread_id threadID, uint32 action)
|
|||
// When continuing the thread update thread state before actually issuing
|
||||
// the command, since we need to unlock.
|
||||
if (action != MSG_THREAD_STOP)
|
||||
thread->SetState(THREAD_STATE_RUNNING);
|
||||
_SetThreadState(thread, THREAD_STATE_RUNNING, NULL);
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "DebugEvent.h"
|
||||
#include "TeamWindow.h"
|
||||
#include "Worker.h"
|
||||
|
||||
|
||||
class DebuggerInterface;
|
||||
|
@ -21,7 +22,7 @@ class TeamDebugModel;
|
|||
|
||||
|
||||
class TeamDebugger : public DoublyLinkedListLinkImpl<TeamDebugger>,
|
||||
private BLooper, private TeamWindow::Listener {
|
||||
private BLooper, private TeamWindow::Listener, private JobListener {
|
||||
public:
|
||||
TeamDebugger();
|
||||
~TeamDebugger();
|
||||
|
@ -39,6 +40,11 @@ private:
|
|||
thread_id threadID, uint32 action);
|
||||
virtual bool TeamWindowQuitRequested(TeamWindow* window);
|
||||
|
||||
// JobListener
|
||||
virtual void JobDone(Job* job);
|
||||
virtual void JobFailed(Job* job);
|
||||
virtual void JobAborted(Job* job);
|
||||
|
||||
private:
|
||||
static status_t _DebugEventListenerEntry(void* data);
|
||||
status_t _DebugEventListener();
|
||||
|
@ -70,6 +76,8 @@ private:
|
|||
ImageDeletedEvent* event);
|
||||
|
||||
void _UpdateThreadState(::Thread* thread);
|
||||
void _SetThreadState(::Thread* thread, uint32 state,
|
||||
CpuState* cpuState);
|
||||
|
||||
void _HandleThreadAction(thread_id threadID,
|
||||
uint32 action);
|
||||
|
@ -80,6 +88,7 @@ private:
|
|||
team_id fTeamID;
|
||||
port_id fNubPort;
|
||||
DebuggerInterface* fDebuggerInterface;
|
||||
Worker* fWorker;
|
||||
thread_id fDebugEventListener;
|
||||
TeamWindow* fTeamWindow;
|
||||
volatile bool fTerminating;
|
||||
|
|
|
@ -0,0 +1,443 @@
|
|||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "Worker.h"
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <AutoLocker.h>
|
||||
|
||||
|
||||
// #pragma mark - JobListener
|
||||
|
||||
|
||||
JobListener::~JobListener()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
JobListener::JobDone(Job* job)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
JobListener::JobFailed(Job* job)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
JobListener::JobAborted(Job* job)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Job
|
||||
|
||||
|
||||
Job::Job()
|
||||
:
|
||||
fWorker(NULL),
|
||||
fState(JOB_STATE_UNSCHEDULED),
|
||||
fDependency(NULL),
|
||||
fWaitStatus(JOB_DEPENDENCY_NOT_FOUND),
|
||||
fListeners(10)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Job::~Job()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
job_wait_status
|
||||
Job::WaitFor(const JobKey& key)
|
||||
{
|
||||
return fWorker->WaitForJob(this, key);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Job::SetWorker(Worker* worker)
|
||||
{
|
||||
fWorker = worker;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Job::SetState(job_state state)
|
||||
{
|
||||
fState = state;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Job::SetDependency(Job* job)
|
||||
{
|
||||
fDependency = job;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Job::SetWaitStatus(job_wait_status status)
|
||||
{
|
||||
fWaitStatus = status;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Job::AddListener(JobListener* listener)
|
||||
{
|
||||
return fListeners.AddItem(listener) ? B_OK : B_NO_MEMORY;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Job::RemoveListener(JobListener* listener)
|
||||
{
|
||||
fListeners.RemoveItem(listener);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Job::NotifyListeners()
|
||||
{
|
||||
int32 count = fListeners.CountItems();
|
||||
for (int32 i = count - 1; i >= 0; i--) {
|
||||
JobListener* listener = fListeners.ItemAt(i);
|
||||
switch (fState) {
|
||||
case JOB_STATE_SUCCEEDED:
|
||||
listener->JobDone(this);
|
||||
break;
|
||||
case JOB_STATE_FAILED:
|
||||
listener->JobFailed(this);
|
||||
break;
|
||||
case JOB_STATE_ABORTED:
|
||||
default:
|
||||
listener->JobAborted(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Worker
|
||||
|
||||
|
||||
Worker::Worker()
|
||||
:
|
||||
fLock("worker"),
|
||||
fWorkerThread(-1),
|
||||
fTerminating(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Worker::~Worker()
|
||||
{
|
||||
ShutDown();
|
||||
|
||||
if (fWorkerThread >= 0)
|
||||
wait_for_thread(fWorkerThread, NULL);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Worker::Init()
|
||||
{
|
||||
// check lock
|
||||
status_t error = fLock.InitCheck();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// init jobs table
|
||||
error = fJobs.Init();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// create semaphore for the worker
|
||||
fWorkToDoSem = create_sem(0, "work to do");
|
||||
if (fWorkToDoSem < 0)
|
||||
return fWorkToDoSem;
|
||||
|
||||
// spawn worker thread
|
||||
fWorkerThread = spawn_thread(_WorkerLoopEntry, "worker", B_NORMAL_PRIORITY,
|
||||
this);
|
||||
if (fWorkerThread < 0)
|
||||
return fWorkerThread;
|
||||
|
||||
resume_thread(fWorkerThread);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Worker::ShutDown()
|
||||
{
|
||||
AutoLocker<Worker> locker(this);
|
||||
|
||||
if (fTerminating)
|
||||
return;
|
||||
|
||||
fTerminating = true;
|
||||
|
||||
// abort all jobs
|
||||
Job* job = fJobs.Clear(true);
|
||||
while (job != NULL) {
|
||||
Job* nextJob = static_cast<HashTableLink<Job>*>(job)->fNext;
|
||||
_AbortJob(job, false);
|
||||
job = nextJob;
|
||||
|
||||
}
|
||||
|
||||
// let the work thread terminate
|
||||
delete_sem(fWorkToDoSem);
|
||||
fWorkToDoSem = -1;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Worker::ScheduleJob(Job* job, JobListener* listener)
|
||||
{
|
||||
if (job == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
ObjectDeleter<Job> jobDeleter(job);
|
||||
AutoLocker<Worker> locker(this);
|
||||
|
||||
if (fTerminating)
|
||||
return B_ERROR;
|
||||
|
||||
if (listener != NULL) {
|
||||
status_t error = job->AddListener(listener);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
}
|
||||
|
||||
bool notify = fUnscheduledJobs.IsEmpty() && fAbortedJobs.IsEmpty();
|
||||
|
||||
job->SetWorker(this);
|
||||
job->SetState(JOB_STATE_UNSCHEDULED);
|
||||
fJobs.Insert(job);
|
||||
fUnscheduledJobs.Add(job);
|
||||
jobDeleter.Detach();
|
||||
|
||||
if (notify)
|
||||
release_sem(fWorkToDoSem);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Worker::AbortJob(const JobKey& key)
|
||||
{
|
||||
AutoLocker<Worker> locker(this);
|
||||
|
||||
Job* job = fJobs.Lookup(key);
|
||||
if (job == NULL)
|
||||
return;
|
||||
|
||||
_AbortJob(job, true);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Worker::AddListener(const JobKey& key, JobListener* listener)
|
||||
{
|
||||
AutoLocker<Worker> locker(this);
|
||||
|
||||
Job* job = fJobs.Lookup(key);
|
||||
if (job == NULL)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
return job->AddListener(listener);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Worker::RemoveListener(const JobKey& key, JobListener* listener)
|
||||
{
|
||||
AutoLocker<Worker> locker(this);
|
||||
|
||||
if (Job* job = fJobs.Lookup(key))
|
||||
job->RemoveListener(listener);
|
||||
}
|
||||
|
||||
|
||||
job_wait_status
|
||||
Worker::WaitForJob(Job* waitingJob, const JobKey& key)
|
||||
{
|
||||
AutoLocker<Worker> locker(this);
|
||||
|
||||
// don't wait when the game is over anyway
|
||||
if (fTerminating || waitingJob->State() == JOB_STATE_ABORTED)
|
||||
return JOB_DEPENDENCY_ABORTED;
|
||||
|
||||
Job* job = fJobs.Lookup(key);
|
||||
if (job == NULL)
|
||||
return JOB_DEPENDENCY_NOT_FOUND;
|
||||
|
||||
waitingJob->SetWaitStatus(JOB_DEPENDENCY_ACTIVE);
|
||||
waitingJob->SetDependency(job);
|
||||
job->DependentJobs().Add(waitingJob);
|
||||
|
||||
// TODO: Continuations would be nice. For the time being we have to use
|
||||
// recursion. Disadvantages are that we'll use more stack and that aborting
|
||||
// a job waiting for a dependency won't abort the job before the dependency
|
||||
// is done.
|
||||
locker.Unlock();
|
||||
_ProcessJobs(job);
|
||||
locker.Lock();
|
||||
|
||||
// ignore the actual wait status when the game is over anyway
|
||||
if (fTerminating || waitingJob->State() == JOB_STATE_ABORTED)
|
||||
return JOB_DEPENDENCY_ABORTED;
|
||||
|
||||
return waitingJob->WaitStatus();
|
||||
}
|
||||
|
||||
|
||||
/*static*/ status_t
|
||||
Worker::_WorkerLoopEntry(void* data)
|
||||
{
|
||||
return ((Worker*)data)->_WorkerLoop();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Worker::_WorkerLoop()
|
||||
{
|
||||
_ProcessJobs(NULL);
|
||||
|
||||
// clean up aborted jobs
|
||||
AutoLocker<Worker> locker(this);
|
||||
while (Job* job = fAbortedJobs.RemoveHead())
|
||||
_FinishJob(job);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Worker::_ProcessJobs(Job* finalJob)
|
||||
{
|
||||
while (true) {
|
||||
AutoLocker<Worker> locker(this);
|
||||
|
||||
// wait for next job
|
||||
if (fUnscheduledJobs.IsEmpty() && fAbortedJobs.IsEmpty()) {
|
||||
locker.Unlock();
|
||||
|
||||
status_t error = acquire_sem(fWorkToDoSem);
|
||||
if (error != B_OK) {
|
||||
if (error == B_INTERRUPTED)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
locker.Lock();
|
||||
}
|
||||
|
||||
// clean up aborted jobs
|
||||
while (Job* job = fAbortedJobs.RemoveHead()) {
|
||||
_FinishJob(job);
|
||||
|
||||
if (job == finalJob)
|
||||
break;
|
||||
}
|
||||
|
||||
// process the next job
|
||||
if (Job* job = fUnscheduledJobs.RemoveHead()) {
|
||||
job->SetState(JOB_STATE_ACTIVE);
|
||||
|
||||
locker.Unlock();
|
||||
status_t error = job->Do();
|
||||
locker.Lock();
|
||||
|
||||
if (job->State() == JOB_STATE_ACTIVE) {
|
||||
job->SetState(
|
||||
error == B_OK ? JOB_STATE_SUCCEEDED : JOB_STATE_FAILED);
|
||||
}
|
||||
|
||||
_FinishJob(job);
|
||||
|
||||
if (job == finalJob)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Worker::_AbortJob(Job* job, bool removeFromTable)
|
||||
{
|
||||
switch (job->State()) {
|
||||
case JOB_STATE_ABORTED:
|
||||
return;
|
||||
|
||||
case JOB_STATE_UNSCHEDULED:
|
||||
fUnscheduledJobs.Remove(job);
|
||||
fAbortedJobs.Add(job);
|
||||
break;
|
||||
|
||||
case JOB_STATE_WAITING:
|
||||
job->Dependency()->DependentJobs().Remove(job);
|
||||
job->SetDependency(NULL);
|
||||
break;
|
||||
|
||||
case JOB_STATE_ACTIVE:
|
||||
case JOB_STATE_FAILED:
|
||||
case JOB_STATE_SUCCEEDED:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
job->SetState(JOB_STATE_ABORTED);
|
||||
if (removeFromTable)
|
||||
fJobs.Remove(job);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Worker::_FinishJob(Job* job)
|
||||
{
|
||||
// wake up dependent jobs
|
||||
if (!job->DependentJobs().IsEmpty()) {
|
||||
job_wait_status waitStatus;
|
||||
switch (job->State()) {
|
||||
case JOB_STATE_ABORTED:
|
||||
waitStatus = JOB_DEPENDENCY_ABORTED;
|
||||
break;
|
||||
case JOB_STATE_FAILED:
|
||||
waitStatus = JOB_DEPENDENCY_FAILED;
|
||||
break;
|
||||
case JOB_STATE_SUCCEEDED:
|
||||
waitStatus = JOB_DEPENDENCY_SUCCEEDED;
|
||||
break;
|
||||
|
||||
case JOB_STATE_UNSCHEDULED:
|
||||
case JOB_STATE_WAITING:
|
||||
case JOB_STATE_ACTIVE:
|
||||
default:
|
||||
// should never happen
|
||||
waitStatus = JOB_DEPENDENCY_NOT_FOUND;
|
||||
break;
|
||||
}
|
||||
|
||||
while (Job* dependentJob = job->DependentJobs().RemoveHead()) {
|
||||
dependentJob->SetDependency(NULL);
|
||||
dependentJob->SetWaitStatus(waitStatus);
|
||||
}
|
||||
}
|
||||
|
||||
if (job->State() != JOB_STATE_ABORTED)
|
||||
fJobs.Remove(job);
|
||||
job->NotifyListeners();
|
||||
delete job;
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef WORKER_H
|
||||
#define WORKER_H
|
||||
|
||||
#include <Locker.h>
|
||||
|
||||
#include <ObjectList.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
#include <util/OpenHashTable.h>
|
||||
|
||||
|
||||
class Job;
|
||||
class Worker;
|
||||
|
||||
|
||||
enum job_state {
|
||||
JOB_STATE_UNSCHEDULED,
|
||||
JOB_STATE_WAITING,
|
||||
JOB_STATE_ACTIVE,
|
||||
JOB_STATE_ABORTED,
|
||||
JOB_STATE_FAILED,
|
||||
JOB_STATE_SUCCEEDED
|
||||
};
|
||||
|
||||
enum job_wait_status {
|
||||
JOB_DEPENDENCY_NOT_FOUND,
|
||||
JOB_DEPENDENCY_SUCCEEDED,
|
||||
JOB_DEPENDENCY_FAILED,
|
||||
JOB_DEPENDENCY_ABORTED,
|
||||
JOB_DEPENDENCY_ACTIVE
|
||||
// internal only
|
||||
};
|
||||
|
||||
|
||||
struct JobKey {
|
||||
void* object;
|
||||
uint32 type;
|
||||
|
||||
JobKey(void* object, uint32 type)
|
||||
:
|
||||
object(object),
|
||||
type(type)
|
||||
{
|
||||
}
|
||||
|
||||
JobKey(const JobKey& other)
|
||||
:
|
||||
object(other.object),
|
||||
type(other.type)
|
||||
{
|
||||
}
|
||||
|
||||
JobKey& operator=(const JobKey& other)
|
||||
{
|
||||
object = other.object;
|
||||
type = other.type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const JobKey& other) const
|
||||
{
|
||||
return object == other.object && type == other.type;
|
||||
}
|
||||
|
||||
size_t HashValue() const
|
||||
{
|
||||
return (size_t)(addr_t)object ^ (size_t)type;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class JobListener {
|
||||
public:
|
||||
virtual ~JobListener();
|
||||
|
||||
virtual void JobDone(Job* job);
|
||||
virtual void JobFailed(Job* job);
|
||||
virtual void JobAborted(Job* job);
|
||||
};
|
||||
|
||||
|
||||
typedef DoublyLinkedList<Job> JobList;
|
||||
|
||||
|
||||
class Job : public DoublyLinkedListLinkImpl<Job>, public HashTableLink<Job> {
|
||||
public:
|
||||
Job();
|
||||
virtual ~Job();
|
||||
|
||||
virtual JobKey Key() const = 0;
|
||||
virtual status_t Do() = 0;
|
||||
|
||||
Worker* GetWorker() const { return fWorker; }
|
||||
job_state State() const { return fState; }
|
||||
|
||||
protected:
|
||||
job_wait_status WaitFor(const JobKey& key);
|
||||
|
||||
private:
|
||||
friend class Worker;
|
||||
|
||||
private:
|
||||
void SetWorker(Worker* worker);
|
||||
void SetState(job_state state);
|
||||
|
||||
Job* Dependency() const { return fDependency; }
|
||||
void SetDependency(Job* job);
|
||||
|
||||
JobList& DependentJobs() { return fDependentJobs; }
|
||||
|
||||
job_wait_status WaitStatus() const { return fWaitStatus; }
|
||||
void SetWaitStatus(job_wait_status status);
|
||||
|
||||
status_t AddListener(JobListener* listener);
|
||||
void RemoveListener(JobListener* listener);
|
||||
void NotifyListeners();
|
||||
|
||||
private:
|
||||
typedef BObjectList<JobListener> ListenerList;
|
||||
|
||||
private:
|
||||
Worker* fWorker;
|
||||
job_state fState;
|
||||
Job* fDependency;
|
||||
JobList fDependentJobs;
|
||||
job_wait_status fWaitStatus;
|
||||
ListenerList fListeners;
|
||||
};
|
||||
|
||||
|
||||
class Worker {
|
||||
public:
|
||||
Worker();
|
||||
~Worker();
|
||||
|
||||
status_t Init();
|
||||
void ShutDown();
|
||||
|
||||
bool Lock() { return fLock.Lock(); }
|
||||
void Unlock() { fLock.Unlock(); }
|
||||
|
||||
status_t ScheduleJob(Job* job,
|
||||
JobListener* listener = NULL);
|
||||
// always takes over ownership
|
||||
void AbortJob(const JobKey& key);
|
||||
|
||||
status_t AddListener(const JobKey& key,
|
||||
JobListener* listener);
|
||||
void RemoveListener(const JobKey& key,
|
||||
JobListener* listener);
|
||||
|
||||
private:
|
||||
friend class Job;
|
||||
|
||||
struct JobHashDefinition {
|
||||
typedef JobKey KeyType;
|
||||
typedef Job ValueType;
|
||||
|
||||
size_t HashKey(const JobKey& key) const
|
||||
{
|
||||
return key.HashValue();
|
||||
}
|
||||
|
||||
size_t Hash(Job* value) const
|
||||
{
|
||||
return HashKey(value->Key());
|
||||
}
|
||||
|
||||
bool Compare(const JobKey& key, Job *value) const
|
||||
{
|
||||
return value->Key() == key;
|
||||
}
|
||||
|
||||
HashTableLink<Job>* GetLink(Job* value) const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
typedef OpenHashTable<JobHashDefinition> JobTable;
|
||||
|
||||
private:
|
||||
job_wait_status WaitForJob(Job* waitingJob, const JobKey& key);
|
||||
|
||||
static status_t _WorkerLoopEntry(void* data);
|
||||
status_t _WorkerLoop();
|
||||
|
||||
void _ProcessJobs(Job* finalJob);
|
||||
void _AbortJob(Job* job, bool removeFromTable);
|
||||
void _FinishJob(Job* job);
|
||||
|
||||
private:
|
||||
BLocker fLock;
|
||||
JobTable fJobs;
|
||||
JobList fUnscheduledJobs;
|
||||
JobList fAbortedJobs;
|
||||
sem_id fWorkToDoSem;
|
||||
thread_id fWorkerThread;
|
||||
volatile bool fTerminating;
|
||||
};
|
||||
|
||||
|
||||
#endif // WORKER_H
|
Loading…
Reference in New Issue