[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