* 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:
Ingo Weinhold 2009-06-19 15:09:56 +00:00
parent 3609af391d
commit 495676cab6
7 changed files with 818 additions and 16 deletions

View File

@ -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

View File

@ -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;
}

37
src/apps/debugger/Jobs.h Normal file
View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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;
}

206
src/apps/debugger/Worker.h Normal file
View File

@ -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