Debugger: Tweaks to ConsoleOutputView.

- When receiving console output, we now put it into a pending output list.
  An asynchronous worker thread then tells the view to add another line to
  the output view, with a 10 msec delay between lines. This ensures that if
  the debugged program has a large amount of console output, it doesn't result
  in the debugger UI going unresponsive due to handling the constant influx
  of text insertions in rapid succession.
This commit is contained in:
Rene Gollent
2014-12-03 19:23:48 -05:00
parent 1ee239ec7c
commit 049e8f6fbd
2 changed files with 128 additions and 24 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2013, Rene Gollent, [email protected].
* Copyright 2013-2014, Rene Gollent, [email protected].
* Distributed under the terms of the MIT License.
*/
@@ -15,9 +15,31 @@
#include <String.h>
#include <TextView.h>
#include <AutoDeleter.h>
enum {
MSG_CLEAR_OUTPUT = 'clou'
MSG_CLEAR_OUTPUT = 'clou',
MSG_POST_OUTPUT = 'poou'
};
static const bigtime_t kOutputWaitInterval = 10000;
// #pragma mark - ConsoleOutputView::OutputInfo
struct ConsoleOutputView::OutputInfo {
int32 fd;
BString text;
OutputInfo(int32 fd, const BString& text)
:
fd(fd),
text(text)
{
}
};
@@ -30,7 +52,10 @@ ConsoleOutputView::ConsoleOutputView()
fStdoutEnabled(NULL),
fStderrEnabled(NULL),
fConsoleOutput(NULL),
fClearButton(NULL)
fClearButton(NULL),
fPendingOutput(NULL),
fWorkToDoSem(-1),
fOutputWorker(-1)
{
SetName("ConsoleOutput");
}
@@ -38,6 +63,13 @@ ConsoleOutputView::ConsoleOutputView()
ConsoleOutputView::~ConsoleOutputView()
{
if (fWorkToDoSem > 0)
delete_sem(fWorkToDoSem);
if (fOutputWorker > 0)
wait_for_thread(fOutputWorker, NULL);
delete fPendingOutput;
}
@@ -65,27 +97,14 @@ ConsoleOutputView::ConsoleOutputReceived(int32 fd, const BString& output)
else if (fd == 2 && fStderrEnabled->Value() != B_CONTROL_ON)
return;
text_run_array run;
run.count = 1;
run.runs[0].font = be_fixed_font;
run.runs[0].offset = 0;
run.runs[0].color.red = fd == 1 ? 0 : 192;
run.runs[0].color.green = 0;
run.runs[0].color.blue = 0;
run.runs[0].color.alpha = 255;
OutputInfo* info = new(std::nothrow) OutputInfo(fd, output);
if (info == NULL)
return;
bool autoScroll = false;
BScrollBar* scroller = fConsoleOutput->ScrollBar(B_VERTICAL);
float min, max;
scroller->GetRange(&min, &max);
if (min == max || scroller->Value() == max)
autoScroll = true;
fConsoleOutput->Insert(fConsoleOutput->TextLength(), output.String(),
output.Length(), &run);
if (autoScroll) {
scroller->GetRange(&min, &max);
fConsoleOutput->ScrollTo(0.0, max);
ObjectDeleter<OutputInfo> infoDeleter(info);
if (fPendingOutput->AddItem(info)) {
infoDeleter.Detach();
release_sem(fWorkToDoSem);
}
}
@@ -97,8 +116,18 @@ ConsoleOutputView::MessageReceived(BMessage* message)
case MSG_CLEAR_OUTPUT:
{
fConsoleOutput->SetText("");
fPendingOutput->MakeEmpty();
break;
}
case MSG_POST_OUTPUT:
{
OutputInfo* info = fPendingOutput->RemoveItemAt(0);
if (info == NULL)
break;
ObjectDeleter<OutputInfo> infoDeleter(info);
_HandleConsoleOutput(info);
}
default:
BGroupView::MessageReceived(message);
break;
@@ -145,6 +174,18 @@ ConsoleOutputView::SaveSettings(BMessage& settings)
void
ConsoleOutputView::_Init()
{
fPendingOutput = new OutputInfoList(10, true);
fWorkToDoSem = create_sem(0, "output_work_available");
if (fWorkToDoSem < 0)
throw std::bad_alloc();
fOutputWorker = spawn_thread(_OutputWorker, "output worker", B_LOW_PRIORITY, this);
if (fOutputWorker < 0)
throw std::bad_alloc();
resume_thread(fOutputWorker);
BScrollView* consoleScrollView;
BLayoutBuilder::Group<>(this, B_HORIZONTAL, 0.0f)
@@ -166,3 +207,56 @@ ConsoleOutputView::_Init()
fConsoleOutput->SetStylable(true);
fConsoleOutput->SetDoesUndo(false);
}
int32
ConsoleOutputView::_OutputWorker(void* arg)
{
ConsoleOutputView* view = (ConsoleOutputView*)arg;
for (;;) {
status_t error = acquire_sem(view->fWorkToDoSem);
if (error == B_INTERRUPTED)
continue;
else if (error != B_OK)
break;
BMessenger(view).SendMessage(MSG_POST_OUTPUT);
snooze(kOutputWaitInterval);
}
return B_OK;
}
void
ConsoleOutputView::_HandleConsoleOutput(OutputInfo* info)
{
if (info->fd == 1 && fStdoutEnabled->Value() != B_CONTROL_ON)
return;
else if (info->fd == 2 && fStderrEnabled->Value() != B_CONTROL_ON)
return;
text_run_array run;
run.count = 1;
run.runs[0].font = be_fixed_font;
run.runs[0].offset = 0;
run.runs[0].color.red = info->fd == 1 ? 0 : 192;
run.runs[0].color.green = 0;
run.runs[0].color.blue = 0;
run.runs[0].color.alpha = 255;
bool autoScroll = false;
BScrollBar* scroller = fConsoleOutput->ScrollBar(B_VERTICAL);
float min, max;
scroller->GetRange(&min, &max);
if (min == max || scroller->Value() == max)
autoScroll = true;
fConsoleOutput->Insert(fConsoleOutput->TextLength(), info->text,
info->text.Length(), &run);
if (autoScroll) {
scroller->GetRange(&min, &max);
fConsoleOutput->ScrollTo(0.0, max);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013, Rene Gollent, [email protected].
* Copyright 2013-2014, Rene Gollent, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef CONSOLE_OUTPUT_VIEW_H_
@@ -7,6 +7,7 @@
#include <GroupView.h>
#include <ObjectList.h>
class BButton;
@@ -31,14 +32,23 @@ public:
void LoadSettings(const BMessage& settings);
status_t SaveSettings(BMessage& settings);
private:
struct OutputInfo;
typedef BObjectList<OutputInfo> OutputInfoList;
private:
void _Init();
static int32 _OutputWorker(void* arg);
void _HandleConsoleOutput(OutputInfo* info);
private:
BCheckBox* fStdoutEnabled;
BCheckBox* fStderrEnabled;
BTextView* fConsoleOutput;
BButton* fClearButton;
OutputInfoList* fPendingOutput;
sem_id fWorkToDoSem;
thread_id fOutputWorker;
};