Debugger: CliContext: Introduce the notion of events

* The input loop can now wait on abstract events, which other threads
  (or even the input loop thread itself) can signal.
* Use the new mechanism in QuitSession().
* Also (with the exception of the SIGINT part) implement
  WaitForThreadOrUser().
This commit is contained in:
Ingo Weinhold
2012-07-29 00:28:56 +02:00
parent 4d8eaa5b11
commit d266c87d17
2 changed files with 121 additions and 17 deletions
@@ -28,16 +28,22 @@ CliContext::CliContext()
fHistory(NULL), fHistory(NULL),
fPrompt(NULL), fPrompt(NULL),
fBlockingSemaphore(-1), fBlockingSemaphore(-1),
fInputLoopWaitingForEvents(0),
fEventsOccurred(0),
fInputLoopWaiting(false), fInputLoopWaiting(false),
fTerminating(false) fTerminating(false)
{ {
sCurrentContext = this; sCurrentContext = this;
} }
CliContext::~CliContext() CliContext::~CliContext()
{ {
Cleanup(); Cleanup();
sCurrentContext = NULL; sCurrentContext = NULL;
if (fBlockingSemaphore >= 0)
delete_sem(fBlockingSemaphore);
} }
@@ -47,6 +53,8 @@ CliContext::Init(Team* team, UserInterfaceListener* listener)
fTeam = team; fTeam = team;
fListener = listener; fListener = listener;
fTeam->AddListener(this);
status_t error = fLock.InitCheck(); status_t error = fLock.InitCheck();
if (error != B_OK) if (error != B_OK)
return error; return error;
@@ -88,6 +96,11 @@ CliContext::Cleanup()
history_end(fHistory); history_end(fHistory);
fHistory = NULL; fHistory = NULL;
} }
if (fTeam != NULL) {
fTeam->RemoveListener(this);
fTeam = NULL;
}
} }
@@ -97,13 +110,7 @@ CliContext::Terminating()
AutoLocker<BLocker> locker(fLock); AutoLocker<BLocker> locker(fLock);
fTerminating = true; fTerminating = true;
_SignalInputLoop(EVENT_QUIT);
if (fBlockingSemaphore >= 0) {
delete_sem(fBlockingSemaphore);
fBlockingSemaphore = -1;
}
fInputLoopWaiting = false;
// TODO: Signal the input loop, should it be in PromptUser()! // TODO: Signal the input loop, should it be in PromptUser()!
} }
@@ -134,27 +141,104 @@ CliContext::AddLineToInputHistory(const char* line)
void void
CliContext::QuitSession(bool killTeam) CliContext::QuitSession(bool killTeam)
{ {
AutoLocker<BLocker> locker(fLock); _PrepareToWaitForEvents(EVENT_QUIT);
sem_id blockingSemaphore = fBlockingSemaphore;
fInputLoopWaiting = true;
locker.Unlock();
fListener->UserInterfaceQuitRequested( fListener->UserInterfaceQuitRequested(
killTeam killTeam
? UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM ? UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM
: UserInterfaceListener::QUIT_OPTION_ASK_RESUME_TEAM); : UserInterfaceListener::QUIT_OPTION_ASK_RESUME_TEAM);
while (acquire_sem(blockingSemaphore) == B_INTERRUPTED) { _WaitForEvents();
}
} }
void void
CliContext::WaitForThreadOrUser() CliContext::WaitForThreadOrUser()
{ {
// TODO:... // TODO: Deal with SIGINT as well!
for (;;) {
_PrepareToWaitForEvents(
EVENT_USER_INTERRUPT | EVENT_THREAD_STATE_CHANGED);
// check whether there are any threads stopped already
thread_id stoppedThread = -1;
AutoLocker<Team> teamLocker(fTeam);
for (ThreadList::ConstIterator it = fTeam->Threads().GetIterator();
Thread* thread = it.Next();) {
if (thread->State() == THREAD_STATE_STOPPED) {
stoppedThread = thread->ID();
break;
}
}
teamLocker.Unlock();
if (stoppedThread >= 0)
_SignalInputLoop(EVENT_THREAD_STATE_CHANGED);
uint32 events = _WaitForEvents();
if ((events & EVENT_QUIT) != 0 || stoppedThread >= 0)
return;
}
}
void
CliContext::ThreadStateChanged(const Team::ThreadEvent& event)
{
_SignalInputLoop(EVENT_THREAD_STATE_CHANGED);
}
void
CliContext::_PrepareToWaitForEvents(uint32 eventMask)
{
// Set the events we're going to wait for -- always wait for "quit".
AutoLocker<BLocker> locker(fLock);
fInputLoopWaitingForEvents = eventMask | EVENT_QUIT;
fEventsOccurred = fTerminating ? EVENT_QUIT : 0;
}
uint32
CliContext::_WaitForEvents()
{
AutoLocker<BLocker> locker(fLock);
if (fEventsOccurred == 0) {
sem_id blockingSemaphore = fBlockingSemaphore;
fInputLoopWaiting = true;
locker.Unlock();
while (acquire_sem(blockingSemaphore) == B_INTERRUPTED) {
}
locker.Lock();
}
uint32 events = fEventsOccurred;
fEventsOccurred = 0;
return events;
}
void
CliContext::_SignalInputLoop(uint32 events)
{
AutoLocker<BLocker> locker(fLock);
if ((fInputLoopWaitingForEvents & events) == 0)
return;
fEventsOccurred = fInputLoopWaitingForEvents & events;
fInputLoopWaitingForEvents = 0;
if (fInputLoopWaiting) {
fInputLoopWaiting = false;
release_sem(fBlockingSemaphore);
}
} }
@@ -12,12 +12,21 @@
#include <Locker.h> #include <Locker.h>
#include "Team.h"
class Team; class Team;
class UserInterfaceListener; class UserInterfaceListener;
class CliContext { class CliContext : private Team::Listener {
public:
enum {
EVENT_QUIT = 0x01,
EVENT_USER_INTERRUPT = 0x02,
EVENT_THREAD_STATE_CHANGED = 0x04,
};
public: public:
CliContext(); CliContext();
~CliContext(); ~CliContext();
@@ -40,6 +49,15 @@ public:
void WaitForThreadOrUser(); void WaitForThreadOrUser();
private: private:
// Team::Listener
virtual void ThreadStateChanged(
const Team::ThreadEvent& event);
private:
void _PrepareToWaitForEvents(uint32 eventMask);
uint32 _WaitForEvents();
void _SignalInputLoop(uint32 events);
static const char* _GetPrompt(EditLine* editLine); static const char* _GetPrompt(EditLine* editLine);
private: private:
@@ -50,6 +68,8 @@ private:
History* fHistory; History* fHistory;
const char* fPrompt; const char* fPrompt;
sem_id fBlockingSemaphore; sem_id fBlockingSemaphore;
uint32 fInputLoopWaitingForEvents;
uint32 fEventsOccurred;
bool fInputLoopWaiting; bool fInputLoopWaiting;
volatile bool fTerminating; volatile bool fTerminating;
}; };