/*
* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <DebugLooper.h>
#include <new>
#include <AutoLocker.h>
#include <DebugMessageHandler.h>
#include <TeamDebugger.h>
#include <util/DoublyLinkedList.h>
struct BDebugLooper::Debugger {
BTeamDebugger* debugger;
BDebugMessageHandler* handler;
Debugger(BTeamDebugger* debugger, BDebugMessageHandler* handler)
:
debugger(debugger),
handler(handler)
{
}
};
struct BDebugLooper::Job : DoublyLinkedListLinkImpl<Job> {
Job()
:
fDoneSemaphore(-1)
{
}
virtual ~Job()
{
}
status_t Wait(BLocker& lock)
{
fDoneSemaphore = create_sem(0, "debug looper job");
lock.Unlock();
while (acquire_sem(fDoneSemaphore) == B_INTERRUPTED) {
}
lock.Lock();
delete_sem(fDoneSemaphore);
fDoneSemaphore = -1;
return fResult;
}
void Done(status_t result)
{
fResult = result;
release_sem(fDoneSemaphore);
}
virtual status_t Do(BDebugLooper* looper) = 0;
protected:
sem_id fDoneSemaphore;
status_t fResult;
};
struct BDebugLooper::JobList : DoublyLinkedList<Job> {
};
struct BDebugLooper::AddDebuggerJob : Job {
AddDebuggerJob(BTeamDebugger* debugger,
BDebugMessageHandler* handler)
:
fDebugger(debugger),
fHandler(handler)
{
}
virtual status_t Do(BDebugLooper* looper)
{
Debugger* debugger = new(std::nothrow) Debugger(fDebugger, fHandler);
if (debugger == NULL || !looper->fDebuggers.AddItem(debugger)) {
delete debugger;
return B_NO_MEMORY;
}
return B_OK;
}
private:
BTeamDebugger* fDebugger;
BDebugMessageHandler* fHandler;
};
struct BDebugLooper::RemoveDebuggerJob : Job {
RemoveDebuggerJob(team_id team)
:
fTeam(team)
{
}
virtual status_t Do(BDebugLooper* looper)
{
for (int32 i = 0; Debugger* debugger = looper->fDebuggers.ItemAt(i);
i++) {
if (debugger->debugger->Team() == fTeam) {
delete looper->fDebuggers.RemoveItemAt(i);
return B_OK;
}
}
return B_ENTRY_NOT_FOUND;
}
private:
team_id fTeam;
};
// #pragma mark -
BDebugLooper::BDebugLooper()
:
fLock("debug looper"),
fThread(-1),
fOwnsThread(false),
fTerminating(false),
fNotified(false),
fJobs(NULL),
fEventSemaphore(-1)
{
}
BDebugLooper::~BDebugLooper()
{
}
status_t
BDebugLooper::Init()
{
status_t error = fLock.InitCheck();
if (error != B_OK)
return error;
AutoLocker<BLocker> locker(fLock);
if (fThread >= 0)
return B_BAD_VALUE;
if (fJobs == NULL) {
fJobs = new(std::nothrow) JobList;
if (fJobs == NULL)
return B_NO_MEMORY;
}
if (fEventSemaphore < 0) {
fEventSemaphore = create_sem(0, "debug looper event");
if (fEventSemaphore < 0)
return fEventSemaphore;
}
return B_OK;
}
thread_id
BDebugLooper::Run(bool spawnThread)
{
AutoLocker<BLocker> locker(fLock);
if (fThread >= 0)
return B_BAD_VALUE;
fNotified = false;
if (spawnThread) {
fThread = spawn_thread(&_MessageLoopEntry, "debug looper",
B_NORMAL_PRIORITY, this);
if (fThread < 0)
return fThread;
fOwnsThread = true;
resume_thread(fThread);
return B_OK;
}
fThread = find_thread(NULL);
fOwnsThread = false;
_MessageLoop();
return B_OK;
}
void
BDebugLooper::Quit()
{
AutoLocker<BLocker> locker(fLock);
fTerminating = true;
_Notify();
}
status_t
BDebugLooper::AddTeamDebugger(BTeamDebugger* debugger,
BDebugMessageHandler* handler)
{
if (debugger == NULL || handler == NULL)
return B_BAD_VALUE;
AddDebuggerJob job(debugger, handler);
return _DoJob(&job);
}
bool
BDebugLooper::RemoveTeamDebugger(BTeamDebugger* debugger)
{
if (debugger == NULL)
return false;
RemoveDebuggerJob job(debugger->Team());
return _DoJob(&job) == B_OK;
}
bool
BDebugLooper::RemoveTeamDebugger(team_id team)
{
if (team < 0)
return false;
RemoveDebuggerJob job(team);
return _DoJob(&job) == B_OK;
}
/*static*/ status_t
BDebugLooper::_MessageLoopEntry(void* data)
{
return ((BDebugLooper*)data)->_MessageLoop();
}
status_t
BDebugLooper::_MessageLoop()
{
while (true) {
// prepare the wait info array
int32 debuggerCount = fDebuggers.CountItems();
object_wait_info waitInfos[debuggerCount + 1];
for (int32 i = 0; i < debuggerCount; i++) {
waitInfos[i].object
= fDebuggers.ItemAt(i)->debugger->DebuggerPort();
waitInfos[i].type = B_OBJECT_TYPE_PORT;
waitInfos[i].events = B_EVENT_READ;
}
waitInfos[debuggerCount].object = fEventSemaphore;
waitInfos[debuggerCount].type = B_OBJECT_TYPE_SEMAPHORE;
waitInfos[debuggerCount].events = B_EVENT_ACQUIRE_SEMAPHORE;
// wait for the next event
wait_for_objects(waitInfos, debuggerCount + 1);
AutoLocker<BLocker> locker(fLock);
// handle all pending jobs
bool handledJobs = fJobs->Head() != NULL;
while (Job* job = fJobs->RemoveHead())
job->Done(job->Do(this));
// acquire notification semaphore and mark unnotified
if ((waitInfos[debuggerCount].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0)
acquire_sem(fEventSemaphore);
fNotified = false;
if (fTerminating)
return B_OK;
// Always loop when jobs were executed, since that might add/remove
// debuggers.
if (handledJobs)
continue;
// read a pending port message
for (int32 i = 0; i < debuggerCount; i++) {
if ((waitInfos[i].events & B_EVENT_READ) != 0) {
Debugger* debugger = fDebuggers.ItemAt(i);
// read the message
debug_debugger_message_data message;
int32 code;
ssize_t messageSize = read_port(
debugger->debugger->DebuggerPort(), &code, &message,
sizeof(message));
if (messageSize < 0)
continue;
// handle the message
bool continueThread = debugger->handler->HandleDebugMessage(
code, message);
// If requested, tell the thread to continue (only when there
// is a thread and the message was synchronous).
if (continueThread && message.origin.thread >= 0
&& message.origin.nub_port >= 0) {
debugger->debugger->ContinueThread(message.origin.thread);
}
// Handle only one message -- the hook might have added/removed
// debuggers which makes further iteration problematic.
break;
}
}
}
}
status_t
BDebugLooper::_DoJob(Job* job)
{
AutoLocker<BLocker> locker(fLock);
// execute directly, if in looper thread or not running yet
if (fThread < 0 || fThread == find_thread(NULL))
return job->Do(this);
// execute in the looper thread
fJobs->Add(job);
_Notify();
return job->Wait(fLock);
}
void
BDebugLooper::_Notify()
{
if (fNotified)
return;
fNotified = true;
release_sem(fEventSemaphore);
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fResult.