[rkward-cvs] SF.net SVN: rkward-code:[4707] trunk/rkward/rkward/rbackend

tfry at users.sf.net tfry at users.sf.net
Sun Apr 21 18:26:23 UTC 2013


Revision: 4707
          http://sourceforge.net/p/rkward/code/4707
Author:   tfry
Date:     2013-04-21 18:26:22 +0000 (Sun, 21 Apr 2013)
Log Message:
-----------
Start implementing "priority commands" (which will be run out-of-execution-order inside R's event loop).
So far the frontend side is done, only (but all dry theory so far; not tested beyond not breaking existing commands).

Modified Paths:
--------------
    trunk/rkward/rkward/rbackend/rcommand.h
    trunk/rkward/rkward/rbackend/rcommandstack.cpp
    trunk/rkward/rkward/rbackend/rinterface.cpp
    trunk/rkward/rkward/rbackend/rinterface.h
    trunk/rkward/rkward/rbackend/rkrbackendprotocol_frontend.cpp
    trunk/rkward/rkward/rbackend/rkrbackendprotocol_frontend.h
    trunk/rkward/rkward/rbackend/rkrbackendprotocol_shared.h

Modified: trunk/rkward/rkward/rbackend/rcommand.h
===================================================================
--- trunk/rkward/rkward/rbackend/rcommand.h	2013-04-21 13:10:07 UTC (rev 4706)
+++ trunk/rkward/rkward/rbackend/rcommand.h	2013-04-21 18:26:22 UTC (rev 4707)
@@ -141,22 +141,24 @@
 /** Types of commands (potentially more to come), bitwise or-able,
 	although partially exclusive. See \ref UsingTheInterfaceToR for a overview of what these are used for. TODO: find out, why Canceled is in here, and document that fact. */
 	enum CommandTypes {
-		User=1,		/**< Command was created directly by the user (e.g. in the console or in a command editor window) */
-		Plugin=1 << 1,		/**< Command comes from a plugin */
-		App=1 << 2,			/**< Command comes from the application (e.g. loading / saving the workspace */
-		Sync=1 << 3,		/**< Command is used to sync data to or from R-space. Typically used in the editor classes */
-		EmptyCommand=1 << 4,		/**< Command is empty and will not be processed (an empty command may be used as a "marker") */
-		Console=1 << 5,	/**< Command originated in the console. These commands will get some extra treatment in RKwatch */
-		Internal=1 << 6,	/**< Command is meant to be used in the backend, only. Do not use outside rbackend classes! */
-		Silent=1 << 7,	/**< Command can be interrupted, but is otherwise an internal command. In particular it should not be carbon copied to the output. */
-		GetIntVector=1 << 8,			/**< Try to fetch result as an array of integers */
-		GetStringVector=1 << 9,	/**< Try to fetch result as an array of chars */
-		GetRealVector=1 << 10,		/**< Try to fetch result as an array of doubles */
-		GetStructuredData=1 << 11,		/**< Try to fetch result as an RData structure */
-		CCOutput=1 << 12,		/**< Append command output to the HTML-output file */
-		CCCommand=1 << 13,		/**< Append the command itself to the HTML-output file */
-		ObjectListUpdate=1 << 14,		/**< The command may change the list of objects available. Do an update */
-		QuitCommand=1 << 15		/**< The R backend should be killed */
+		User=1,               /**< Command was created directly by the user (e.g. in the console or in a command editor window) */
+		Plugin=1 << 1,        /**< Command comes from a plugin */
+		App=1 << 2,           /**< Command comes from the application (e.g. loading / saving the workspace */
+		Sync=1 << 3,          /**< Command is used to sync data to or from R-space. Typically used in the editor classes */
+		EmptyCommand=1 << 4,  /**< Command is empty and will not be processed (an empty command may be used as a "marker") */
+		Console=1 << 5,       /**< Command originated in the console. These commands will get some extra treatment in RKwatch */
+		Internal=1 << 6,      /**< Command is meant to be used in the backend, only. Do not use outside rbackend classes! */
+		Silent=1 << 7,        /**< Command can be interrupted, but is otherwise an internal command. In particular it should not be carbon copied to the output. */
+		GetIntVector=1 << 8,  /**< Try to fetch result as an array of integers */
+		GetStringVector=1 << 9, /**< Try to fetch result as an array of chars */
+		GetRealVector=1 << 10, /**< Try to fetch result as an array of doubles */
+		GetStructuredData=1 << 11, /**< Try to fetch result as an RData structure */
+		CCOutput=1 << 12,     /**< Append command output to the HTML-output file */
+		CCCommand=1 << 13,    /**< Append the command itself to the HTML-output file */
+		ObjectListUpdate=1 << 14, /**< The command may change the list of objects available. Do an update */
+		QuitCommand=1 << 15,  /**< The R backend should be killed */
+		PriorityCommand=1 << 16 /**< The command has high priority, should be run during R's event loop processing. In general, PriorityCommands *must* not have side-effects
+		                             Use only, when absolutely necessary. */
 	};
 	enum CommandStatus {
 		Running=1,						/**< command is currently running */

Modified: trunk/rkward/rkward/rbackend/rcommandstack.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rcommandstack.cpp	2013-04-21 13:10:07 UTC (rev 4706)
+++ trunk/rkward/rkward/rbackend/rcommandstack.cpp	2013-04-21 18:26:22 UTC (rev 4707)
@@ -47,8 +47,17 @@
 	RK_TRACE (RBACKEND);
 	RK_ASSERT (parent);
 
-	RCommandStackModel::getModel ()->aboutToAdd (parent, parent->sub_commands.size ());
-	parent->sub_commands.append (child);
+	int pos = parent->sub_commands.size ();
+	if (child->is_command && (child->toCommand ()->type () & RCommand::PriorityCommand)) {
+		// Add priority commands before any regular command, after other priority commands. Always in main chain.
+		RK_ASSERT (parent == regular_stack);
+		for (pos = 0; pos < parent->sub_commands.size (); ++pos) {
+			RCommand *com = parent->sub_commands[pos]->toCommand ();
+			if (!(com && (com->type () & RCommand::PriorityCommand))) break;
+		}
+	}
+	RCommandStackModel::getModel ()->aboutToAdd (parent, pos);
+	parent->sub_commands.insert (pos, child);
 	child->parent = parent;
 	RCommandStackModel::getModel ()->addComplete ();
 }
@@ -91,12 +100,6 @@
 RCommand* RCommandStack::currentCommand () {
 	RK_TRACE (RBACKEND);
 
-	if (RK_Debug_CommandStep) {
-		QTime t;
-		t.start ();
-		while (t.elapsed () < RK_Debug_CommandStep) {}
-	}
-
 	RCommandChain *dummy;
 	do {	// first pop any empty things in the way
 		dummy = activeSubItemOf (regular_stack);

Modified: trunk/rkward/rkward/rbackend/rinterface.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rinterface.cpp	2013-04-21 13:10:07 UTC (rev 4706)
+++ trunk/rkward/rkward/rbackend/rinterface.cpp	2013-04-21 18:26:22 UTC (rev 4707)
@@ -135,22 +135,35 @@
 	return (RCommandStack::regular_stack->isEmpty() && (!runningCommand()));
 }
 
-RCommand *RInterface::popPreviousCommand () {
+RCommand *RInterface::popPreviousCommand (int id) {
 	RK_TRACE (RBACKEND);
 
 	RK_ASSERT (!all_current_commands.isEmpty ());
-	RCommand *ret = all_current_commands.takeLast ();
-	RCommandStack::pop (ret);
-	return ret;
+	for (int i = all_current_commands.size () - 1; i >= 0; --i) {
+		RCommand *ret = all_current_commands[i];
+		if (ret->id () == id) {
+			RCommandStack::pop (ret);
+			all_current_commands.removeAt (i);
+			return ret;
+		}
+	}
+	RK_ASSERT (false);
+	return 0;
 }
 
 void RInterface::tryNextCommand () {
 	RK_TRACE (RBACKEND);
-	if (!currentCommandRequest ()) return;
+	RCommand *command = RCommandStack::currentCommand ();
+	if (command_requests.isEmpty ()) {
+		// if the backend is not requesting anything, only priority commands will be pushed
+		if (!command) return;
+		if (!(command->type () & RCommand::PriorityCommand)) return;
+		if (all_current_commands.contains (command)) return;
+	}
 
+	bool priority = command && (command->type () & RCommand::PriorityCommand);
 	bool on_top_level = all_current_commands.isEmpty ();
-	if (!(on_top_level && locked)) {                                     // do not respect locks for sub-commands
-		RCommand *command = RCommandStack::currentCommand ();
+	if (!(on_top_level && locked && !(priority))) {                                     // do not respect locks for sub-commands
 		if (!on_top_level && all_current_commands.contains (command)) {  // all sub-commands of the current command have finished
 			doNextCommand (0);
 			return;
@@ -164,7 +177,7 @@
 				command->status |= RCommand::Failed;
 
 				// notify ourselves...
-				RCommand* dummy = popPreviousCommand ();
+				RCommand* dummy = popPreviousCommand (command->id ());
 				RK_ASSERT (dummy == command);
 				handleCommandOut (command);
 				return;
@@ -223,8 +236,17 @@
 void RInterface::doNextCommand (RCommand *command) {
 	RK_TRACE (RBACKEND);
 	RBackendRequest* command_request = currentCommandRequest ();
-	RK_ASSERT (command_request);
+	if (!command_request) {
+		if (!(command && (command->type () & RCommand::PriorityCommand))) return;
+	}
+	// importantly, this point is not reached for the fake startup command
 
+	if (RK_Debug_CommandStep) {
+		QTime t;
+		t.start ();
+		while (t.elapsed () < RK_Debug_CommandStep) {}
+	}
+
 	flushOutput (true);
 	RCommandProxy *proxy = 0;
 	if (command) {
@@ -248,9 +270,14 @@
 		}
 	}
 
-	command_request->command = proxy;
-	RKRBackendProtocolFrontend::setRequestCompleted (command_request);
-	command_requests.pop_back ();
+	if (command && (command->type () & RCommand::PriorityCommand)) {
+		RKRBackendProtocolFrontend::sendPriorityCommand (proxy);
+	} else {
+		RK_ASSERT (command_request);
+		command_request->command = proxy;
+		RKRBackendProtocolFrontend::setRequestCompleted (command_request);
+		command_requests.pop_back ();
+	}
 }
 
 void RInterface::rCommandDone (RCommand *command) {
@@ -321,11 +348,10 @@
 	if (request->type == RBackendRequest::CommandOut) {
 		RCommandProxy *cproxy = request->takeCommand ();
 		RCommand *command = 0;
-#warning: when there are priority commands, the command to pop is NOT necessarily the one we last submitted!
 		// NOTE: the order of processing is: first try to submit the next command, then handle the old command.
 		// The reason for doing it this way, instead of the reverse, is that this allows the backend thread / process to continue working, concurrently
 		// NOTE: cproxy should only ever be 0 in the very first cycle
-		if (cproxy) command = popPreviousCommand ();
+		if (cproxy) command = popPreviousCommand (cproxy->id);
 		command_requests.append (request);
 		tryNextCommand ();
 		if (cproxy) {

Modified: trunk/rkward/rkward/rbackend/rinterface.h
===================================================================
--- trunk/rkward/rkward/rbackend/rinterface.h	2013-04-21 13:10:07 UTC (rev 4706)
+++ trunk/rkward/rkward/rbackend/rinterface.h	2013-04-21 18:26:22 UTC (rev 4707)
@@ -101,7 +101,7 @@
 	RBackendRequest* currentCommandRequest () const { return (command_requests.isEmpty () ? 0 : command_requests.last ()); };
 	void tryNextCommand ();
 	void doNextCommand (RCommand *command);
-	RCommand *popPreviousCommand ();
+	RCommand *popPreviousCommand (int id);
 	void handleCommandOut (RCommand *command);
 	bool previously_idle;
 

Modified: trunk/rkward/rkward/rbackend/rkrbackendprotocol_frontend.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rkrbackendprotocol_frontend.cpp	2013-04-21 13:10:07 UTC (rev 4706)
+++ trunk/rkward/rkward/rbackend/rkrbackendprotocol_frontend.cpp	2013-04-21 18:26:22 UTC (rev 4707)
@@ -75,6 +75,15 @@
 	qApp->postEvent (RKFrontendTransmitter::instance (), new RKRBackendEvent (req));
 }
 
+void RKRBackendProtocolFrontend::sendPriorityCommand (RCommandProxy* proxy) {
+	RK_TRACE (RBACKEND);
+
+	RBackendRequest *req = new RBackendRequest (false, RBackendRequest::PriorityCommand);
+	req->command = proxy;
+	qApp->postEvent (RKFrontendTransmitter::instance (), new RKRBackendEvent (req));
+
+}
+
 void RKRBackendProtocolFrontend::terminateBackend () {
 	RK_TRACE (RBACKEND);
 

Modified: trunk/rkward/rkward/rbackend/rkrbackendprotocol_frontend.h
===================================================================
--- trunk/rkward/rkward/rbackend/rkrbackendprotocol_frontend.h	2013-04-21 13:10:07 UTC (rev 4706)
+++ trunk/rkward/rkward/rbackend/rkrbackendprotocol_frontend.h	2013-04-21 18:26:22 UTC (rev 4707)
@@ -2,7 +2,7 @@
                           rkrbackendprotocol  -  description
                              -------------------
     begin                : Thu Nov 04 2010
-    copyright            : (C) 2010, 2011 by Thomas Friedrichsmeier
+    copyright            : (C) 2010, 2011, 2013 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -24,6 +24,7 @@
 
 class RInterface;
 class QThread;
+class RCommandProxy;
 
 class RKRBackendProtocolFrontend : public QObject {
 public:
@@ -32,7 +33,8 @@
 
 	static void setRequestCompleted (RBackendRequest *request);
 	ROutputList flushOutput (bool force);
-	void interruptCommand (int command_id);
+	static void interruptCommand (int command_id);
+	static void sendPriorityCommand (RCommandProxy *proxy);
 	void terminateBackend ();
 	void setupBackend ();
 	static RKRBackendProtocolFrontend* instance () { return _instance; };

Modified: trunk/rkward/rkward/rbackend/rkrbackendprotocol_shared.h
===================================================================
--- trunk/rkward/rkward/rbackend/rkrbackendprotocol_shared.h	2013-04-21 13:10:07 UTC (rev 4706)
+++ trunk/rkward/rkward/rbackend/rkrbackendprotocol_shared.h	2013-04-21 18:26:22 UTC (rev 4707)
@@ -43,7 +43,8 @@
 		Debugger,
 		CommandLineIn,	/**< The next line of the current user command has been submitted in the backend. */
 		Output,		/**< A piece of output. Note: If the backend runs in a single process, output is handled in a pull fashion, instead of using requests. */
-		Interrupt,	/**< Interrupt evaluation. This request type originates in the frontend, not the backend (the only one so far). */
+		Interrupt,	/**< Interrupt evaluation. This request type originates in the frontend, not the backend. */
+		PriorityCommand, /**< Send a command to be run during R's event processing. This request type originates in the frontend, not the backend. */
 		OutputStartedNotification, /**< Only used in the frontend: Notification that a new bit of output has arrived. Used to trigger flushing after a timeout. */
 		OtherRequest		/**< Any other type of request. Note: which requests are in the enum, and which are not has mostly historical reasons. @see params */
 	};





More information about the rkward-tracker mailing list