[Haiku-commits] r31116 - haiku/trunk/src/apps/debugger

bonefish at BerliOS bonefish at mail.berlios.de
Fri Jun 19 17:09:56 CEST 2009


Author: bonefish
Date: 2009-06-19 17:09:56 +0200 (Fri, 19 Jun 2009)
New Revision: 31116
ViewCVS: http://svn.berlios.de/viewcvs/haiku?rev=31116&view=rev

Added:
   haiku/trunk/src/apps/debugger/Jobs.cpp
   haiku/trunk/src/apps/debugger/Jobs.h
   haiku/trunk/src/apps/debugger/Worker.cpp
   haiku/trunk/src/apps/debugger/Worker.h
Modified:
   haiku/trunk/src/apps/debugger/Jamfile
   haiku/trunk/src/apps/debugger/TeamDebugger.cpp
   haiku/trunk/src/apps/debugger/TeamDebugger.h
Log:
* 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.


Modified: haiku/trunk/src/apps/debugger/Jamfile
===================================================================
--- haiku/trunk/src/apps/debugger/Jamfile	2009-06-19 11:09:21 UTC (rev 31115)
+++ haiku/trunk/src/apps/debugger/Jamfile	2009-06-19 15:09:56 UTC (rev 31116)
@@ -23,11 +23,13 @@
 #	ElfFile.cpp
 	Image.cpp
 	ImageInfo.cpp
+	Jobs.cpp
 	Team.cpp
 	TeamDebugger.cpp
 	TeamDebugModel.cpp
 	Thread.cpp
 	ThreadInfo.cpp
+	Worker.cpp
 
 	# arch
 	Architecture.cpp

Added: haiku/trunk/src/apps/debugger/Jobs.cpp
===================================================================
--- haiku/trunk/src/apps/debugger/Jobs.cpp	2009-06-19 11:09:21 UTC (rev 31115)
+++ haiku/trunk/src/apps/debugger/Jobs.cpp	2009-06-19 15:09:56 UTC (rev 31116)
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2009, Ingo Weinhold, ingo_weinhold at 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;
+}

Added: haiku/trunk/src/apps/debugger/Jobs.h
===================================================================
--- haiku/trunk/src/apps/debugger/Jobs.h	2009-06-19 11:09:21 UTC (rev 31115)
+++ haiku/trunk/src/apps/debugger/Jobs.h	2009-06-19 15:09:56 UTC (rev 31116)
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2009, Ingo Weinhold, ingo_weinhold at 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

Modified: haiku/trunk/src/apps/debugger/TeamDebugger.cpp
===================================================================
--- haiku/trunk/src/apps/debugger/TeamDebugger.cpp	2009-06-19 11:09:21 UTC (rev 31115)
+++ haiku/trunk/src/apps/debugger/TeamDebugger.cpp	2009-06-19 15:09:56 UTC (rev 31116)
@@ -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 @@
 	fDebugModel(NULL),
 	fTeamID(-1),
 	fDebuggerInterface(NULL),
+	fWorker(NULL),
 	fDebugEventListener(-1),
 	fTeamWindow(NULL),
 	fTerminating(false)
@@ -43,15 +45,17 @@
 	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 @@
 	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 @@
 }
 
 
+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,15 +369,8 @@
 	if (thread == NULL)
 		return false;
 
-	// update the thread state
-	thread->SetState(THREAD_STATE_STOPPED);
+	_SetThreadState(thread, THREAD_STATE_STOPPED, cpuState);
 
-	if (cpuState != NULL) {
-		thread->SetCpuState(cpuState);
-	} else {
-		// TODO: Trigger updating the CPU state!
-	}
-
 	return true;
 }
 
@@ -435,22 +462,46 @@
 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);
+		}
+	}
+}
+
+
+void
 TeamDebugger::_HandleThreadAction(thread_id threadID, uint32 action)
 {
 	AutoLocker< ::Team> locker(fTeam);
@@ -469,7 +520,7 @@
 	// 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();
 

Modified: haiku/trunk/src/apps/debugger/TeamDebugger.h
===================================================================
--- haiku/trunk/src/apps/debugger/TeamDebugger.h	2009-06-19 11:09:21 UTC (rev 31115)
+++ haiku/trunk/src/apps/debugger/TeamDebugger.h	2009-06-19 15:09:56 UTC (rev 31116)
@@ -13,6 +13,7 @@
 
 #include "DebugEvent.h"
 #include "TeamWindow.h"
+#include "Worker.h"
 
 
 class DebuggerInterface;
@@ -21,7 +22,7 @@
 
 
 class TeamDebugger :  public DoublyLinkedListLinkImpl<TeamDebugger>,
-	private BLooper, private TeamWindow::Listener {
+	private BLooper, private TeamWindow::Listener, private JobListener {
 public:
 								TeamDebugger();
 								~TeamDebugger();
@@ -39,6 +40,11 @@
 									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 @@
 									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 @@
 			team_id				fTeamID;
 			port_id				fNubPort;
 			DebuggerInterface*	fDebuggerInterface;
+			Worker*				fWorker;
 			thread_id			fDebugEventListener;
 			TeamWindow*			fTeamWindow;
 	volatile bool				fTerminating;

Added: haiku/trunk/src/apps/debugger/Worker.cpp
===================================================================
--- haiku/trunk/src/apps/debugger/Worker.cpp	2009-06-19 11:09:21 UTC (rev 31115)
+++ haiku/trunk/src/apps/debugger/Worker.cpp	2009-06-19 15:09:56 UTC (rev 31116)
@@ -0,0 +1,443 @@
+/*
+ * Copyright 2009, Ingo Weinhold, ingo_weinhold at 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;
+}

Added: haiku/trunk/src/apps/debugger/Worker.h
===================================================================
--- haiku/trunk/src/apps/debugger/Worker.h	2009-06-19 11:09:21 UTC (rev 31115)
+++ haiku/trunk/src/apps/debugger/Worker.h	2009-06-19 15:09:56 UTC (rev 31116)
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2009, Ingo Weinhold, ingo_weinhold at 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




More information about the Haiku-commits mailing list