[rkward] rkward/rbackend: Move output capturing to the backend.
Thomas Friedrichsmeier
null at kde.org
Wed Oct 3 20:54:38 BST 2018
Git commit fecf37b8d24d2223dd43f6133fa21c303b8bc744 by Thomas Friedrichsmeier.
Committed on 03/10/2018 at 11:12.
Pushed by tfry into branch 'master'.
Move output capturing to the backend.
Right now this is only an internal change / cleanup, but it will make it possible
to script output capturing (_including_ system() output, of course).
M +2 -5 rkward/rbackend/rkrbackend.cpp
M +71 -12 rkward/rbackend/rkrbackendprotocol_shared.cpp
M +16 -1 rkward/rbackend/rkrbackendprotocol_shared.h
M +0 -35 rkward/rbackend/rkrinterface.cpp
M +0 -3 rkward/rbackend/rkrinterface.h
https://commits.kde.org/rkward/fecf37b8d24d2223dd43f6133fa21c303b8bc744
diff --git a/rkward/rbackend/rkrbackend.cpp b/rkward/rbackend/rkrbackend.cpp
index 7cb1f54e..96f004a5 100644
--- a/rkward/rbackend/rkrbackend.cpp
+++ b/rkward/rbackend/rkrbackend.cpp
@@ -1451,16 +1451,13 @@ void RKRBackend::printCommand (const QString &command) {
void RKRBackend::startOutputCapture () {
RK_TRACE (RBACKEND);
- // TODO: One of those days, we need to revisit output handling. This request could be perfectly async, but unfortunately, in that case, output chunks can sneak in front of it.
- handlePlainGenericRequest (QStringList ("recordOutput"), true);
+ pushOutputCapture (RecordMessages | RecordOutput);
}
void RKRBackend::printAndClearCapturedMessages (bool with_header) {
RK_TRACE (RBACKEND);
- QStringList params ("recordOutput");
- params.append ("end");
- QString out = handlePlainGenericRequest (params, true).value (0);
+ QString out = popOutputCapture ();
if (out.isEmpty ()) return;
if (with_header) out.prepend ("<h2>Messages, warnings, or errors:</h2>\n");
diff --git a/rkward/rbackend/rkrbackendprotocol_shared.cpp b/rkward/rbackend/rkrbackendprotocol_shared.cpp
index 5048373d..131c6e8e 100644
--- a/rkward/rbackend/rkrbackendprotocol_shared.cpp
+++ b/rkward/rbackend/rkrbackendprotocol_shared.cpp
@@ -94,38 +94,97 @@ RKROutputBuffer::RKROutputBuffer () {
RKROutputBuffer::~RKROutputBuffer () {
RK_TRACE (RBACKEND);
+
+ if (!output_captures.isEmpty ()) RK_DEBUG (RBACKEND, DL_WARNING, "%d requests for recording output still active on interface shutdown", output_captures.size ());
}
-bool RKROutputBuffer::handleOutput (const QString &output, int buf_length, ROutput::ROutputType output_type, bool allow_blocking) {
- if (!buf_length) return false;
+void RKROutputBuffer::pushOutputCapture (int capture_mode) {
RK_TRACE (RBACKEND);
- RK_DEBUG (RBACKEND, DL_DEBUG, "Output type %d: %s", output_type, qPrintable (output));
+ OutputCapture capture;
+ capture.mode = capture_mode;
+ output_captures.append (capture);
+}
- // wait while the output buffer is exceeded to give downstream threads a chance to catch up
- while ((out_buf_len > MAX_BUF_LENGTH) && allow_blocking) {
- if (!doMSleep (10)) break;
+QString RKROutputBuffer::popOutputCapture () {
+ RK_TRACE (RBACKEND);
+
+ if (output_captures.isEmpty ()) {
+ RK_ASSERT (!output_captures.isEmpty ());
+ return QString ();
}
+ OutputCapture capture = output_captures.takeLast ();
+ if (capture.recorded.isEmpty ()) return QString ();
- output_buffer_mutex.lock ();
- bool previously_empty = (out_buf_len <= 0);
+ QString ret;
+ ROutput::ROutputType previous_type = ROutput::NoOutput;
+ for (int i = 0; i < capture.recorded.length (); ++i) {
+ const ROutput * output = capture.recorded[i];
+ if (output->output.isEmpty ()) continue;
+
+ if (output->type != ROutput::Error) { // NOTE: skip error output. It has already been written as a warning.
+ if (output->type != previous_type) {
+ if (!ret.isEmpty ()) ret.append ("</pre>\n");
+
+ if (output->type == ROutput::Output) ret.append ("<pre class=\"output_normal\">");
+ else if (output->type == ROutput::Warning) ret.append ("<pre class=\"output_warning\">");
+ else {
+ RK_ASSERT (false);
+ ret.append ("<pre>");
+ }
+ }
+ ret.append (output->output.toHtmlEscaped ());
+ previous_type = output->type;
+ }
+ }
+ if (!ret.isEmpty ()) ret.append ("</pre>\n");
+ return ret;
+}
+void appendToOutputList (ROutputList *list, const QString &output, ROutput::ROutputType output_type) {
+// No trace
ROutput *current_output = 0;
- if (!output_buffer.isEmpty ()) {
+ if (!list->isEmpty ()) {
// Merge with previous output fragment, if of the same type
- current_output = output_buffer.last ();
+ current_output = list->last ();
if (current_output->type != output_type) current_output = 0;
}
if (!current_output) {
current_output = new ROutput;
current_output->type = output_type;
current_output->output.reserve (OUTPUT_STRING_RESERVE);
- output_buffer.append (current_output);
+ list->append (current_output);
}
current_output->output.append (output);
+}
+
+bool RKROutputBuffer::handleOutput (const QString &output, int buf_length, ROutput::ROutputType output_type, bool allow_blocking) {
+ if (!buf_length) return false;
+ RK_TRACE (RBACKEND);
+ RK_DEBUG (RBACKEND, DL_DEBUG, "Output type %d: %s", output_type, qPrintable (output));
+
+ // wait while the output buffer is exceeded to give downstream threads a chance to catch up
+ while ((out_buf_len > MAX_BUF_LENGTH) && allow_blocking) {
+ if (!doMSleep (10)) break;
+ }
+
+ QMutexLocker lock (&output_buffer_mutex);
+ bool previously_empty = (out_buf_len <= 0);
+
+ for (int i = output_captures.length () - 1; i >= 0; --i) {
+ OutputCapture &cap = output_captures[i];
+ if (output_type == ROutput::Output) {
+ if (cap.mode & RecordOutput) appendToOutputList (&(cap.recorded), output, output_type);
+ if (cap.mode & EatOutput) return previously_empty;
+ } else {
+ if (cap.mode & RecordMessages) appendToOutputList (&(cap.recorded), output, output_type);
+ if (cap.mode & EatMessages) return previously_empty;
+ }
+ }
+
+ appendToOutputList (&output_buffer, output, output_type);
out_buf_len += buf_length;
- output_buffer_mutex.unlock ();
return previously_empty;
}
diff --git a/rkward/rbackend/rkrbackendprotocol_shared.h b/rkward/rbackend/rkrbackendprotocol_shared.h
index 254ada5f..3e5ab79c 100644
--- a/rkward/rbackend/rkrbackendprotocol_shared.h
+++ b/rkward/rbackend/rkrbackendprotocol_shared.h
@@ -2,7 +2,7 @@
rkrbackendprotocol - description
-------------------
begin : Thu Nov 04 2010
- copyright : (C) 2010, 2011, 2013 by Thomas Friedrichsmeier
+ copyright : (C) 2010-2018 by Thomas Friedrichsmeier
email : thomas.friedrichsmeier at kdemail.net
***************************************************************************/
@@ -134,6 +134,15 @@ public:
returns true, if a *new* piece of output started, i.e. the buffer was empty before this. */
bool handleOutput (const QString &output, int len, ROutput::ROutputType type, bool allow_blocking=true);
+ enum CaptureMode {
+ RecordMessages = 1,
+ RecordOutput = 2,
+ EatMessages = 4,
+ EatOutput = 8
+ };
+ void pushOutputCapture (int capture_mode);
+ QString popOutputCapture ();
+
/** Flushes current output buffer. Meant to be called from RInterface::flushOutput, only.
@param forcibly: if true, will always flush the output. If false, will flush the output only if the mutex can be locked without waiting. */
ROutputList flushOutput (bool forcibly=false);
@@ -147,6 +156,12 @@ private:
QMutex output_buffer_mutex;
/** current length of output. If the backlog of output which has not yet been processed by the frontend becomes too long, output will be paused, automatically */
int out_buf_len;
+
+ struct OutputCapture {
+ ROutputList recorded;
+ int mode;
+ };
+ QList<OutputCapture> output_captures;
};
namespace RKRSharedFunctionality {
diff --git a/rkward/rbackend/rkrinterface.cpp b/rkward/rbackend/rkrinterface.cpp
index 2ef9e4ec..6b6e7950 100644
--- a/rkward/rbackend/rkrinterface.cpp
+++ b/rkward/rbackend/rkrinterface.cpp
@@ -87,8 +87,6 @@ RInterface::RInterface () {
previously_idle = false;
locked = 0;
backend_dead = false;
- num_active_output_record_requests = 0;
- previous_output_type = ROutput::NoOutput;
flush_timer_id = 0;
dummy_command_on_stack = 0;
@@ -116,7 +114,6 @@ void RInterface::issueCommand (const QString &command, int type, const QString &
RInterface::~RInterface(){
RK_TRACE (RBACKEND);
- if (num_active_output_record_requests) RK_DEBUG (RBACKEND, DL_WARNING, "%d requests for recording output still active on interface shutdown", num_active_output_record_requests);
RKWindowCatcher::discardInstance ();
}
@@ -442,24 +439,6 @@ void RInterface::flushOutput (bool forced) {
RK_DEBUG (RBACKEND, DL_DEBUG, "output '%s'", qPrintable (output->output));
}
- if (num_active_output_record_requests) {
- if (output->type != ROutput::Error) { // NOTE: skip error output. It has already been written as a warning.
- if (output->type != previous_output_type) {
- if (!recorded_output.isEmpty ()) recorded_output.append ("</pre>\n");
-
- if (output->type == ROutput::Output) recorded_output.append ("<pre class=\"output_normal\">");
- else if (output->type == ROutput::Warning) recorded_output.append ("<pre class=\"output_warning\">");
- else {
- RK_ASSERT (false);
- recorded_output.append ("<pre>");
- }
-
- previous_output_type = output->type;
- }
- recorded_output.append (output->output.toHtmlEscaped ());
- }
- }
-
bool first = true;
foreach (RCommand* command, all_current_commands) {
ROutput *coutput = output;
@@ -664,20 +643,6 @@ QStringList RInterface::processPlainGenericRequest (const QStringList &calllist)
}
}
}
- } else if (call == "recordOutput") {
- // NOTE: requests to record output can overlap (i.e. several can be active at the same time). However, we always clear the buffer, each time a request ends, i.e. then
- // recorded output does NOT overlap.
- if (calllist.value (1) == "end") {
- RK_ASSERT (num_active_output_record_requests > 0);
- --num_active_output_record_requests;
- QString dummy = recorded_output;
- recorded_output.clear ();
- if (!dummy.isEmpty ()) dummy.append ("</pre>\n");
- previous_output_type = ROutput::NoOutput;
- return QStringList (dummy);
- } else {
- ++num_active_output_record_requests;
- }
} else if (call == "printPreview") {
RKPrintAgent::printPostscript (calllist.value (1), true);
} else if (call == "endBrowserContext") {
diff --git a/rkward/rbackend/rkrinterface.h b/rkward/rbackend/rkrinterface.h
index d7c666b4..d310c367 100644
--- a/rkward/rbackend/rkrinterface.h
+++ b/rkward/rbackend/rkrinterface.h
@@ -123,9 +123,6 @@ private:
QString startup_errors;
bool startup_phase2_error;
- int num_active_output_record_requests;
- ROutput::ROutputType previous_output_type;
- QString recorded_output;
RCommand *dummy_command_on_stack;
friend class RKRBackendProtocolFrontend;
bool backend_dead;
More information about the rkward-tracker
mailing list