* 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:
@@ -23,11 +23,13 @@ Application Debugger :
|
|||||||
# ElfFile.cpp
|
# ElfFile.cpp
|
||||||
Image.cpp
|
Image.cpp
|
||||||
ImageInfo.cpp
|
ImageInfo.cpp
|
||||||
|
Jobs.cpp
|
||||||
Team.cpp
|
Team.cpp
|
||||||
TeamDebugger.cpp
|
TeamDebugger.cpp
|
||||||
TeamDebugModel.cpp
|
TeamDebugModel.cpp
|
||||||
Thread.cpp
|
Thread.cpp
|
||||||
ThreadInfo.cpp
|
ThreadInfo.cpp
|
||||||
|
Worker.cpp
|
||||||
|
|
||||||
# arch
|
# arch
|
||||||
Architecture.cpp
|
Architecture.cpp
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* 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, [email protected].
|
||||||
|
* 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 "CpuState.h"
|
||||||
#include "DebuggerInterface.h"
|
#include "DebuggerInterface.h"
|
||||||
|
#include "Jobs.h"
|
||||||
#include "MessageCodes.h"
|
#include "MessageCodes.h"
|
||||||
#include "Team.h"
|
#include "Team.h"
|
||||||
#include "TeamDebugModel.h"
|
#include "TeamDebugModel.h"
|
||||||
@@ -29,6 +30,7 @@ TeamDebugger::TeamDebugger()
|
|||||||
fDebugModel(NULL),
|
fDebugModel(NULL),
|
||||||
fTeamID(-1),
|
fTeamID(-1),
|
||||||
fDebuggerInterface(NULL),
|
fDebuggerInterface(NULL),
|
||||||
|
fWorker(NULL),
|
||||||
fDebugEventListener(-1),
|
fDebugEventListener(-1),
|
||||||
fTeamWindow(NULL),
|
fTeamWindow(NULL),
|
||||||
fTerminating(false)
|
fTerminating(false)
|
||||||
@@ -43,15 +45,17 @@ TeamDebugger::~TeamDebugger()
|
|||||||
fTerminating = true;
|
fTerminating = true;
|
||||||
|
|
||||||
fDebuggerInterface->Close();
|
fDebuggerInterface->Close();
|
||||||
|
fWorker->ShutDown();
|
||||||
|
|
||||||
locker.Unlock();
|
locker.Unlock();
|
||||||
|
|
||||||
if (fDebugEventListener >= 0)
|
if (fDebugEventListener >= 0)
|
||||||
wait_for_thread(fDebugEventListener, NULL);
|
wait_for_thread(fDebugEventListener, NULL);
|
||||||
|
|
||||||
|
delete fDebuggerInterface;
|
||||||
|
delete fWorker;
|
||||||
delete fDebugModel;
|
delete fDebugModel;
|
||||||
delete fTeam;
|
delete fTeam;
|
||||||
delete fDebuggerInterface;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -86,6 +90,15 @@ TeamDebugger::Init(team_id teamID, thread_id threadID, bool stopInMain)
|
|||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
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
|
// create debugger interface
|
||||||
fDebuggerInterface = new(std::nothrow) DebuggerInterface(fTeamID);
|
fDebuggerInterface = new(std::nothrow) DebuggerInterface(fTeamID);
|
||||||
if (fDebuggerInterface == NULL)
|
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
|
/*static*/ status_t
|
||||||
TeamDebugger::_DebugEventListenerEntry(void* data)
|
TeamDebugger::_DebugEventListenerEntry(void* data)
|
||||||
{
|
{
|
||||||
@@ -335,14 +369,7 @@ TeamDebugger::_HandleThreadStopped(thread_id threadID, CpuState* cpuState)
|
|||||||
if (thread == NULL)
|
if (thread == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// update the thread state
|
_SetThreadState(thread, THREAD_STATE_STOPPED, cpuState);
|
||||||
thread->SetState(THREAD_STATE_STOPPED);
|
|
||||||
|
|
||||||
if (cpuState != NULL) {
|
|
||||||
thread->SetCpuState(cpuState);
|
|
||||||
} else {
|
|
||||||
// TODO: Trigger updating the CPU state!
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -435,18 +462,42 @@ TeamDebugger::_HandleImageDeleted(ImageDeletedEvent* event)
|
|||||||
void
|
void
|
||||||
TeamDebugger::_UpdateThreadState(::Thread* thread)
|
TeamDebugger::_UpdateThreadState(::Thread* thread)
|
||||||
{
|
{
|
||||||
CpuState* state = NULL;
|
CpuState* cpuState = NULL;
|
||||||
status_t error = fDebuggerInterface->GetCpuState(thread->ID(), state);
|
status_t error = fDebuggerInterface->GetCpuState(thread->ID(), cpuState);
|
||||||
|
|
||||||
uint32 newState = THREAD_STATE_UNKNOWN;
|
uint32 newState = THREAD_STATE_UNKNOWN;
|
||||||
if (error == B_OK) {
|
if (error == B_OK) {
|
||||||
newState = THREAD_STATE_STOPPED;
|
newState = THREAD_STATE_STOPPED;
|
||||||
state->RemoveReference();
|
cpuState->RemoveReference();
|
||||||
} else if (error == B_BAD_THREAD_STATE)
|
} else if (error == B_BAD_THREAD_STATE)
|
||||||
newState = THREAD_STATE_RUNNING;
|
newState = THREAD_STATE_RUNNING;
|
||||||
|
|
||||||
thread->SetState(newState);
|
_SetThreadState(thread, newState, cpuState);
|
||||||
thread->SetCpuState(state);
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
// When continuing the thread update thread state before actually issuing
|
||||||
// the command, since we need to unlock.
|
// the command, since we need to unlock.
|
||||||
if (action != MSG_THREAD_STOP)
|
if (action != MSG_THREAD_STOP)
|
||||||
thread->SetState(THREAD_STATE_RUNNING);
|
_SetThreadState(thread, THREAD_STATE_RUNNING, NULL);
|
||||||
|
|
||||||
locker.Unlock();
|
locker.Unlock();
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include "DebugEvent.h"
|
#include "DebugEvent.h"
|
||||||
#include "TeamWindow.h"
|
#include "TeamWindow.h"
|
||||||
|
#include "Worker.h"
|
||||||
|
|
||||||
|
|
||||||
class DebuggerInterface;
|
class DebuggerInterface;
|
||||||
@@ -21,7 +22,7 @@ class TeamDebugModel;
|
|||||||
|
|
||||||
|
|
||||||
class TeamDebugger : public DoublyLinkedListLinkImpl<TeamDebugger>,
|
class TeamDebugger : public DoublyLinkedListLinkImpl<TeamDebugger>,
|
||||||
private BLooper, private TeamWindow::Listener {
|
private BLooper, private TeamWindow::Listener, private JobListener {
|
||||||
public:
|
public:
|
||||||
TeamDebugger();
|
TeamDebugger();
|
||||||
~TeamDebugger();
|
~TeamDebugger();
|
||||||
@@ -39,6 +40,11 @@ private:
|
|||||||
thread_id threadID, uint32 action);
|
thread_id threadID, uint32 action);
|
||||||
virtual bool TeamWindowQuitRequested(TeamWindow* window);
|
virtual bool TeamWindowQuitRequested(TeamWindow* window);
|
||||||
|
|
||||||
|
// JobListener
|
||||||
|
virtual void JobDone(Job* job);
|
||||||
|
virtual void JobFailed(Job* job);
|
||||||
|
virtual void JobAborted(Job* job);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static status_t _DebugEventListenerEntry(void* data);
|
static status_t _DebugEventListenerEntry(void* data);
|
||||||
status_t _DebugEventListener();
|
status_t _DebugEventListener();
|
||||||
@@ -70,6 +76,8 @@ private:
|
|||||||
ImageDeletedEvent* event);
|
ImageDeletedEvent* event);
|
||||||
|
|
||||||
void _UpdateThreadState(::Thread* thread);
|
void _UpdateThreadState(::Thread* thread);
|
||||||
|
void _SetThreadState(::Thread* thread, uint32 state,
|
||||||
|
CpuState* cpuState);
|
||||||
|
|
||||||
void _HandleThreadAction(thread_id threadID,
|
void _HandleThreadAction(thread_id threadID,
|
||||||
uint32 action);
|
uint32 action);
|
||||||
@@ -80,6 +88,7 @@ private:
|
|||||||
team_id fTeamID;
|
team_id fTeamID;
|
||||||
port_id fNubPort;
|
port_id fNubPort;
|
||||||
DebuggerInterface* fDebuggerInterface;
|
DebuggerInterface* fDebuggerInterface;
|
||||||
|
Worker* fWorker;
|
||||||
thread_id fDebugEventListener;
|
thread_id fDebugEventListener;
|
||||||
TeamWindow* fTeamWindow;
|
TeamWindow* fTeamWindow;
|
||||||
volatile bool fTerminating;
|
volatile bool fTerminating;
|
||||||
|
|||||||
@@ -0,0 +1,443 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* 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, [email protected].
|
||||||
|
* 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
|
||||||
Reference in New Issue
Block a user